How to delegate interface calls to a member that implements the interface

老子叫甜甜 提交于 2019-12-11 11:18:33

问题


Suppose, I have an interface ICry that has functions that return the sound of an animal:

public interface ICry
{
    string Cry();
}

I have several classes that implement ICry: class Cat : ICry { public string Cry { get { return "Meow!"; } } }

class Dog: ICry
{
    public string Cry { get { return "Woof"; } }
}

If I have a PetOwner object that owns a pet, and I know this PetOwner can Cry(), but want to hide how he cries, I could do the following:

class PetOwner : ICry
{
    private Cat cat = new Cat();
    public string Cry() {return cat.Cry();}
}

Then usage would be:

PetOwner john = new PetOwner();
console.WriteLine(john.Cry());

So far so good, everything quiet on the western front

But what to do if ICry has a lot of functions. Using this method obliges you to implement a PetOwner function for all ICry functions. Although these functions wouldn't do much more than call the corresponding function of Cat.ICry, it will still be a lot of useless work.

Is there a pattern to prevent this?


回答1:


The reason why you would choose to use an interface instead of derivation is because you want to hide HOW something is done. You use an interface if you want to express that a class is capable of doing something. In this case you want to show that John is capable to produce an animal sound.

The same question was asked here: implements interface using member as implementor

The solution there was to derive the pet owner from the cat. But what if you have to implement two interfaces? Derive from two classes?

Besides, a PetOwner isn't a Cat. A cat can do a lot of things that a PetOwner probably can't.

One of the solutions given in the link is to remove the ICry from the PetOwner and give the PetOwner a property to get the Cat and then call the Cat's ICry functions:

class PetOwner
{
    private Cat cat = new Cat();
    public Cat Cat {return cat;}}
}

usage would be:

PetOwner John = new PetOwner();
John.Cat.Cry();

Using this method, users of PetOwners will have to know that the PetOwner will not cry himself, but uses his cat to cry.

The problem is, that if your PetOwner decides to get rid of the cat and buy a dog, your code doesn't work anymore. Your plan to use the ICry interface doesn't work.

If you really want to hide how the PetOwner cries, but you don't want to implement all functions of you ICry, don't implement the interface, but implement a get property that will give you an object that implements the ICry interface. Like this:

interface ICry
{
    string Cry { get; }
}

class Cat : ICry
{
    public string Cry { get { return "Meow!"; } }
}

class Dog: ICry
{
    public string Cry { get { return "Woof"; } }
}

class PetOwner
{
    private Cat cat = new Cat();
    public ICry ICry {get{return cat;}}
}

Usage:

PetOwner John = new PetOwner();
John.ICry.Cry();

Now you don't know HOW John cries. You only know that John will take care that Cry() is done. So if John decide to take a dog, and decides that from now on the dog will have to Cry(), the user doesn't have to change:

class PetOwner
{
    private Cat cat = new Cat();
    private Dog dog = new Dog();
    public ICry ICry {get{return dog;} }
}

usage is not changed: PetOwner John = new PetOwner(); John.ICry.Cry();

Even if John decides to get rid of all his animals and Cry() himself users don't have to change:

class PetOwner : ICry // The petowner has decide to Cry himself
{
    public ICry ICry {get{return this;}}
    public string Cry { get { return "Hello!"; } }
}

usage is still not changed: PetOwner John = new PetOwner(); John.ICry.Cry();



来源:https://stackoverflow.com/questions/18253366/how-to-delegate-interface-calls-to-a-member-that-implements-the-interface

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!