2016-05-25 10 views
2

以下の例では、から派生したTerrierクラスの2つのインスタンスがあります。
1つのインスタンスは、Terrierという変数で参照されます。
この変数を使用すると、Terrierクラスのすべてのメンバーにアクセスできます。
一方、タイプがDogの変数は、参照がインスタンスTerrierを指しているにもかかわらず、Dogクラスのメンバーのみを参照できます。多くの一般的なメソッドといくつかの一意のメソッドを持つクラスオブジェクトの設計アプローチ

Terrier bubba = new Terrier("Bubba", 2, "Happy"); 
bubba.Growl(); // Can call Terrier.Growl 

Dog jack = new Terrier("Jack", 17, "Surly"); 
jack.Growl();  // ERROR: Can't call Growl method 

私はCatオブジェクトまたはDogオブジェクトのいずれかを保持することができますList<Pets>を持つクラスMyPetsを実装する必要があります。
これらのオブジェクトには、MakeNoise()のようないくつかの一般的なメソッドがありますが、Catのような基本クラスにはない固有のメソッドもあります。ClimbTree()
このMyPetsクラスには、List<animals>を反復し、MakeNoise()メソッドとClimbTree()メソッドを呼び出すメソッドもあります。

抽象基本クラスまたはその他の方法を使用して、これを達成するための最良の方法は何か。私のコメントに関して

+0

「ペット」と「動物」は同じクラスを意味すると思いますか? 'MakeNoise()'と 'ClimbTree()'を呼び出す 'MyPets'のセマンティックコンテキストは何かを説明する必要があります。そうであれば、「ペット」には1つの重要な能力があります。それを基本クラスに抽象化することができます。あなたのペットの能力モデルが異なる場合、他のアプローチがあります。 – grek40

+0

'Dog'と' Cat'の両方が 'makeNoise()'メソッドでそれを継承するように、 'Pet'(またはインターフェース)の抽象基本クラスを使います。次に、あなたの反復中に 'makeNoise()'を呼び出して、それらが 'Cat'のサブクラスかどうかを確認し、そうであれば' climbTree() 'メソッドを呼び出します。 [オブジェクトのキャストを扱うのはここを参照](http:// stackoverflow。実際のタイプのキャストオブジェクト)と[タイプのチェックについてはこちらを参照してください](http://stackoverflow.com/questions/2742276/how-do-i- com/questions/12234097/how-to-its-actual-type)サブタイプまたはオブジェクトのタイプのチェック) – Draken

答えて

1

、これらの線に沿って何かがあなたの問題を解決する必要があります。

public class Visitor 
{ 
    public void doItterate(Cat c) 
    { 
     Console.WriteLine(c.ToString()); 
     c.makeNoise(); 
     c.climbTree(); 
    } 

    public void doItterate(Dog d) 
    { 
     Console.WriteLine(d.ToString()); 
     d.makeNoise(); 
    } 
} 

public abstract class Pet 
{ 
    public Pet(string name, int age, Mood mood) 
    { 
     this.MoodOfPet = mood; 
     this.Name = name; 
     this.Age = age; 
    } 

    public string Name 
    { 
     get; 
     private set; 
    } 

    public int Age 
    { 
     get; 
     private set; 
    } 

    public Mood MoodOfPet 
    { 
     get; 
     private set; 
    } 

    public abstract void makeNoise(); 
    public override string ToString() 
    { 
     return this.Name + " is " + this.Age + 
      " years old and feels " + this.MoodOfPet; 
    } 

    public abstract void accept(Visitor v); 
} 

public enum Mood 
{ 
    Surly, 
    Happy 
} 

public abstract class Dog : Pet 
{ 
    public Dog(string name, int age, Mood mood): base (name, age, mood) 
    { 
    } 

    public override void makeNoise() 
    { 
     Console.WriteLine(this.Name + " is woofing"); 
    } 

    public override void accept(Visitor v) 
    { 
     v.doItterate(this); 
    } 
} 

public class SheepDog : Dog 
{ 
    public SheepDog(string name, int age, Mood mood): base (name, age, mood) 
    { 
    } 
} 

public class Cat : Pet 
{ 
    public Cat(string name, int age, Mood mood): base (name, age, mood) 
    { 
    } 

    public void climbTree() 
    { 
     Console.WriteLine(this.Name + " is climbing"); 
    } 

    public override void makeNoise() 
    { 
     Console.WriteLine(this.Name + " is meowing"); 
    } 

    public override void accept(Visitor v) 
    { 
     v.doItterate(this); 
    } 
} 

public class Terrier : Dog 
{ 
    public Terrier(string name, int age, Mood mood): base (name, age, mood) 
    { 
    } 

    public void growl() 
    { 
     Console.WriteLine(this.Name + " is growling"); 
    } 

    public override void makeNoise() 
    { 
     growl(); 
    } 
} 

public class MyPets 
{ 
    private Visitor visitor = new Visitor(); 
    public MyPets() 
    { 
     Pets = new List<Pet>(); 
    } 

    public List<Pet> Pets 
    { 
     get; 
     private set; 
    } 

    public void addPet(Pet p) 
    { 
     Pets.Add(p); 
    } 

    public void itterate() 
    { 
     foreach (Pet p in Pets) 
     { 
      p.accept(visitor); 
     } 
    } 
} 

Fiddle

それは後でより具体的なクラスでオーバーロードされます抽象メソッドを使用して、標準的なOOPの設計です。

EDIT今では、次のコードを実行しているビジターパターンに

を使用しています:ボブは2歳と

のSurly感じ

MyPets pets = new MyPets(); 
pets.addPet(new Cat("Bob", 2, Mood.Surly)); 
pets.addPet(new Terrier("Jack", 17, Mood.Surly)); 
pets.addPet(new SheepDog("Bubba", 2, Mood.Happy)); 
pets.itterate(); 

は、これらの結果を生成します

ボブは鳴っています

ボブは

ジャックは17歳の登山とのSurly

ジャックは

ババは2歳うなるされ感じているとハッピー

ババは

+0

特別な能力を持つ動物がパーティーに加わると、「itterate」の部分がかなり醜いものになります。 – grek40

+0

ええ、本当です。しかし、それはあなたがあなたがよりよい解決策を持っていると感じるなら、OPがそれを望んでいたものです、私は(そして私はOPを前にしています)提案に開かれています – Draken

+1

訪問者のパターンをスローする前に、能力の抽象化または注釈/リフレクションをプールに追加する。 OPがより具体的な要件を述べていない限り、あなたの答えは孤立しているので問題ありません。 – grek40

0

をwoofingされ感じていますこの回答では、属性ベースのソリューションを提示します。いくつかの側面では、@Drakenによって提案された解決策よりも保守が簡単かもしれませんが、他の面ではそれはあまり有用ではありません。

アトリビュートとしてマークするために、アトリビュートを持つペットメソッドに注釈を付けるアイデア。私はその属性を非常にシンプルにしました。それは、追加のメタ情報を持たない唯一のマーカーです。

IteratePetAbilities()の方法は、ペットの能力にアクセスするための重要な方法です。これは、リフレクションを使用して、能力属性でマークされたメソッドを見つけ出し、それらを呼び出す。

// Note: Target method is required to have an empty parameter list 
[AttributeUsage(AttributeTargets.Method)] 
public sealed class PetAbilityAttribute : Attribute 
{ 
} 


public class MyPets 
{ 
    public MyPets() 
    { 
     Pets = new List<Pet>(); 
    } 

    public ICollection<Pet> Pets { get; set; } 

    // Discover PetAbilityAttribute methods on the concrete pet type and invoke them dynamically 
    public void IteratePetAbilities() 
    { 
     foreach (var pet in Pets) 
     { 
      Console.WriteLine("Pet '" + pet.PetName + "' enters the stage"); 
      var abilities = pet.GetType().GetMethods().Where(x => x.GetCustomAttributes(typeof(PetAbilityAttribute), true).Any()); 
      foreach (var abilityMethod in abilities) 
      { 
       Console.Write("# {0,12}() # ", abilityMethod.Name); 
       abilityMethod.Invoke(pet, new object[] { }); 
      } 
      Console.WriteLine(); 
     } 
    } 
} 


public abstract class Pet 
{ 
    public string PetName { get; set; } 

    [PetAbility] 
    public abstract void MakeNoise(); 

    // Note: this is not marked as an ability here 
    public abstract void GoSleep(); 
} 


public class Dog : Pet 
{ 
    [PetAbility] // no effect, since base already has this attribute 
    public override void MakeNoise() 
    { 
     Console.WriteLine("Says woof"); 
    } 

    // not marked as an ability 
    public override void GoSleep() 
    { 
     Console.WriteLine("Goes to the dogs house and sleeps"); 
    } 
} 


public class Terrier : Dog 
{ 
    [PetAbility] 
    public void HuntACat() 
    { 
     Console.WriteLine("Starts running after a poor little cat"); 
    } 


    [PetAbility] // Unlike a regular dog, the Terrier goes to sleep by ability :) 
    public override void GoSleep() 
    { 
     base.GoSleep(); 
    } 
} 


public class Cat : Pet 
{ 
    public override void MakeNoise() 
    { 
     Console.WriteLine("Says meow"); 
    } 

    [PetAbility] 
    public void ClimbTree() 
    { 
     Console.WriteLine("Climbs a tree and is to scared to return on its own"); 
    } 

    [PetAbility] // makes GoSleep an ability only for cats, even though the method exists in base class 
    public override void GoSleep() 
    { 
     Console.WriteLine("Refuses to sleep and starts sharpening its claws"); 
    } 
} 



class Program 
{ 
    static void Main(string[] args) 
    { 
     var myPets = new MyPets(); 
     myPets.Pets.Add(new Cat() { PetName = "Super Cat" }); 
     myPets.Pets.Add(new Dog() { PetName = "Little Dog" }); 
     myPets.Pets.Add(new Terrier() { PetName = "Hunter" }); 

     myPets.IteratePetAbilities(); 
    } 

} 

出力

Pet 'Super Cat' enters the stage 
# MakeNoise() # Says meow 
# ClimbTree() # Climbs a tree and is to scared to return on its own 
#  GoSleep() # Refuses to sleep and starts sharpening its claws 

Pet 'Little Dog' enters the stage 
# MakeNoise() # Says woof 

Pet 'Hunter' enters the stage 
#  HuntACat() # Starts running after a poor little cat 
#  GoSleep() # Goes to the dogs house and sleeps 
# MakeNoise() # Says woof 

プロ

  • 関係なく、それはサブクラス階層(単一のポイントにネストされているか深く、任意のPetサブクラスで新しい能力を追加するのは簡単ではありませんコード変更の内容)

コン

  • は妥当性が施行されなければならない場合
    • が複雑になる反射を使用します(たとえば、属性が入力パラメータを持つメソッドに適用された場合にどのような?)。
    • 一部の人々は、それが時間のバインディングをコンパイルよりも遅いと言う(私は自分自身を測定していなかったが、私はそれを信じる傾向にある)特定の中
  • 限定制御(たとえば、出力したい場合は、ペットの能力ため、追加の作業が必要です)
    • これは、一般的にE

複数の能力との間の相互依存関係に適用されますDIT

サイドノート:オーバーライドメソッドは属性でマークされていないため、

(代わりにtrueの)x.GetCustomAttributes(typeof(PetAbilityAttribute), false)場合にはIteratePetAbilities()で呼び出され、その後Cat.MakeNoiseは、発見されません。

0

サブクラスの数が比較的少ない場合は、単にisおよび/またはas演算子を使用できます。

List<Pet> pets = new List<Pet> 
{ 
    new Cat("cat1", 1, "Happy"), 
    new Cat("cat2", 2, "Happy"), 
    new Dog("dog", 1, "Surly"), 
    new Terrier("dog", 3, "Happy") 
}; 

// in your Iterate() method 
foreach (var pet in pets) 
{ 
    // common methods here 
    pet.MakeNoise(); 

    if (pet is Cat) 
    { 
     var animal = (Cat)pet; 
     animal.ClimbTree(); 
    } 
    else if (pet is Terrier) 
    { 
     var animal = (Terrier)pet; 
     animal.Growl(); 
    } 
} 

あなたのケースではない操作からデータを分離する必要がある場合は、ビジターパターンを使用する価値があります。

属性の解決は、反映のために避けるべきです。

+0

実際には、データが分離されています'Pet'クラスの階層にはデータが含まれているので(能力もデータと考える)、その能力にアクセスする操作はデータ階層の一部ではないMyPetsに属しているからです。しかし、簡単なif-else解決法もここに属していることに同意します。残念ながら、@Drakenはビジターパターンを追加するのではなく、彼の答えからこれを編集しました。 – grek40

関連する問題