Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/337.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
与继承C#示例相比,如何最大化此接口中的代码重用_C#_Oop_Inheritance_Interface - Fatal编程技术网

与继承C#示例相比,如何最大化此接口中的代码重用

与继承C#示例相比,如何最大化此接口中的代码重用,c#,oop,inheritance,interface,C#,Oop,Inheritance,Interface,灵感来源于主题“偏爱对象组合而非继承”,该主题使用了JavaScript示例;我想在C#中尝试一下,以测试我对这个概念的理解,但没有达到我希望的效果 /// PREMISE // Animal base class, Animal can eat public class Animal { public void Eat() { } } // Dog inherits from Animal and can eat and bark public class Dog : Animal {

灵感来源于主题“偏爱对象组合而非继承”,该主题使用了JavaScript示例;我想在C#中尝试一下,以测试我对这个概念的理解,但没有达到我希望的效果

/// PREMISE
// Animal base class, Animal can eat
public class Animal
{
    public void Eat() { }
}
// Dog inherits from Animal and can eat and bark
public class Dog : Animal
{
    public void Bark() { Console.WriteLine("Bark"); }
}
// Cat inherits from Animal and can eat and meow
public class Cat : Animal
{
    public void Meow() { Console.WriteLine("Meow"); }
}
// Robot base class, Robot can drive
public class Robot
{
    public void Drive() { }
}
问题是我想添加一个机器人狗类,它可以吠叫和驾驶,但不能吃东西。


第一个解决方案是创建RobotDog作为Robot的子类

public class RobotDog : Robot
{
    public void Bark() { Console.WriteLine("Bark"); }
}
但是为了给它一个树皮函数,我们必须复制并粘贴狗的树皮函数,所以现在我们有了重复的代码


第二个解决方案是创建一个带有树皮方法的公共超类,然后动物类和机器人类都继承自

public class WorldObject
{
    public void Bark() { Console.WriteLine("Bark"); }
}
public class Animal : WorldObject { ... }
public class Robot : WorldObject { ... }
但现在,每一种动物和每一个机器人都会有一种吠叫方法,而大多数动物和机器人并不需要这种方法。继续使用这种模式,子类将装载它们不需要的方法


第三种解决方案是为可以吠叫的类创建IBarkable接口

public interface IBarkable
{
    void Bark();
}
并在Dog和RobotDog类中实现它

public class Dog : Animal, IBarkable
{
    public void IBarkable.Bark() { Console.WriteLine("Bark"); }
}
public class RobotDog : Robot, IBarkable
{
    public void IBarkable.Bark() { Console.WriteLine("Bark"); }
}
但我们又一次有了重复的代码


第四种方法是再次使用IBarkable接口,但创建一个树皮助手类,然后每个Dog和RobotDog接口实现调用该类

这感觉像是最好的方法(视频似乎也推荐了这种方法),但我也可以看到项目中出现了一个问题:助手太多


第五个建议的(hacky?)解决方案是在一个空的IBarkable接口上挂起一个扩展方法,这样如果您实现IBarkable,那么您就可以使用它了

public interface IBarker {    }
public static class ExtensionMethods
{
    public static void Bark(this IBarker barker) { 
        Console.WriteLine("Woof!"); 
    }
}

这个网站上有很多类似的问题,以及我读过的文章,似乎都建议使用
抽象类
类,但是,这些类的问题与解决方案2不一样吗



将RobotDog类添加到此示例中的最佳面向对象方法是什么?

我认为这更像是一个概念上的两难问题,而不是组合问题

当你说: 并在Dog和RobotDog类中实现它

public class Dog : Animal, IBarkable
{
    public void IBarkable.Bark() { Console.WriteLine("Bark"); }
}
public class RobotDog : Robot, IBarkable
{
    public void IBarkable.Bark() { Console.WriteLine("Bark"); }
}
但我们又一次有了重复的代码


如果Dog和RobotDog具有相同的Bark()实现,则它们应该从Animal类继承。但如果它们的Bark()实现不同,则从IBarkable接口派生是有意义的。否则,Dog和RobotDog的区别在哪里?

首先,如果您想遵循“组合优先于继承”,那么超过一半的解决方案不适合,因为您仍然在这些解决方案中使用继承

实际上,通过“组合而非继承”实现它存在多种不同的方法,可能每种方法都有各自的优点和缺点。起初,这是一种可能的方式,但目前在C#中是不可能的。至少不能使用重写IL代码的扩展。一个典型的想法是使用mixin。所以您有接口和一个Mixin类。Mixin基本上只包含被“注入”到类中的方法。它们不是从中衍生出来的。所以您可以有这样一个类(所有代码都是伪代码)

IEat
IBark
提供了接口,而
MEat
MBark
则是可以注入的带有一些默认实现的混搭。这样的设计在JavaScript中是可行的,但目前在C#中不可行。它的优点是,最终您拥有了一个
RobotDog
类,该类拥有
IEat
IBark
的所有方法,并具有共享实现。同时,这也是一个缺点,因为您使用许多方法创建大型类。除此之外,还可能存在方法冲突。例如,当您想要注入两个具有相同名称/签名的不同接口时。虽然这种方法看起来很好,但我认为缺点很大,我不鼓励这样的设计


由于C#不直接支持mixin,您可以使用扩展方法以某种方式重新构建上述设计。所以您仍然有
IEat
IBark
接口。并为接口提供扩展方法。但它与mixin实现具有相同的缺点。所有方法都出现在对象上,方法名称冲突问题。最重要的是,组合的思想也是您可以提供不同的实现。您还可以为同一接口使用不同的mixin。最重要的是,mixin只是用于某种默认实现,其思想仍然是您可以覆盖或更改方法

用扩展方法做这种事情是可能的,但我不会使用这样的设计。理论上,您可以创建多个不同的名称空间,因此根据加载的名称空间,您可以使用不同的实现获得不同的扩展方法。但这样的设计让我觉得更尴尬。所以我不会使用这样的设计


我解决这个问题的典型方法是,为您想要的每一个行为指定字段。所以你的机器狗看起来像这样

class RobotDog(ieat, ibark)
    IEat  Eat  = ieat
    IBark Bark = ibark
所以这意味着。您有一个包含两个属性的类
Eat
Bark
。这些属性的类型为
IEat
IBark
。如果您想创建一个
RobotDog
实例,那么您必须传入您想要使用的特定
IEat
IBark
实现

let eat  = new CatEat()
let bark = new DogBark()
let robotdog = new RobotDog(eat, bark)
现在机器狗会像猫一样吃东西,像狗一样吠叫。你可以叫你的机器狗做什么

robotdog.Eat.Fruit()
robotdof.Eat.Drink()
robotdog.Bark.Loud()
现在,机器狗的行为完全取决于构建对象时提供的注入对象。您还可以在运行时将行为切换到另一个类。如果你的机器狗在游戏中,吠叫得到了升级,你可以在运行时用另一个对象和你想要的行为来代替吠叫

robotdog.Bark <- new DeadlyScreamBarking()

以前的设计可以稍微改变一下,但我不认为这会使它更好。但是很多人都是无脑地领养的
robotdog.Bark <- new DeadlyScreamBarking()
robotdog.Health.Increase(10)
robotdog.Shield.Increase(10)
robotdog.Eat.Fruit()
robotdog.Eat.Drink()
robotdog.EatFruit()
robotdog.EatDrink()
robotdog.IncreaseHealt(10)
robotdog.IncreaseShield(10)