合成复用原则是指:尽量先使用组合或者聚合等关联关系
来实现,其次才考虑使用继承关系
来实现。通常类的复用分为继承复用和合成复用两种。
继承复用虽然有简单和易实现的优点,但它也存在以下缺点:
- 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为
白箱
复用。 - 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
- 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。
采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点:
- 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。
- 对象间的耦合度低。可以在类的成员位置声明抽象。
- 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。
1 案例分析(汽车分类管理)
汽车按动力源
划分可分为汽油汽车、电动汽车等;按颜色
划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这两种分类,其组合就很多。
using System;
namespace DesignPattern
{
internal static class Program
{
public static void Main(string[] args)
{
new RedPetrolCar().Move();
new RedElectricCar().Move();
new WhitePetrolCar().Move();
new WhiteElectricCar().Move();
}
}
/// <summary>
/// 抽象汽车类
/// </summary>
public abstract class Car
{
/// <summary>
/// 行驶功能
/// </summary>
public abstract void Move();
}
/// <summary>
/// 汽油车类
/// </summary>
public abstract class PetrolCar : Car
{
}
/// <summary>
/// 电动汽车类
/// </summary>
public abstract class ElectricCar : Car
{
}
/// <summary>
/// 红色汽油汽车类
/// </summary>
public class RedPetrolCar : PetrolCar
{
/// <summary>
/// <inheritdoc cref="Move"/>
/// </summary>
public override void Move() => Console.WriteLine(nameof(RedPetrolCar));
}
/// <summary>
/// 白色汽油汽车类
/// </summary>
public class WhitePetrolCar : PetrolCar
{
/// <summary>
/// <inheritdoc cref="Move"/>
/// </summary>
public override void Move() => Console.WriteLine(nameof(WhitePetrolCar));
}
/// <summary>
/// 红色电动汽车类
/// </summary>
public class RedElectricCar : ElectricCar
{
/// <summary>
/// <inheritdoc cref="Move"/>
/// </summary>
public override void Move() => Console.WriteLine(nameof(RedPetrolCar));
}
/// <summary>
/// 白色电动汽车类
/// </summary>
public class WhiteElectricCar : ElectricCar
{
/// <summary>
/// <inheritdoc cref="Move"/>
/// </summary>
public override void Move() => Console.WriteLine(nameof(WhitePetrolCar));
}
}
如果此时研发了一种新型能源(例如,氢能源)的汽车,该类型汽车又有红色和白色两个种类,这样就需要另外定义HydrogenCar:Car
,RedHydrogenCar:HydrogenCar
,WhiteHydrogenCar:HydrogenCar
,易造成类爆炸
。
2 案例改进
using System;
namespace DesignPattern
{
internal static class Program
{
public static void Main(string[] args)
{
new PetrolCar() {Color = new WhiteColor()}.Move();
new ElectricCar() {Color = new RedColor()}.Move();
}
}
/// <summary>
/// 颜色接口
/// </summary>
public interface IColor
{
string Name {get;}
}
/// <summary>
/// 红色
/// </summary>
public class RedColor : IColor
{
public string Name => "Red";
}
/// <summary>
/// 白色
/// </summary>
public class WhiteColor : IColor
{
/// <summary>
/// 颜色
/// </summary>
public string Name => "Red";
}
/// <summary>
/// 抽象汽车类
/// </summary>
public abstract class Car
{
/// <summary>
/// 聚合颜色属性
/// </summary>
public IColor Color {get;set;}
/// <summary>
/// 行驶功能
/// </summary>
public abstract void Move();
}
/// <summary>
/// 汽油车类
/// </summary>
public class PetrolCar : Car
{
/// <summary>
/// <inheritdoc cref="Move"/>
/// </summary>
public override void Move() => Console.WriteLine($"{this.Color.Name} {nameof(PetrolCar)} is " + nameof(Move));
}
/// <summary>
/// 电动车类
/// </summary>
public class ElectricCar : Car
{
/// <summary>
/// <inheritdoc cref="Move"/>
/// </summary>
public override void Move() => Console.WriteLine($"{this.Color.Name} {nameof(ElectricCar)} is " + nameof(Move));
}
}
通过聚合关系,减少了类数量。如果增加新型能源的汽车,只需要增加一个类HydrogenCar
。