41

「ドメインモデルの継承は開発者の生活を複雑にしている」という議論が職場でありました。私は OO プログラマーなので、あちこちにスイッチを配置する代わりに、ドメイン モデルに継承を持たせることで実際に開発者の生活が楽になるという議論を探し始めました。

私が見たいのはこれです:

class Animal {

}

class Cat : Animal {

}

class Dog : Animal {

}

他の同僚が言っていることは次のとおりです。

public enum AnimalType {
    Unknown,
    Cat,
    Dog
}

public class Animal {

    public AnimalType Type { get; set; }

}

この種の状況では、クラス階層の方が列挙型プロパティを使用するよりも優れていることを彼に納得させるにはどうすればよいでしょうか (リンクはWELCOMEです)。

ありがとう!

4

6 に答える 6

27

これが私がそれについてどのように理由をつけているかです:

ロール/タイプが変更されない場合にのみ継承を使用してください。例えば

次のようなものに継承を使用します。

消防士 ← 従業員 ← 人が間違っています。

消防士の Freddy が転職するか失業するとすぐに、彼を殺し、古いリレーションをすべて付けた新しいタイプの新しいオブジェクトを再作成する必要があります。

したがって、上記の問題に対する単純な解決策は、JobTitle enum プロパティを person クラスに与えることです。ロール/タイプに関連付けられた非常に複雑な動作が必要ない場合など、一部のシナリオではこれで十分です。

より正しい方法は、人物クラスに役割のリストを与えることです。各ロールは、たとえば期間のある雇用を表します。

例えば

freddy.Roles.Add(new Employement( employmentDate, jobTitle ));

またはそれがやり過ぎの場合:

freddy.CurrentEmployment = new Employement( employmentDate, jobTitle );

このようにして、最初にフレディを殺さなくても、フレディは開発者になることができます。

ただし、役職に列挙型または型階層を使用する必要があるかどうかについては、私のすべてのとりとめのない答えがまだありません。

純粋な in mem OO では、ここで役職に継承を使用する方が正しいと思います。

しかし、O/R マッピングを行っている場合、マッパーが各サブタイプを新しいテーブルにマップしようとすると、バックグラウンドで少し複雑すぎるデータ モデルになる可能性があります。したがって、そのような場合、型に関連付けられた実際の/複雑な動作がない場合は、enum アプローチを使用することがよくあります。「if type == JobTitles.Fireman ...」の使用法が制限されていて、物事がより簡単または単純になる場合は、私は一緒に暮らすことができます。

たとえば、.NET の Entity Framework 4 デザイナーは、各サブタイプを新しいテーブルにのみマップできます。また、実際の利点なしにデータベースにクエリを実行すると、醜いモデルや大量の結合が得られる可能性があります。

ただし、タイプ/ロールが静的な場合は継承を使用します。たとえば、製品の場合。

CD <- Product と Book <- Product があるかもしれません。この場合、型に関連付けられた状態が異なる可能性が高いため、ここでは継承が優先されます。CD にはトラック数のプロパティがあり、本にはページ数のプロパティがある場合があります。

要するに、それは依存します;-)

また、最終的には、どちらにしても多くの switch ステートメントが必要になる可能性が高くなります。"Product" を編集したいとしましょう。継承を使用している場合でも、おそらく次のようなコードになるでしょう。

if (製品が本) Response.Redicted("~/EditBook.aspx?id" + product.id);

エンティティ クラスで編集本の URL をエンコードすると、ビジネス エンティティがサイト構造などを認識しなければならないため、見苦しくなります。

于 2010-11-23T09:57:47.753 に答える
10

列挙型は次の場合に適しています。

  1. 値のセットは固定されており、変更されることはありません。
  2. 値の和集合を表現できるようにする(つまり、フラグを組み合わせる)必要があります。
  3. 各値に他の状態を付加する必要はありません。(Javaにはこの制限はありません。)

数値を使用して問題を解決できる場合は、列挙型が適切であり、タイプセーフである可能性があります。上記よりも柔軟性が必要な場合は、列挙型が正しい答えではない可能性があります。ポリモーフィッククラスを使用すると、次のことができます。

  1. すべてのタイプ固有の動作が処理されることを静的に確認します。たとえば、すべての動物ができるようにする必要がある場合、抽象メソッドを使用してクラスをBark()作成すると、コンパイラは各サブクラスがそれを実装していることを確認できます。列挙型とビッグを使用する場合、すべてのケースを処理したことを保証するものではありません。AnimalBark()switch

  2. 新しいケース(例では動物の種類)を追加できます。これは、ソースファイル全体、さらにはパッケージの境界を越えて実行できます。列挙型を使用すると、宣言すると凍結されます。オープンエンド拡張は、OOPの主な強みの1つです。

あなたの同僚の例はあなたの例に直接反対していないことに注意することが重要です。彼が動物の型を公開プロパティにしたい場合(これはいくつかのことに役立ちます)、列挙型を使用せずに、型オブジェクトパターンを使用してそれを行うことができます:

public abstract class AnimalType {
    public static AnimalType Unknown { get; private set; }
    public static AnimalType Cat { get; private set; }
    public static AnimalType Dog { get; private set; }

    static AnimalType() {
        Unknown = new AnimalType("Unknown");
        Cat = new AnimalType("Cat");
        Dog = new AnimalType("Dog");
    }
}

public class Animal {
    public AnimalType Type { get; set; }
}

これにより、列挙型の利便性が得られます。実行できAnimalType.Cat、動物の種類を取得できます。ただし、クラスの柔軟性も提供します。フィールドを追加AnimalTypeして、各タイプのデータを追加したり、仮想メソッドを追加したりできます。さらに重要なのは、の新しいインスタンスを作成するだけで、新しい動物のタイプを定義できることですAnimalType

于 2010-11-23T17:17:32.777 に答える
10

列挙型を持つことは、それらすべての人々のためにパーティーを開くようなものOpen/Closed Principle is for suckersです。

動物が特定のタイプであるかどうかを確認し、各タイプにカスタム ロジックを適用するように促します。そして、それは恐ろしいコードをレンダリングする可能性があり、システム上で構築を続けることを非常に困難にします.

于 2011-05-09T07:59:16.130 に答える
8

再考することをお勧めします:貧血ドメイン モデル(上記のコメントによる) では、猫は犬と異なる行動をとらないため、ポリモーフィズムはありません。動物のタイプは、実際には単なる属性です。継承がそこで何を買うのかを理解するのは難しいです。

于 2010-11-23T17:38:35.280 に答える
1

最も重要なことは、OOPS が現実をモデル化することを意味することです。継承は、猫が動物であると言う機会を与えてくれます。動物は、猫が今それを叫んでいるかどうかを知る必要がなく、鳴き声ではなく鳴き声であると判断します。カプセル化はそこで敗北します。あなたが言ったようにIf elseをする必要がないので、コードが少なくなります。

于 2010-11-23T09:56:19.937 に答える
1

どちらのソリューションも正しいです。どの手法が問題に適しているかを確認する必要があります。

プログラムがいくつかの異なるオブジェクトを使用し、新しいクラスを追加しない場合は、列挙型を使用することをお勧めします。

しかし、プログラムが多くの異なるオブジェクト (異なるクラス) を使用し、将来新しいクラスを追加する可能性がある場合は、継承の方法を試してみてください。

于 2011-05-03T16:18:55.920 に答える