OOP概念:是否可以更新实例化对象的类?

时间:2013-06-10 22:04:29

标签: vb.net oop object inheritance instantiation

我正在尝试编写一个简单的程序,该程序应该允许用户保存和显示异构的集合,但不知何故相关的数据。为清楚起见,我将使用车辆的代表性示例。程序流程如下:

  1. 该程序会创建一个 Garage 对象,该对象基本上是一个可以包含车辆对象列表的类
  2. 然后,用户创建车辆对象,这些车辆每个都有一个属性,让我们说车牌号。创建后,车辆对象会添加到车库对象
  3. 中的列表中
  4. - 稍后 - ,用户可以指定给定的 Vehicle 对象实际上是 Car 对象或卡车对象(从而可以访问某些特定属性,例如汽车座位数货物重量卡车)
  5. 乍一看,这可能看起来像是一个涉及基类和继承的OOP教科书问题,但问题更加微妙,因为在创建对象时(直到用户决定提供更多信息),计算机没有&# 39;不知道确切的车辆类型。

    因此我的问题:您将如何继续实施此计划流程? OOP是否可行?

    只是给出一个初步答案,这是我到现在为止所做的。只有一个Vehicle类,各个属性/值由主程序(不是类)通过字典处理。但是,我非常确定必须有一个更优雅的解决方案(我正在使用VB.net进行开发):

    Public Class Garage
        Public GarageAdress As String
        Private _ListGarageVehicles As New List(Of Vehicles)
    
        Public Sub AddVehicle(Vehicle As Vehicles)
            _ListGarageVehicles.Add(Vehicle)
        End Sub
    End Class
    
    Public Class Vehicles
        Public LicensePlateNumber As String
        Public Enum VehicleTypes
            Generic = 0
            Car = 1
            Truck = 2
        End Enum
        Public VehicleType As VehicleTypes
        Public DictVehicleProperties As New Dictionary(Of String, String)
    End Class
    

    请注意,在上面的示例中,public / private修饰符不一定反映原始代码

5 个答案:

答案 0 :(得分:1)

让我们首先区分一组答案,这些答案可以从车库中的对象(其属性)询问这些问题的答案(其状态)。

如果您只是查看答案集更改的场景,则应用简单的状态模式。属性保持不变,状态发生变化。所有对象实例都保留为具有常量属性的单一类型。

如果您正在查看更复杂的情况,其中车库中对象的可用属性发生更改,则使用Decorator模式。但是,我认为这也不适合您的情况。 Decorator模式适用于存在易处理数量的属性的场景,但可能的组合数量可能是指数级的,因为没有限制哪个属性。

我认为最能处理您情况的场景是,在识别之前,对象实际上是未定义的,只有最初创建的代理(由车辆键表示),一旦对象被完全识别,它似乎一次全部发生,它的完整对象被实例化。

您可能希望装饰器位于代理顶部,但也可能不需要。

答案 1 :(得分:1)

  

- 稍后 - ,用户可以指定给定的Vehicle对象实际上是Car对象或Truck对象

你非常接近要求将无限制的向下转换作为一项功能。这在托管代码中是不可能的,CLR提供了很难保证非法的向下转换永远不可能。无论如何,它会引发InvalidCastException。

更具体一点,如果原始对象是作为Vehicle创建的,那么就无法解释或访问该对象,就像它是一辆卡车一样。例如,卡车具有车辆所没有的货物属性。事实上,车辆甚至没有货物存储。将车辆重新解释为卡车将为货物提供完整的垃圾价值。而且更糟糕的是,编写Cargo属性会破坏记忆。

在某些语言(如C和C ++)中可以进行无限制的向下转换。特别是在C中它几乎是不可避免的,void *是C的“对象类”。但是这些语言在编写在运行时崩溃的代码时也非常有名。非法堕落是引发这种崩溃的极好而常见的方法。这导致的堆损坏非常难以诊断,直到很久以后才发生崩溃,远离原始损坏的地方。

使用标准工厂模式创建具有所需属性集的特定类的实例。向上基类的上传始终有效。例如,这样的工厂将返回Vehicle类型的引用,即使它创建了Truck对象。稍后向卡车投降将有效。

答案 2 :(得分:1)

面向对象编程在尝试对真实对象进行建模时效果最佳,而不是那些做出无意义事情的“神奇”对象。

在现实世界中,你不能拥有一个模糊的汽车,但突然变成了一辆皮卡车。因此,以这种方式对系统进行建模是没有意义的,并且您将遇到各种问题,导致您一次又一次地回到“魔法”。

可以将编译器和运行时环境视为一种“口袋世界”,并且可以将编译器强制执行的某些规则视为适用于该宇宙的“物理定律”。在某些情况下,在给定某些补偿的情况下,你会弯曲这些定律,但一般情况下你不应该试图这样做,因为它会在时空连续体中引起巨大的裂缝(即,你可以破坏程序的内部状态)。 / p> 相反,我会这样建模。您可以拥有“车牌”对象列表,当您想要“创建”皮卡车时,您使用Factory类,传入车牌对象,它将创建一个使用该许可证对象的皮卡车。 / p>

请记住,对象通常包含其他对象。车牌本身就是一个对象,为什么不这样对待?由于你似乎在模糊的“车辆”和车牌之间没有真正的联系,这更有意义。

答案 3 :(得分:1)

我的理解是:您正试图在VB.net中实现一些您可以在JavaScript及其构造函数中实际动态实现的东西......

我不知道你是否可以在VB.net中动态创建方法,函数,事件或属性,如:

Public Module SampleMembers

    Public _PaxNum As Integer = 0
    Public _CargoAmount As Integer = 0

    Public Function GetPassengerNumbers() As Integer
        Return _PaxNum
    End Function

    Public Function GetCargoAmount() As Integer
        Return _CargoAmount
    End Function
End Module

然后,在您的应用程序中声明一个基本对象,如:

Dim MyVehicle As Object

稍后,在运行时,动态地将成员添加到您的车辆对象,如:

Public Sub ConvertBaseVehicleToCar(ByRef CurrentVehicle As Object)
    ' ...
    Object.AddMember(SampleMember._PaxNum, CurrentVehicle)
    Object.AddMember(SampleMember.GetPassengerNumber(), CurrentVehicle)
    ' Where Object would have a magical Constructor Modyfier...
    ' That would be GREAT... of course
End Sub

但如果我没弄错的话,你不能在VB.net中这样做

如果只是关于数据...

我会用:

Public Class Vehicle
    Private _PropertiesList As New SortedList(Of String, String)

    Public Function AddProperty(ByVal PropertyName As String, ByVal PropertyValue As String) As Boolean
        If _PropertiesList.ContainsKey(PropertyName) Then
            _PropertiesList.Item(PropertyName) = PropertyValue
            Return False ' Property replaced !
        Else
            _PropertiesList.Add(PropertyName, PropertyValue)
            Return Property ' New Property added !
        End If
    End Function

    Public Function RemoveProperty(ByVal PropertyName) As Boolean
        If _PropertiesList.ContainsKey(PropertyName) Then
            _PropertiesList.Remove(PropertyName)
            Return True ' Property actually removed !
        Else
            Return False ' No property with that name !
        End If
    End Function

    Public Function GetPropertiesList() As List(Of String)
        Dim NewList As New List(Of String)
        Dim CurrentProperty As String

        For Each CurrentProperty In _PropertiesList.Keys
            NewList.Add(CurrentProperty)
        Next

        Return NewList
    End Function

    Public Function GetProperty(ByVal PropertyName As String) As String
        If _PropertiesList.ContainsKey(PropertyName) Then
            Return _PropertiesList.Item(PropertyName)
        Else
            Return ""
            ' Or whatever explicit code of your choice
            ' like Return "N/A" or Return "#"
        End If
    End Function
    ' I would replace this latest function by 
    Public Property Item(ByVal PropertyName As String) As String
        ' ...
    End Property

    ' ...

    ' And the Constructor
    Public Sub New(ByVal VehicleType As String)
        InitializeType(VehicleType)
    End Sub

    ' With its default Properties like :
    Private Sub InitializeType(ByVal ProposedType As String)
        ProposedType = ProposedType.Trim().ToUpper()
        Select Case ProposedType
            Case "CAR":
                Item("Type") = "CAR"
            Case "TRUCK":
                Item("Type") = "TRUCK"
            Case "MINIVAN":
                Item("Type") = "MINIVAN"
        End Select
    End Sub

    ' And add a FINAL ReadOnly Property
    Public ReadOnly Property VehicleType() As String
        Get
            Return Item("Type")
        End Get
    End Property
End Class

现在,MyVehicle可以是任何东西,汽车,卡车,飞机,甚至是PlanetEarth ......

但是,我无法在运行时屏蔽或添加方法,函数和属性。我的属性都是“String”类型

MyCar.Item("NumberOfWheels") = "6"
' ^^ I'll have to cast this to Integer before using it...
MessageBox.Show(SumOfWheels(MyListOfVehicles).ToString())
' Where :
Public Function SumOfWheels(ByVal ListOfVehicles As List(Of Vehicles)) As Integer
    Dim CurrentVehicle As Vehicle
    Dim CurrentWheels As Integer
    Dim TotalWheels As Integer = 0
    For Each CurrentVehicle In ListOfVehicles
        If Integer.TryParse(CurrentVehicle.Item("NumberOfWheels"), CurrentWheels)
            TotalWheels = TotalWheels + CurrentWheels
        End If
    Next
    Return TotalWheels
End Function

但是,我可以添加虚拟类型modyfier :最初的ReadOnly属性VehicleType()

' ...
    Public Property VehicleType() As String
        ' The Getter is the same, but the setter is a litte bit different :
        Set(ByVal NewType As String)
            InitializeType(NewType) ' Simple ? No ! I'll have to edit the Method...
        End Set
    End Property

    Private Sub InitializeType(ByVal ProposedType As String)
        ProposedType = ProposedType.Trim().ToUpper()
        Select Case ProposedType
            Case "CAR":
                Item("Type") = "CAR"
                RemoveProperty("CargoHold")
                Item("Drivers") = "1"
            Case "TRUCK":
                Item("Type") = "TRUCK"
                RemoveProperty("PaxSeats") ' Well, you actually can have one.. or two..
                Item("Drivers") = "1"
            Case "MINIVAN":
                Item("Type") = "MINIVAN"
                Item("Drivers") = "1"
            Case "MOTORBIKE":
                Item("Type") = "MOTORBIKE"
                RemoveProperty("CargoHold")
                Item("Drivers") = "1"
                Item("PaxSeats") = "1"
                Item("NumberOfWheels") = "2"
            Case "JETLINER":
                Item("Type") = "JETLINER"
                Item("Drivers") = "2"
            Case "VINTAGEJETLINER":
                Item("Type") = "VINTAGEJETLINER"
                Item("Drivers") = "3"
        End Select
    End Sub
    ' ...

无论如何,我必须使用车库中的几辆车为特定程序编写代码。这将是我的车库类中的成员。每当我想为一组给定的车辆做特定的事情时,我将不得不检查它是什么类型的车辆并选择正确的代码路径来运行.........

如果你想拥有一堆车型子模型,这将变得非常棘手......

' VEHICLE>MINIVAN
' VEHICLE>MINIVAN>CITROEN
' VEHICLE>MINIVAN>CITROEN>3CV
' VEHICLE>MINIVAN>CITROEN>3CV>BASIC
' VEHICLE>MINIVAN>CITROEN>3CV>COLLECTOR
' VEHICLE>MINIVAN>CITROEN>3CV>DEADHULK

但至少,您可以拥有一个有用的功能,可以在车库中检索具有特定属性的所有车辆:

Public Function GetVehicleUsingProperty(ByVal PropertyName As String, ByVal PropertyValue As String) As List(Of Vehicle)
' And a better one :
Public Function GetVehicleUsingProperty(ByVal PropertiesParam As SortedList(Of String, String)) As List(Of Vehicle)
' ... :P 

就像我看待事物一样。希望其他人可以用更好的方式来实现所有这些?

答案 4 :(得分:0)

我倾向于拥有一个“Vehicle”基类,可以使用您知道的基本属性正常创建(非抽象)。包括您定义的VehicleType默认设置为“Generic”。

为每个子类型创建每种特定类型。以ridgid格式定义正确的属性以强制执行良好的代码。

在基类型中创建一个函数,将Clone Vehicle Properties克隆到传入的Object中。例如

Public sub CloneTo(byval OtherVehicle as Vehicle)

当“通用”车辆需要更具体时,请创建新的子类型,将其传递给例程以克隆现有信息,并将旧类型替换为Garage Collection中的新类型。

您需要为车库集合中的每个项目评估子类型以确定可用的扩展属性,但我认为如果所有正确的级别都已就位,那么一个好的完整的子树列表可以最大限度地减少这项工作(较低级别最常访问,如果任何属性可以放在树中的最高级别)例如。车 - 车 - 轿车。例如,PassengerCapacity实际上是车辆的属性。