1

我的代码中有太多的失望。在 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 虚函数的原因是我可能有许多不同的 Owner 类,例如 BoyOwner、GirlOwner、WomanOwner、ManOwner,每个都有自己喂养宠物的方式。Pet 是一个通用类,被许多其他类使用,但 Owner 类只与 Pet 交互。此外,FeedDog 等函数需要访问 Owner 类的私有成员。

4

3 回答 3

1

您尝试解决的问题是如何根据对象类型进行分派,当您在设计对象时不知道可能需要的所有不同操作时。(其他答案建议将所有必需的操作放在基类中;这有时是不可能的,并且会导致过度耦合)。

答案不是测试对象的类型和类型;如果有的话,你应该很少这样做。一个好的解决方案是使用访问者模式:当您使对象可访问时,您可以通过实现操作而不是更改对象本身来添加在对象类型上调度的新操作。

于 2013-04-24T15:23:24.477 回答
0

好吧,向下转换被认为是糟糕的设计……您应该将通用方法移至基类并使它们抽象或具有一个接口来做到这一点。

如果你有:

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();
}
于 2013-04-24T15:11:59.470 回答
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();
    }
}
于 2013-04-24T15:15:27.150 回答