装饰者模式(decorator pattern)
策略模式(strategy pattern)
组合模式(composite pattern)
使用了组合关系
模板模式(template pattern)
相信很多人都看到过这句话:
Program to an interface, not an implementation. (面向接口编程,而不是具体的实现),面向接口编程的好处是可以极大程度地减少具体实现之间的相互依赖,达到一种松耦合的效果。
但是对于下面这句话,理解不够深刻。
Favor object composition over class inheritance.(如果某个场景的代码复用既可以通过类继承实现, 也可以通过对象组合实现, 尽量选择对象组合的设计方式)
为什么会优先选择组合这种方式呢?
首先看一下两者的区别
继承:是一种is-a的关系,白盒复用,父类的实现对于子类来说是透明的,实现一个子类的时候,需要了解父类的实现细节,以此觉得是否重写父类方法
类之间的继承关系时在编译时刻静态地定义好的, 因此使用起来也非常直观, 毕竟继承是被编程语言本身所支持的功能。
类继承也使得修改要重用的代码变得相对容易, 因为可以仅仅重写要更改的父类方法。
1、 没有办法在运行时刻改变继承了父类的子类行为。
2、与第一个缺点相比往往更严重: 通过继承实现的代码复用,本质上把父类的内部实现细节暴露给了子类, 破坏了类的封装特性,将父类的实现细节暴露给了子类。子类的实现依赖父类的实现,两者高度耦合,一旦父类代码修改,就会影响所有子类的逻辑。
3、继承层次过深、继承关系过于复杂会影响到代码的可读性和可维护性。
组合:是一种has-a的关系,黑盒复用,被引用者的细节对于组合引用者不可见,只是调用其方法实现功能,在实践中,一般通过引用接口方式实现,以便在运行时刻被替换成另外一个实现了相同接口且类型相同对象。
对象的组合是在运行时刻通过对象之间获取引用关系定义的,所以对象组合要求不同的对象遵从对方所实现的接口来实现引用传递, 这样反过来会要求更加用心设计的接口,以此支持你在使用一个对象时, 可以把它和很多其他的对象组合在一起使用而不会出现问题。
对象的组合由于是通过接口实现的, 这样在复用的过程中是不会打破其封装的。 任意一个对象都可以在运行时刻被替换成另外一个实现了相同接口且类型相同对象, 更重要的是,由于一个对象的实现是针对接口而编写的, 具体实现之间的依赖会更少。
对象组合的方式可以帮助你保持每个类的内聚性,让每个类专注实现一个任务。 类的层次会保持的很小,不会增长到一种无法管理的恐怖数量。 (这也是为什么Java语言支持单继承的原因)
不具备之前所罗列的类继承的优点
尽管我们鼓励多用组合少用继承,但组合也并不是完美的,继承也并非一无是处。
继承改写成组合意味着要做更细粒度的类的拆分。
这也就意味着,我们要定义更多的类和接口。类和接口的增多也就或多或少地增加代码的复杂程度和维护成本。所以,在实际的项目开发中,我们还是要根据具体的情况,来具体选择该用继承还是组合。
如果类之间的继承结构稳定(不会轻易改变),继承层次比较浅(比如,最多有两层继承关系),继承关系不复杂,我们就可以大胆地使用继承。
反之,系统越不稳定,继承层次很深,继承关系复杂,我们就尽量使用组合来替代继承。
参考资料
根据这两篇文章内容组合而成,供学习