我正在尝试编写一个简单的程序,该程序应该允许用户保存和显示异构的集合,但不知何故相关的数据。为清楚起见,我将使用车辆的代表性示例。程序流程如下:
乍一看,这可能看起来像是一个涉及基类和继承的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修饰符不一定反映原始代码
答案 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实际上是车辆的属性。