在下面的示例中,我们有两个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基类或其他方法实现此目标的最佳方法是什么?
答案 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);
}
}
}
使用抽象方法的标准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
当数据应与不属于您的情况的操作分开时,访客模式值得使用。
由于反思,应避免使用属性解决方案。