5

私はいくつかの検索を行いましたが(おそらく私の問題を十分に説明していません)、これに対する答えを見つけることができませんでした

次のPOCOがあるとします。

public class StandardObject
{
   public string A {get;set;}
   public string B {get;set;}
}

プログラムの他の場所には、StandardObjectsを処理するためのロジックがあります。StandardObjectを別の方法で処理する必要がある場合があります。StandardObjectを作成するときにこれを知っていますが、現在のプロパティを使用して、チェーンのさらに下流でこれを決定することはできません。1つのアプローチは、StandardObjectにフラグを追加して設定するか、列挙型を入力し、オブジェクトを処理するときにこれを確認することです。例えば

if(standardObject.IsSpecial){...}

if(standardObject.ObjectType == StandardObjectTypeEnum.Special){...}

ただし、これは最善のアプローチとは思えません。

もう1つのオプションは、派生クラスを作成することです。

public class SpecialObject : StandardObject { }

したがって、プロパティをチェックするのではなく、タイプをチェックできるようになりました。例えば

if(standardObject.GetType() == typeof(SpecialObject)){...}

(実行内容によっては、型チェックの実装が異なる場合があります)

SpecialObjectは、StandardObjectを追加または変更しないことに注意してください。それは本質的に同じオブジェクトです。このアプローチには、より柔軟であるという利点があります(たとえば、SpecialObjectにいくつかのプロパティを追加できます)が、実際には変更されません。常に同じになります。

私には、継承がより良いアプローチのように思えます。タイプフラグはコードの臭いのように見え、今後の継承は正しいOOPアプローチのように見えます。ここで私がよくわからないのは、StandardObjectとSpecialObjectが同じであるとすると、これを行うのは悪い習慣ですか?それとも、これを避けるべき理由はありますか?

ここにも同様の質問があります:

チェスの駒の階層設計:継承とタイプフィールド

しかし、議論のほとんどは、良いデザインと見なされるものではなく、チェスの問題に焦点を当てているようです

編集:

カプセル化が一般的な解決策のようです。カプセル化を回避した理由は、以下の例で最もよく説明されています。

  • StandardObjectは本質的にDTOです
  • StandardObjectのリストがあります。
  • リストが処理され、StandardObjectがシリアル化されます
  • シリアル化されたデータは、任意の数の異なるプロトコルを介してどこかに送信されます
  • StandardObjectが「特殊」である場合、プロトコルの1つで特定のパラメーターを設定する必要があります

「特殊な」ケースの追加ロジックは、StandardObjectを処理するいくつかの異なるメカニズムの1つでのみ必要とされるため、このロジックは、StandardObjectの近くにあるようには見えません。

4

3 に答える 3

3

どちらコードの臭いです。2つの関連するタイプ間で異なる方法で実行する必要がある特別な処理がある場合は、両方に、特別な場合を処理できるようにする仮想メソッドまたはインターフェース操作を実装してもらいます(必要でない場合はそうではありません)。

于 2012-09-07T02:48:06.583 に答える
1

共有実装を配置できる抽象基本クラスを使用することで、戦略パターンを使用して「処理」ロジックをカプセル化することもできます。または、派生実装と継承実装を呼び出すこともできます。

public abstract class Vehicle
{
    private MovementStrategy movementStrategy;

    protected Vehicle(MovementStrategy strategy)
    {
        this.movementStrategy = strategy;
    }

    public void Move()
    {
        movementStrategy.Move();
    }

    public virtual void CommonAlert()
    {
        Console.WriteLine("Base generic vehicle alert");
    }
}

public class Car : Vehicle
{
    public Car(MovementStrategy movementStrategy)
        : base(movementStrategy)
    {
    }

    public override void CommonAlert()
    {
        Console.WriteLine("Car says 'Honk!'");
    }
}

public class Elevator : Vehicle
{
    public Elevator(MovementStrategy movementStrategy)
        : base(movementStrategy)
    {
    }

    public override void CommonAlert()
    {
        Console.WriteLine("Elevator says 'Ding!'");
        base.CommonAlert();
    }
}

public abstract class MovementStrategy
{
    public abstract void Move();
}

public class CarMovementStrategy : MovementStrategy
{
    public override void Move()
    {
        Console.WriteLine("Car moved");
    }
}

public class ElevatorMovementStrategy : MovementStrategy
{
    public override void Move()
    {
        Console.WriteLine("Elevator moved");
    }
}

次に、メインプログラムの場合、サンプルを使用します。

class Program
{
    static void Main(string[] args)
    {
        Vehicle elevator = new Elevator(new ElevatorMovementStrategy());
        Vehicle car = new Car(new CarMovementStrategy());

        elevator.Move();
        car.Move();

        elevator.CommonAlert();
        car.CommonAlert();

        Console.Read();

    }
}
于 2012-09-07T03:03:32.717 に答える
0

オブジェクトのプロパティに基づいてオブジェクトを異なる方法で処理する必要がある場合は、クラス内でオブジェクトを操作するメソッドを配置することをお勧めします。オブジェクトの動作は、プライベート状態変数または派生オブジェクトのタイプによって決定できますが、呼び出し元のコードはその知識を必要としません。

これをスケッチするには、属性に応じて特定の方法で描画する必要がある要素があるとします。

クラスに単に処理が含まれている場合は、次のことを行う必要があります。

MyElement.Draw();

それ以外の:

if(MyElement.Flag)
{
     DrawBlue(MyElement);
}
else
{
    DrawRed(MyElement);
}

呼び出し元のコードに対して、特定の動作を決定する方法を公開する必要はありません。

于 2012-09-07T02:57:41.180 に答える