具有许多常见和一些独特方法的类对象的设计方法

时间:2016-05-25 06:20:33

标签: c# class oop

在下面的示例中,我们有两个Terrier类的实例,它来自Dog
一个实例由Terrier类型的变量引用 使用此变量,您可以访问Terrier类的所有成员 另一方面,类型为Dog的变量只能引用Dog类的成员,即使引用指向Terrier的实例。

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

我需要实现一个MyPets类,其List<Pets>可以包含Cat对象或Dog对象。
这两个对象都有一些常见的方法,如MakeNoise(),但是一些独特的方法也不像Cat这样的基类有方法ClimbTree()。 此MyPets类还有一个方法可以遍历List<animals>并调用MakeNoise()方法和ClimbTree()方法。

使用Abstract基类或其他方法实现此目标的最佳方法是什么?

3 个答案:

答案 0 :(得分: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设计,稍后会在更具体的类中重载。

编辑现在使用访客模式

运行以下代码:

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();

产生以下结果:

  

Bob已经2岁了,感觉Surly

     鲍勃喵喵叫

     鲍勃正在攀登

     

杰克17岁,感觉很好。

     

杰克正在咆哮

     

布巴2岁,感觉很开心

     

布巴是低调的

答案 1 :(得分:0)

在这个答案中,我提出了一个基于属性的解决方案。在某些方面,它可能比@Draken提出的解决方案更容易维护,但在其他方面它不太有用。

这个想法:使用属性注释pet方法,以便将其标记为一种能力。我保持属性非常简单,它只是一个没有任何额外元信息的标记。

IteratePetAbilities()方法是访问宠物能力的关键方法。它使用反射来查找用ability属性标记的方法并调用它们。

// 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子类中添加新功能都很容易,无论它嵌套在子类层次结构中有多深(单点代码更改)

N

  • 使用反射
    • 必须强制执行有效性时变得更复杂(例如,如果将属性应用于具有输入参数的方法,该怎么办?)。
    • 有些人说它比编译时绑定慢(我没有衡量自己,但我倾向于相信它们)
  • 有限控制(例如,如果您想按特定顺序输出宠物能力,则需要额外的工作)
    • 这通常适用于多种能力之间的任何相互依赖

修改

旁注:

如果在x.GetCustomAttributes(typeof(PetAbilityAttribute), false)中调用true(而不是IteratePetAbilities()),则不会发现Cat.MakeNoise,因为覆盖方法未标记属性。< / p>

答案 2 :(得分:0)

如果子类的数量相对较小,您可以简单地使用session.add和/或is运算符。

as

当数据应与不属于您的情况的操作分开时,访客模式值得使用。

由于反思,应避免使用属性解决方案。