オブジェクト指向設計


23

次のものがあるとします:

     +--------+     +------+
     | Animal |     | Food |
     +-+------+     +----+-+
       ^                 ^
       |                 |
       |                 |
  +------+              +-------+
  | Deer |              | Grass |
  +------+              +-------+

Deerから継承しAnimal、からGrass継承しFoodます。

ここまでは順調ですね。Animalオブジェクトはオブジェクトを食べることができFoodます。

それでは少し混ぜましょう。Lionを継承するを追加しましょうAnimal

     +--------+     +------+
     | Animal |     | Food |
     +-+-----++     +----+-+
       ^     ^           ^
       |     |           |
       |     |           |
  +------+ +------+     +-------+
  | Deer | | Lion |     | Grass |
  +------+ +------+     +-------+

今、私たちはので、問題を持っている必要がありLion、両方を食べることができるDeerGrass、しかし、Deerされていない FoodことがありますAnimal

多重継承を使用せずに、オブジェクト指向設計を使用して、この問題をどのように解決しますか?

参考までに、http://www.asciiflow.comを使用してASCIIダイアグラムを作成しました。


14
現実の世界をモデル化することは、通常、遅かれ早かれ問題になります。なぜなら、常に奇妙なことが起こっているからです(トビウオ、魚、鳥など)。@Amptの言うことはもっともらしいと思われますが、動物には食べるもののコレクションが必要です。
ロブファンデルヴィール

2
私は、動物は食物を継承すべきだと思います。何かがライオンを食べようとした場合、InvalidOperationExceptionをスローするだけです。
ラルフシャピン

4
@RalphChapin:あらゆる種類のものがライオン(ハゲタカ、バグなど)を食べます。動物と食物は、十分に広範ではないために破壊される人工的な区別だと思います(最終的にはすべての動物は他の動物の食物です)。「LivingThing」で分類した場合、非生物(ミネラルなど)を食べる植物のエッジケースを処理するだけでよく、LivingThing.Eat(LivingThing)を使用しても何も壊れません。
悪魔のような子犬

2
あなたの研究を共有することは皆に役立ちます。試したことと、それがニーズに合わなかった理由を教えてください。これは、あなたが時間をかけて自分自身を助けようとしていること、明白な答えを繰り返すことから私たちを救うこと、そして何よりもあなたがより具体的で関連性のある答えを得ることを助けることを示しています。質問方法
-gnat

9
この質問は、ゲームAge of Empire IIIによって回答されました。ageofempires.wikia.com/wiki/List_of_Animals DeerとGazelle implements IHuntable、SheepとCowはIHerdable(人間によって制御可能)、LionはIAnimalのみを実装しますが、これらのインターフェースは含まれていません。AOE3はinstanceof、プログラムがその機能を照会できるようにする特定のオブジェクト(に類似)がサポートするインターフェースのセットの照会をサポートします。
rwong

回答:


38

IS A関係=継承

ライオンは動物です

関係あり=構成

車には車輪があります

CAN DO関係=インターフェイス

食べられる


5
それほど単純でありながら3つの異なる関係タイプの良好な要約である+1
dreza

4
代替案:ICanBeEatenまたはIEdible
Mike Weller

2
CAN HAZ関係= lolcats-
スティーブンA.ロウ

1
これは質問にどのように答えますか?
user253751

13

オブジェクト指向は、現実世界を模したメタファーにすぎません。しかし、比phorはこれまでのところだけです。

通常、オブジェクト指向で何かをモデル化する正しい方法はありません。そこにそれを行うための正しい方法である特定の問題のため特定のドメインは、ドメインオブジェクトが同じであっても、あなたはあなたの問題を変更した場合、それがうまく動作するように期待するべきではありません。

これはほとんどのコンプでよくある誤解だと思います。工学 学生は最初の数年で持っています。OOは普遍的なソリューションではなく、ドメインを合理的に適切にモデル化できる、ある種の問題に対する適切なツールです。

ドメイン情報が不足しているからこそ、質問には答えませんでした。ただし、上記を念頭に置いて、ニーズに合ったものを設計できる可能性があります。


3
+1 OOはツールであり、宗教ではありません。
ムービシエル

この問題が変化し進化し続けると、完璧な解決策が得られない可能性があります。現在の状態では、この問題には解決策を考え出すためのドメイン情報がありませんか?
マイケルアイリー

現実の世界がOPでモデル化されていると真剣に考えていますか?関係モデルは、例によって表されます。
バシレフス

@Basilevs実際には動物がどのように振る舞うかについて言及しているので、それは一種の含意です。プログラムIMOでその振る舞いを説明する必要があるのはなぜかを気にする必要があります。そうは言っても、可能なデザインを提案してくれたら嬉しかったでしょう。
DPM

10

動物をさらにサブクラスに分類したい(または、少なくともあなたがしていることに意味がある限り)。基本的な動物と2種類の食物(植物と肉)のように見えるもので作業していることを考えると、肉食動物と草食動物を使用して動物をさらに定義し、それらを分離しておくことは理にかなっています。これが私があなたのために作成したものです。

             +----------------+                   +--------------------+
             |    Animal      |                   |      Food          |
             |----------------|<--+Interfaces+--->|--------------------|
             |                |                   |                    |
             +----------------+                   +--------------------+
                +           +                       +                 +
                |           |    Abstract Classes   |                 |
                |           |        |          |   |                 |
                v           v        v          v   v                 v
   +-----------------+  +----------------+     +------------+      +------------+
   |   Herbivore     |  |  Carnivore     |     |   Plant    |      |   Meat     |
   |-----------------|  |----------------|     |------------|      |------------|
   |Eat(Plant p)     |  |Eat(Meat m)     |     |            |      |            |
   |                 |  |                |     |            |      |            |
   +-----------------+  +----------------+     +------------+      +------------+
            +                    +                    +                   +
            |                    |                    |                   |
            v                    v                    v                   v
   +-----------------+  +----------------+     +------------+      +------------+
   |  Deer           |  |   Lion         |     |  Grass     |      |  DeerMeat  |
   |-----------------|  |----------------|     |------------|      |------------|
   |DeerMeat Die()      |void Kill(Deer) |     |            |      |            |
   +-----------------+  +----------------+     +------------+      +------------+
                                 ^                    ^
                                 |                    |
                                 |                    |
                              Concrete Classes -------+

ご覧のとおり、どちらもeatメソッドを公開していますが、何を食べるかは変わります。ライオンは鹿を殺すことができ、鹿は死んでDeerMeatを返すことができます。そして、ライオンが鹿を食べるが草は食べないようにするというOPの元の質問は、生態系全体を設計することなく答えられます。

もちろん、鹿も肉の一種と考えることができるため、これは非常に興味深いものになりますが、物事を単純にするために、鹿の下にkill()というメソッドを作成し、鹿の肉を返し、それを肉を伸ばす具体的なクラス。


その後、DeerはIMeatインターフェースを公開しますか?
ダンピチェルマン

肉はインターフェースではなく、抽象クラスです。私はあなたのためにそれを実装する方法を追加しました
-Ampt

Eat(Plant p)そして、Eat(Meat m)の両方がLSPに違反します。
Tulainsコルドバ

@ user61852はどうですか?私は、動物の種類ごとにEatを公開しませんでした。これにより、各タイプの動物が独自のeatメソッドを持つことができます。
アンプト

1
TCWL(複雑すぎる、リークします)。問題は分散して発生し、ソリューションは静的で集中化され、事前定義されています。TCWL。
Tulainsコルドバ

7

私のデザインは次のようになります。

  1. 食品はインターフェースとして宣言されます。IFoodインターフェースと、そこから派生した2つのインターフェース:IMeatとIVegetableがあります
  2. 動物はIMeatを実装し、野菜はIVegetableを実装します
  3. 動物には、肉食動物と食虫動物の2つの子孫がいます
  4. 肉食動物にはIMeatのインスタンスを受け取るEatメソッドがあります
  5. 草食動物には、IVegetableのインスタンスを受け取るEatメソッドがあります
  6. ライオンは肉食動物から降りる
  7. 鹿は草食動物から降りる
  8. 草は野菜から降ります

動物はIMeatを実装しており、鹿は(草食動物)動物であるため、ライオンはIMeatを食べることができる(肉食動物)動物でもあり、鹿も食べることができます。

シカは草食動物なので、IVegetableを実装しているため草を食べることができます。

肉食動物はIVegeableを食べることができず、草食動物はIMeatを食べることができません。


1
ここでは、継承される型が何も実装しない場合に制約するためだけに、継承を使用する列挙型が多数見られます。あなたはコードでユーザビリティに何も値が得られていないタイプのシステムでモデルを展開してきました
ジミー・ホッファ

人間、類人猿、熊のような雑食動物が存在することを覚えておいてください。
Tulainsコルドバ

それでは、ライオンとシカの両方が哺乳類であるとどのように追加しますか?:
ヨハネス

2
@JimmyHoffaこれらは「マーカーインターフェイス」と呼ばれ、インターフェイスの完全に有効な使用法です。使用が正当化されるかどうかを判断するためにコードをレビューする必要がありますが、多くの使用例があります(この例では、草を食べようとしているライオンがNoInterface例外をスローします)。マーカーインターフェイス(または不足)は、サポートされていない引数でメソッドが呼び出された場合にスローされる例外を予告するのに役立ちます。
rwong

1
@rwong私はこの概念を理解していますが、それが以前に公式化されたのを聞いたことはありません。私の経験は、私が働いているコードベースが物事をより複雑にし、維持するのを難しくするたびに経験したことです。おそらく、私の経験は、人々が間違った使い方をしただけのことでしょう。
ジミー・ホッファ

5

動物が食べることができる食べ物は実際には階層を形成しません。この場合、自然は単純なオブジェクト指向モデリングに適合しませんでした(当然ですが、動物は食べ物であるため、食べ物から継承する必要があることに注意してください)。

動物がどんな食物を食べられるかを知ることは、どちらのクラスでも完全に生きることができないので、食物階層のあるメンバーを参照するだけでは、どんなものを食べることができるかを伝えるのに十分ではありません。

多対多の関係です。つまり、動物を追加するたびに、食べることができるものを把握する必要があり、食べ物を追加するたびに、食べることができるものを把握する必要があります。活用する構造がさらにあるかどうかは、モデリングしている動物や食品によって異なります。

多重継承でも​​、これをうまく解決できません。動物が食べられるもの、または食べ物を食べることができる動物のある種のコレクションが必要です。


彼らが正規表現について言うように、「私は問題があったので正規表現を使用し、今は2つの問題がある」、MIは「問題があったのでMIを使用した、今は99の問題がある」という線に沿っていますd食べ物が何を食べることができるかを知っているにもかかわらず、あなたがここで突っ込んでいた無駄に続きます。依存関係反転FTW。
ジミー・ホッファ

1

私は別の側面から問題にアプローチします。OOPは動作に関するものです。あなたの場合、Grass子になるいくつかの動作がありFoodますか?したがって、あなたの場合、Grassクラスは存在しませんFood。少なくとも、から継承されることはありません。また、コンパイル時に誰が何を食べることができるかを強制する必要がある場合、Animal抽象化が必要かどうかは疑問です。また、肉食動物が草を食べているのを見るのは珍しいことではありませんが、生計のためではありません。

したがって、私はこれを次のように設計します(ASCIアートに煩わされることはありません)。

IEdibleType肉、植物、枝肉などの列挙であるproperty を使用します(これは頻繁には変更されず、特定の動作もありません。したがって、これをクラス階層としてモデル化する必要はありません)。

AnimalメソッドCanEat(IEdible food)Eat(IEdible food)、論理的です。その後、特定の動物は、与えられた状況で与えられた食物を食べることができるときはいつでもチェックでき、その後、その食物を食べて栄養を得る/何かをすることができます。また、動物の階層の一部としてではなく、戦略パターンとしてCarnivore、Herbivore、Omnivoreクラスをモデル化します。


1

TL; DR:コンテキストを使用した設計またはモデル。

あなたの質問はあなたが解決しようとしている実際の問題の文脈に欠けているので難しいと思います。いくつかのモデルといくつかの関係がありますが、動作するために必要なフレームワークがありません。コンテキストがなければ、モデリングと隠phorはうまく機能せず、複数の解釈への扉が開かれたままになります。

データがどのように消費されるかに注目する方が生産的だと思います。データ使用のパターンを取得したら、モデルと関係がどうあるべきかをさかのぼることが簡単になります。

たとえば、より詳細な要件では、異なるオブジェクトの関係が必要になります。

  • Animals eatFood類似のサポートGastroliths
  • ChocolateとしてサポートしますPoisonDogs、サポートしませんHumans

提示された単純な関係をモデル化する方法の演習から始めると、Food Interfaceが最適かもしれません。そして、それがシステム内の関係がどのように合計である場合、あなたの罰金。ただし、いくつかの追加要件または関係のみが、単純なケースで機能するモデルと関係に大きな影響を与える可能性があります。


私は同意しますが、それはほんの小さな例であり、世界をモデル化しようとしていませんでした。たとえば、タイヤとナンバープレートを食べるサメを飼うことができます。あらゆる種類のオブジェクトを食べるメソッドで親抽象クラスを作成するだけで、Foodはこの実像クラスを拡張できます。
hagensoft

@hagensoft:同意しました。データがどのように消費され使用される必要があるかを見るのではなく、開発者がすぐに捕らえたメタファーに基づいてモデリングするのを常に見ているため、私は時々夢中になります。最初のアイデアに基づいたオブジェクト指向設計と結婚し、解決策を問題に適合させるのではなく、問題を解決策に適合させようとします。
-Dietbuddha

1

ECS Composition-Over-Inheritanceアプローチ:

An entity is a collection of components.
Systems process entities through their components.

Lion has claws and fangs as weapons.
Lion has meat as food.
Lion has a hunger for meat.
Lion has an affinity towards other lions.

Deer has antlers and hooves as weapons.
Deer has meat as food.
Deer has a hunger for plants.

Grass has plant as food.

擬似コード:

lion = new Entity("Lion")
lion.put(new Claws)
lion.put(new Fangs)
lion.put(new Meat)
lion.put(new MeatHunger)
lion.put(new Affinity("Lion"))

deer = new Entity("Deer")
deer.put(new Antlers)
deer.put(new Hooves)
deer.put(new PlantHunger)

grass = new Entity("Grass")
grass.put(new Plant)

Natureは、systemこれらのエンティティをループし、一般化されたクエリ関数を介してそれらが持つコンポーネントを探します。Nature肉に飢えているエンティティが、そのエンティティに対して親和性を持たない限り、武器を使用して肉を食物として持っている他のエンティティを攻撃します。攻撃が成功すると、実体は被害者を餌にし、その時点で被害者は肉を奪われた死体に変わります。Nature植物が存在する限り、植物に飢えているエンティティが、植物を食物として持つエンティティを食べさせます。

Nature({lion, deer, grass})

Nature(entities)
{
    for each entity in entities:
    {
       if entity.has("MeatHunger"):
           attack_meat(entity, entities.with("Meat", exclude = entity))
       if entity.has("PlantHunger"):
           eat_plants(entity, entites.with("Plant", exclude = entity))
    }
}

おそらく、私たちGrassは日光と水を必要とするように拡張したいと考えており、私たちは世界に日光と水を取り入れたいと考えています。まだGrass持っていないので、これらを直接探し出すことはできませんmobilityAnimals水も必要かもしれませんが、彼らが持っているので積極的にそれを探すことができmobilityます 新しいコンポーネントを追加し、システム(またはシステムの数)の動作を拡張するだけなので、設計全体を壊すことなくこのモデルを拡張および変更し続けるのは非常に簡単です。


0

多重継承を使用せずに、オブジェクト指向設計を使用して、この問題をどのように解決しますか?

ほとんどのものと同様に、それは依存します。

それは何に依存し、あなたがする「この問題」を参照します。

  • それは一般的な実装の問題ですか、たとえば、選択したプラットフォームに多重継承がないことを「回避する」方法ですか?
  • これはこの特定のケースだけの設計上の問題ですか、たとえば、動物も食物であるという事実をどのようにモデル化するのでしょうか?
  • それは、ドメインモデルの哲学的問題ですか。たとえば、「食品」と「動物」は、想定される実際の用途に対して有効で、必要で、十分な分類ですか?

一般的な実装の問題について質問している場合、答えは環境の機能によって異なります。IFoodおよびIAnimalインターフェースは、両方のインターフェースを実装するEdibleAnimalサブクラスで機能します。環境がインターフェイスをサポートしていない場合は、AnimalをFoodから継承するようにします。

この特定の設計上の問題について質問している場合は、AnimalにFoodを継承させてください。これはおそらく動作する最も簡単なものです。

これらの設計概念について質問している場合、答えはモデルをどうするかによって大きく異なります。犬を食べる犬のビデオゲームや、動物園での給餌スケジュールを追跡するアプリケーションの場合でも、動作するのに十分かもしれません。動物の行動パターンの概念モデルの場合は、おそらく少し浅いです。


0

継承は、常に他の何かであり、変更できないものに使用する必要があります。草はいつも食べ物ではありません。たとえば、草は食べません。

特定の動物の食料の役割を果たします。


その単なる抽象化。それが要件である場合、Plant抽象クラスを拡張し、人間が「HumanEatablePlants」のような抽象クラスを食べるようにさらに部門を作成して、人間が食べる植物を具体的なクラスにグループ化できます。
hagensoft

0

OOの基本的な制限にちょうど遭遇しました。

オブジェクト指向は階層構造でうまく機能します。ただし、厳密な階層から抜けると、抽象化はあまりうまくいきません。

私はこれらの制限を回避するために使用される変態構成などについてすべてを知っていますが、それらは不器用であり、さらに重要なことに、不明瞭でコードに従うのが困難になります。

リレーショナルデータベースは、主に厳密な階層構造の制限から逃れるために考案されました。

例を挙げると、草は建築材料、紙の原料、衣類材料、雑草、または作物にもなり得ます。

鹿は、ペット、家畜、動物園の動物、または保護された種です。

ライオンは動物園の動物または保護された種である可能性もあります。

人生は単純ではありません。


0

多重継承を使用せずに、オブジェクト指向設計を使用して、この問題をどのように解決しますか?

何の問題? このシステムは何をしますか?あなたがそれに答えるまで、どのクラスが必要なのか分かりません。肉食動物や草食動物や植物で生態学をモデル化し、種の個体数を将来に投影しようとしていますか?コンピューターに20個の質問をさせようとしていますか?

ユースケースが定義される前に設計を開始するのは時間の無駄です。約10人のチームが写真からソフトウェアを使用して航空会社のOOモデルを作成し始めたとき、私はこれをとんでもない極端に捉えました。彼らは実際のビジネス上の問題を念頭に置いて2年間モデリングを行いました。最後に、顧客は待つことにうんざりし、実際の問題を解決するようチームに依頼しました。そのモデリングはすべて完全に役に立たなかった。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.