张赐荣——一位视障程序员。
赐荣小站: www.prc.cx

張賜榮

张赐荣的技术博客

博客园 首页 新随笔 联系 订阅 管理

“组合优于继承”是一个面向对象编程的设计原则,它建议我们在需要复用代码的时候,尽量使用组合(has-a)的方式,而不是继承(is-a)的方式。组合是指一个类包含另一个类的对象作为自己的属性,而继承是指一个类直接从另一个类派生出来,拥有其所有的属性和方法。

为什么要使用组合而不是继承呢?主要有以下几个原因:

  • 组合可以提高类的封装性灵活性。通过组合,一个类可以动态地改变自己的行为,只需要改变包含的对象的引用即可。而继承则是静态的,一旦确定了父类和子类的关系,就无法在运行时改变。而且,通过组合,一个类可以复用多个类的代码,而继承则只能单根继承,不能同时从多个父类派生。
  • 组合可以避免类之间的耦合污染。通过继承,子类会暴露父类的内部实现细节,如果父类发生了变化,子类也必须跟着改变。而且,子类会无条件地继承父类所有的属性和方法,即使有些并不适用于子类。这样会造成子类的臃肿和混乱。通过组合,一个类只需要关心自己包含的对象提供了哪些接口,而不需要知道其内部实现。而且,一个类可以选择性地使用包含对象的属性和方法,不会被强制继承不需要的东西。

下面我给你举一个简单的例子来说明组合和继承的区别。假设我们要设计一个汽车(Car)类和一个发动机(Engine)类。如果我们使用继承的方式,那么我们可以让汽车类继承发动机类,这样汽车就拥有了发动机的属性和方法。但是这样做有几个问题:

  • 首先,这样违反了“is-a”的逻辑关系。汽车并不是一种发动机,它们之间没有本质上的层次结构。
  • 其次,这样导致了汽车和发动机之间的紧密耦合。如果发动机类发生了变化,比如增加了一个新的属性或方法,那么汽车类也必须跟着改变。这样会增加代码的复杂度和维护成本。
  • 再次,这样限制了汽车和发动机之间的灵活性。如果我们想让汽车换一个不同类型或品牌的发动机,那么我们就必须重新定义一个新的汽车子类来继承那个发动机类。这样会导致代码冗余和浪费。

使用C#语言来表示就是这样:

// 使用继承的方式
public class Engine
{
    public string Type { get; set; }
    public int HorsePower { get; set; }

    public void Start()
    {
        // Start the engine
    }

    public void Stop()
    {
        // Stop the engine
    }
}

public class Car : Engine // Car is not an engine!
{
    public string Model { get; set; }
    public string Color { get; set; }

    public void Drive()
    {
        // Drive the car
    }
}

// 如果想换一个不同的发动机,就要定义一个新的子类
public class CarWithElectricEngine : ElectricEngine // Car is not an electric engine!
{
    public string Model { get; set; }
    public string Color { get; set; }

    public void Drive()
    {
        // Drive the car
    }
}

如果我们使用组合的方式,那么我们可以让汽车类包含一个发动机类的对象作为自己的属性,这样汽车就可以使用发动机的属性和方法。这样做有几个好处:

  • 首先,这样符合了“has-a”的逻辑关系。汽车有一个发动机,它们之间是一种组合关系。
  • 其次,这样降低了汽车和发动机之间的耦合。如果发动机类发生了变化,那么汽车类不需要跟着改变。只要发动机类提供了相同的接口,汽车类就可以正常工作。
  • 再次,这样提高了汽车和发动机之间的灵活性。如果我们想让汽车换一个不同类型或品牌的发动机,那么我们只需要改变汽车类中包含的发动机对象的引用即可。这样可以实现运行时的动态变化。

使用C#语言来表示就是这样:

// 使用组合的方式
public class Engine
{
    public string Type { get; set; }
    public int HorsePower { get; set; }

    public void Start()
    {
        // Start the engine
    }

    public void Stop()
    {
        // Stop the engine
    }
}

public class Car
{
    public string Model { get; set; }
    public string Color { get; set; }
    public Engine Engine { get; set; } // Car has an engine

    public void Drive()
    {
        // Drive the car
        Engine.Start(); // Use the engine's method
    }
}

// 如果想换一个不同的发动机,只要改变引用即可
public class Example
{
    public static void Main()
    {
        Car car = new Car();
        car.Model = "Toyota";
        car.Color = "Red";
        car.Engine = new Engine(); // Use a normal engine
        car.Engine.Type = "Gasoline";
        car.Engine.HorsePower = 200;

        car.Drive(); // Drive the car with a normal engine

        car.Engine = new ElectricEngine(); // Use an electric engine
        car.Engine.Type = "Electric";
        car.Engine.HorsePower = 150;

        car.Drive(); // Drive the car with an electric engine
    }
}
posted on 2023-06-11 15:15  张赐荣  阅读(126)  评论(0编辑  收藏  举报

感谢访问张赐荣的技术分享博客!
博客地址:https://cnblogs.com/netlog/
知乎主页:https://www.zhihu.com/people/tzujung-chang
个人网站:https://prc.cx/