我的代码中有太多的向下转发。在c ++中,我可以使用模板来避免向下转换。但是c#中的以下示例的最佳实现是什么?
class Pet { bool mIsDog; }
class Dog : Pet // mIsDog = true
class Cat : Pet // mIsDog = false
class Owner //There are many different owner classes, may come from different dlls
{
private List<Food> mFoods;
private void FeedDog(Dog dog)
{
... //Use mFoods, which is invisible to dog
}
void HandlePet(Pet pet)
{
if(pet.mIsDog) //In c++ I can use templates to avoid downcasting
FeedDog((Dog)pet);
else
FeedCat((Cat)pet);
... //code handling general pet
while(pet.IsUnhappy())
{
if(pet.mIsDog) //In c++ I can use templates to avoid downcasting
PlayWithDog((Dog)pet);
else
PlayWithCat((Cat)pet);
}
... //a lot of code handling general pet or specific pet (cat or dog)
}
}
请注意,函数HandlePet具有非常复杂的逻辑,具有多级缩进,因此很难将其分解为多个单独的函数。
我没有在Pet类中使用BeFedWith或BePlayedWith虚拟函数的原因是我可能有许多不同的所有者类,例如BoyOwner,GirlOwner,WomanOwner,ManOwner,每个类都有自己的方式来喂养宠物。 Pet是一个常见的类,并被许多其他类使用,但Owner类只与Pet交互。此外,FeedDog等函数需要访问Owner类的私有成员。
答案 0 :(得分:1)
您尝试解决的问题是如何根据对象类型进行调度,当您在设计对象时不知道可能需要的所有不同操作。 (其他答案建议将所有必需的操作放在基类中;这有时是不可能的,并导致过度耦合)。
答案不是测试对象的类型和强制转换;如果有的话,你应该很少这样做。一个好的解决方案是使用Visitor pattern:当你使对象可访问时,可以通过实现操作来添加在对象类型上调度的新操作,而不是通过更改对象本身。
答案 1 :(得分:0)
嗯,向下倾斜被认为是糟糕的设计...... 你应该将常用方法移动到baseclass并使它们抽象或有一个接口来实现它。
如果你有:
public interface Pet{
void Feed();
void Play();
}
public class Dog:Pet{
public void Feed(){
...dog feeding code...
}
public void Play(){
...dog playing code...
}
}
public class Cat:Pet{
public void Feed(){
...cat feeding code...
}
public void Play(){
...cat playing code...
}
}
然后你handlePet函数变为:
void HandlePet(Pet pet){
pet.Feed();
while(pet.isUnhappy())
pet.Play();
}
答案 2 :(得分:0)
如果你必须按照你的建议行事,保持Feed等方法属于所有者而不是宠物,那么你可以利用动态绑定来调用正确的所有者方法:
using System;
class Pet { }
class Dog : Pet { }
class Cat : Pet { }
class Owner
{
public void HandlePet(Pet pet)
{
HandlePet((dynamic)pet);
}
private void HandlePet(Cat pet)
{
Console.WriteLine("Stroke the cat softly whispering Please don't scratch me.");
}
private void HandlePet(Dog pet)
{
Console.WriteLine("Stroke the dog firmly saying Who's a good boy?");
}
}
class Program
{
static void Main(string[] args)
{
var owner = new Owner();
Console.WriteLine("Handle dog first");
owner.HandlePet(new Dog());
Console.WriteLine("Handle cat second");
owner.HandlePet(new Cat());
Console.ReadKey();
}
}
输出:
首先处理狗 坚定地说狗谁是个好男孩? 处理猫秒 轻轻地抚摸着猫,请不要刮我。
如果你可以避免它,那么我会把方法放在Pet类上,如下所示:
一种方法是使Pet成为一个抽象类,它具有EatFood
和Play
抽象方法。 Dog
和Cat
类将实现这些方法:
using System;
abstract class Pet
{
public abstract void EatFood();
public abstract void Play();
public void BrushHair()
{
Console.WriteLine("Hair brushed!");
}
}
class Dog : Pet
{
public override void EatFood()
{
Console.WriteLine("Eating dog food...");
}
public override void Play()
{
Console.WriteLine("Run around manically barking at everything.");
}
}
class Cat : Pet
{
public override void EatFood()
{
Console.WriteLine("Eating cat food...");
}
public override void Play()
{
Console.WriteLine("Randomly choose something breakable to knock over.");
}
}
class Owner
{
public void HandlePet(Pet pet)
{
pet.EatFood();
pet.Play();
pet.BrushHair();
}
}
class Program
{
static void Main(string[] args)
{
var owner = new Owner();
Console.WriteLine("Handle dog first");
owner.HandlePet(new Dog());
Console.WriteLine("Handle cat second");
owner.HandlePet(new Cat());
Console.ReadKey();
}
}
输出:
首先处理狗 吃狗粮...
对所有事情都狂奔吠叫 刷头发!
处理猫秒 吃猫粮...
随机选择易碎的东西来敲门 头发刷了!
如果您想使用访客模式,请尝试以下代码:
using System;
public interface IPetVisitor
{
void Visit(Dog dog);
void Visit(Cat cat);
}
public interface IPetAcceptor<T>
{
void Accept(T visitor);
}
public abstract class Pet : IPetAcceptor<IPetVisitor>
{
public abstract void Accept(IPetVisitor visitor);
}
public class Dog : Pet
{
public override void Accept(IPetVisitor visitor)
{
visitor.Visit(this);
}
}
public class Cat : Pet
{
public override void Accept(IPetVisitor visitor)
{
visitor.Visit(this);
}
}
class Owner
{
// Private variable on owner class
private int HandCount = 2;
// Pet handler is an inner class so we can access the enclosing class' private member.
public class PetHandler : IPetVisitor
{
private Owner Owner;
public PetHandler(Owner owner)
{ Owner = owner; }
public void Visit(Dog dog)
{
Console.WriteLine("Pet the dog with {0} hands", Owner.HandCount);
}
public void Visit(Cat cat)
{
Console.WriteLine("Pet the cat with {0} hands", Owner.HandCount);
}
}
private PetHandler PetHandlerInstance;
public Owner()
{
PetHandlerInstance = new PetHandler(this);
}
public void HandlePet(IPetAcceptor<IPetVisitor> pet)
{
pet.Accept(PetHandlerInstance);
}
}
class Program
{
static void Main(string[] args)
{
var owner = new Owner();
Console.WriteLine("Handle dog first");
owner.HandlePet(new Dog());
Console.WriteLine("Handle cat second");
owner.HandlePet(new Cat());
Console.ReadKey();
}
}