2

ユーザーが異種のセットを保存および表示できるようにする単純なプログラムを作成しようとしていますが、何らかの形で関連するデータです。わかりやすくするために、代表的な車両の例を使用します。プログラムの流れは次のようになります。

  1. プログラムはGarageオブジェクトを作成します。これは基本的に、車両オブジェクトのリストを含むことができるクラスです。
  2. 次に、ユーザーはVehiclesオブジェクトを作成します。これらのVehiclesにはそれぞれプロパティがあり、たとえばLicense Plate Nrとします。Vehicleオブジェクトが作成されると、 Garageオブジェクト内のリストに追加されます。
  3. --後で-- ユーザーは、特定のVehicleオブジェクトが実際にはCarオブジェクトまたはTruckオブジェクトであることを指定できます (したがって、Carの座席数やトラックのCargo weightなどの特定の属性にアクセスできます)。

一見すると、これは基本クラスと継承に関する OOP 教科書の質問のように見えるかもしれませんが、オブジェクトの作成時 (およびユーザーが詳細な情報を提供することを決定するまで) に、コンピューターはオブジェクトを認識しないため、問題はより微妙です。正確な車両タイプ。

したがって、私の質問:このプログラム フローをどのように実装しますか? OOPは行くべき道ですか?

最初の答えを出すために、これが私が今まで思いついたことです。Vehicle クラスは 1 つしかなく、さまざまなプロパティ/値がディクショナリを通じて (クラスではなく) メイン プログラムによって処理されます。ただし、より洗練されたソリューションが必要であると確信しています (私は 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 修飾子は必ずしも元のコードを反映していないことに注意してください。

4

5 に答える 5

1

-- 後で -- ユーザーは、特定の Vehicle オブジェクトが実際には Car オブジェクトまたは Truck オブジェクトであることを指定できます。

機能として無制限のダウンキャストを要求する危険なほど近づいています。これはマネージ コードでは不可能です。CLR は、不正なダウンキャストが絶対に不可能であることを確実に保証します。とにかくしようとすると、InvalidCastException が発生します。

もう少し具体的に言えば、元のオブジェクトが車両として作成された場合、そのオブジェクトがトラックであるかのように解釈またはアクセスする方法はありません。たとえば、Truck には Vehicle にはない Cargo プロパティがあります。実際、Vehicle には Cargo のストレージさえありません。Vehicle を Truck として再解釈すると、Cargo の完全なガベージ値が得られます。さらに悪いことに、Cargo プロパティを書き込むとメモリが破損します。

C や C++ などの一部の言語では、無制限のダウンキャストが可能です。特に C では、ほとんど避けられません。void* は C の「オブジェクト クラス」です。しかし、これらの言語は、実行時にクラッシュするコードを記述することでも有名です。違法なダウンキャストは、このようなクラッシュを誘発する優れた一般的な方法です。これが原因で発生するヒープの破損は、診断が非常に難しく、クラッシュは、元の損傷が発生した場所から遠く離れたかなり後になってから発生します。

標準の Factory パターンを使用して、必要なプロパティ セットを持つ特定のクラスのインスタンスを作成します。基本クラスへのアップキャストは常に有効です。このようなファクトリは、たとえば、Truck オブジェクトを作成した場合でも、Vehicle 型の参照を返します。後でトラックにダウンキャストすると有効になります。

于 2013-06-11T01:50:37.107 に答える
1

最初に、ガレージ内のオブジェクト (その属性) について尋ねることができる一連の回答と、それらの質問に対する一連の回答 (その状態) を区別しましょう。

単純に一連の回答が変化するシナリオを見ている場合は、単純な状態パターンが適用されます。属性は一定のままで、状態が変化します。すべてのオブジェクトのインスタンス化は、定数属性を持つ単一のタイプのままです。

ガレージ内のオブジェクトで使用可能な属性が変化する、より複雑な状況を見ている場合は、Decorator パターンを使用します。ただし、これもあなたのシナリオに完全に適合するとは思いません。Decorator パターンは、扱いやすい数の属性があるシナリオ向けですが、どれがどの属性と一致するかについての制限がないため、可能な組み合わせの数は指数関数的になる可能性があります。

あなたの状況を最もよく処理すると思うシナリオは、オブジェクトが実際には識別されるまで未定義であり、最初に作成されたプロキシ (車両キーで表される) のみであるということです。オブジェクトが完全に識別されると、一度に発生するように見えます。オブジェクトがインスタンス化されます。

Proxy の上に Decorator を置きたいと思うかもしれませんが、それも必要ないかもしれません。

于 2013-06-11T00:18:59.097 に答える
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 など、何でもかまいません。

それでも、実行時にメソッド、関数、プロパティをマスクまたは追加することはできません。私のプロパティはすべて「文字列」型です

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
    ' ...

とにかく、ガレージで複数の車両を使用して特定のルーチンのコードを作成する必要があります。これは私の Garage Class のメンバーになります。特定の一連の車両に対して特定のことを実行するたびに、その車両のタイプを確認し、実行するコードの正しいパスを選択する必要がありました........

たくさんの車両のサブモデルが必要な場合、これは非常に難しい作業になります...

' 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 

私が物事を見る方法だけです。他の誰かがこれをすべて実装するためのより良い方法を提供してくれることを願っていますか?

于 2013-06-14T02:20:06.380 に答える
1

オブジェクト指向プログラミングは、意味をなさないことを行う「魔法の」オブジェクトではなく、現実的なオブジェクトをモデル化しようとするときに最も効果的です。

現実の世界では、あいまいな塊である車を持つことはできませんが、突然ピックアップ トラックになります。したがって、この方法でシステムをモデル化することはほとんど意味がなく、何度も何度も「魔法」に戻る原因となるさまざまな問題に遭遇します。

コンパイラとランタイム環境を一種の「ポケット ユニバース」と考えることができ、コンパイラによって適用される特定のルールをその宇宙に適用される「物理法則」と考えることができます。場合によっては、特定の補償を考慮してこれらの法則を曲げますが、一般的にはこれを行うべきではありません。これは、時空連続体に大きな裂け目を引き起こす可能性があるためです (つまり、プログラムの内部状態を破壊する可能性があります)。

代わりに、このようにモデル化します。「ライセンス プレート」オブジェクトのリストを取得できます。ピックアップ トラックを「作成」する場合は、Factory クラスを使用してライセンス プレート オブジェクトを渡すと、そのライセンス オブジェクトを使用するピックアップ トラックが作成されます。

多くの場合、オブジェクトには他のオブジェクトが含まれていることに注意してください。ナンバー プレートはそれ自体がオブジェクトなので、そのように扱ってみませんか? あいまいな「車両」とナンバー プレートの間に実際のつながりがないように見えるので、これはより理にかなっています。

于 2013-06-11T02:04:01.140 に答える
0

あなたが知っている基本的なプロパティを使用して、通常(抽象的ではない)に作成できる「Vehicle」基本クラスを用意したいと思います。定義した VehicleType を含めて、デフォルトで「Generic」に設定します。

サブタイプごとに特定のタイプを作成します。適切なコードを適用するために、適切なプロパティを Ridgid 形式で定義します。

基本タイプで、渡されたオブジェクトに車両プロパティを複製する関数を作成します。例えば。

Public sub CloneTo(byval OtherVehicle as Vehicle)

「ジェネリック」車両をより具体的に作成する必要がある場合は、新しい子タイプを作成し、それをルーチンに渡して既存の情報を複製し、古いタイプをガレージ コレクションの新しいタイプに置き換えます。

利用可能な拡張プロパティを決定するには、ガレージ コレクション内の各アイテムの子タイプを評価する必要がありますが、すべての正しいレベルが配置されている場合 (下位レベルは最も頻繁にアクセスされ、可能なプロパティが常にツリーの最上位レベルに配置されている場合)。車両 - 車 - セダン。例えば ​​PassengerCapacity は、実際には Vehicle のプロパティです。

于 2013-06-11T01:33:24.603 に答える