欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

c# 如何用组合替代继承

程序员文章站 2022-12-01 21:21:42
如果问面向对象的三大特性是什么,多数人都能回答出来:封装、继承、多态。继承 作为三大特性之一,近来却越来越不推荐使用,更有极端的语言,直接语法中就不支持继承,例如 go。这又是为什么呢?为什么不推荐使...

如果问面向对象的三大特性是什么,多数人都能回答出来:封装、继承、多态。

继承 作为三大特性之一,近来却越来越不推荐使用,更有极端的语言,直接语法中就不支持继承,例如 go。这又是为什么呢?

为什么不推荐使用继承?

假设我们要设计一个关于鸟的类。

我们将“鸟类”定义为一个抽象类 abstractbird。所有更细分的鸟,比如麻雀、鸽子、乌鸦等,都继承这个抽象类。

大部分鸟都会飞,那我们可不可以在 abstractbird 抽象类中,定义一个 fly() 方法呢?

答案是否定的。尽管大部分鸟都会飞,但也有特例,比如鸵鸟就不会飞。鸵鸟继承具有 fly() 方法的父类,那鸵鸟就具有“飞”这样的行为,这显然不符合我们对现实世界中事物的认识。

解决方案一

在鸵鸟这个子类中重写 fly() 方法,让它抛出异常。

public class abstractbird
{
  public virtual void fly()
  {
    console.writeline("i'm flying.");
  }
}

//鸵鸟
public class ostrich : abstractbird
{
  public override void fly()
  {
    throw new notimplementedexception("i can't fly.");
  }
}

这种设计思路虽然可以解决问题,但不够优美。因为除了鸵鸟之外,不会飞的鸟还有很多,比如企鹅。对于这些不会飞的鸟来说,我们都需要重写 fly() 方法,抛出异常。

这违背了迪米特法则(也叫最少知识原则),暴露不该暴露的接口给外部,增加了类使用过程中被误用的概率。

解决方案二

通过 abstractbird 类派生出两个更加细分的抽象类:会飞的鸟类 abstractflyablebird 和不会飞的鸟类 abstractunflyablebird,让麻雀、乌鸦这些会飞的鸟都继承 abstractflyablebird,让鸵鸟、企鹅这些不会飞的鸟,都继承 abstractunflyablebird 类。

此时,继承关系变成了三层,还行得通。

如果要再添加一个游泳 swim() 的方法,那情况就复杂了,要分为四中情况:

  • 会飞会游泳
  • 会飞不会游泳
  • 不会飞会游泳
  • 不会飞不会游泳

如果再有其他行为加入,抽象类的数量就会几何级数增长。

我们要搞清楚某个类具有哪些方法、属性,必须阅读父类的代码、父类的父类的代码……一直追溯到最顶层父类的代码。

使用组合

针对“会飞”这样一个行为特性,我们可以定义一个 flyable 接口,只让会飞的鸟去实现这个接口。针对会游泳,定义一个 swimable 接口,会叫定义一个 tweetable 接口。

public interface flyable
{
  void fly();
}

public interface swimable
{
  void swim();
}

public interface tweetable
{
  void tweet();
}

//麻雀
public class sparrow : flyable, tweetable
{
  public void fly() => console.writeline("i am flying.");

  public void tweet() => console.writeline("!@#$%^&*……");
}

//企鹅
public class penguin : swimable, tweetable
{
  public void swim() => console.writeline("i am swimming.");

  public void tweet() => console.writeline("!@#$%^&*……");
}

麻雀和企鹅都会叫,tweet 实现了两遍,这是坏味道。我们可以用组合来消除这个坏味道。

public interface flyable
{
  void fly();
}

public interface swimable
{
  void swim();
}

public interface tweetable
{
  void tweet();
}

public class flyability : flyable
{
  public void fly() => console.writeline("i am flying.");
}

public class swimability : swimable
{
  public void swim() => console.writeline("i am swimming.");
}

public class tweetability : tweetable
{
  public void tweet() => console.writeline("!@#$%^&*……");
}

//麻雀
public class sparrow : flyable, tweetable
{
  flyability flyability = new flyability();
  tweetability tweetability = new tweetability();

  public void fly() => flyability.fly();

  public void tweet() => tweetability.tweet();
}

//企鹅
public class penguin : swimable, tweetable
{
  swimability swimability = new swimability();
  tweetability tweetability = new tweetability();

  public void swim() => swimability.swim();

  public void tweet() => tweetability.tweet();
}

虽然现在主流的思想都是多用组合少用继承,但是从上面的例子可以看出,继承改写成组合意味着要做更细粒度的类的拆分,要定义更多的类和接口。类和接口的增多也就或多或少地增加代码的复杂程度和维护成本。所以,在实际的项目开发中,我们还是要根据具体的情况,来具体选择该用继承还是组合。

以上就是c# 如何用组合替代继承的详细内容,更多关于c# 组合替代继承的资料请关注其它相关文章!

相关标签: c# 组合 继承