设计模式是对面向对象中反复出现的问题的解决方案,代表了软件开发中的最佳实践,是软件工程的基石。使用设计模式是为了代码重用,让代码更容易被人理解,保证代码的可靠性,这在软件设计和团队开发中是非常重要的。在Java源码,Spring,MyBatis等框架中,设计模式被大量的运用。
设计模式分类
常用的设计模式共23种,它们根据不同的特点分为三大类:
- 创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式;
- 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式;
- 行为型模式:模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。
七大原则
面向对象的设计模式通常遵循七大原则,有的文章说是六大原则,不包含合成/聚合复用原则,但是在《Effective Java》对此原则进行了重点说明,我更愿意把它加入其中。
开闭原则
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。在软件开发周期中,因为变化、升级或者维护对原有代码进行修改时,可能会给旧的代码引入错误。所以当软件需要变更时,尽量对原有代码实体进行扩展,而不是通过修改已有代码来实现变化。
开闭原则是面向对象设计中最基础的设计原则,它指导我们应尽量在不修改原有代码的情况下进行功能扩展。
单一职责原则
单一职责原则指的是不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。在开发中,因为功能的增多,通常会发生职责扩散,导致修改一个功能会导致其他的功能出现故障,而遵循单一职责原则可以避免此情况的发生。
单一职责原则可以降低代码的耦合性,利于代码的阅读和维护,降低了因为代码修改而对其他模块造成的影响。
里氏替换原则
里氏代换原则的定义是:所有引用基类的地方必须能透明地使用其子类的对象,也可以简单理解为任何基类可以出现的地方,子类一定可以出现。里氏替换原则是对开闭原则的具体补充,实现开闭原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
里氏替换原则可以提高代码的复用性,在基类能够使用的地方,它的子类必然也能够使用,这极大的提升了代码的扩展性。
依赖倒置原则
高层模块不应该依赖低层模块,高层模块和低层模块都应该依赖于抽象;抽象不应该依赖于实现,实现应该依赖于抽象。在创建关联关系时(比如方法传参,格式转换),尽量引用抽象层次更高的模块,而不要用具体的实现来做这些。
面向接口编程是对此原则的最佳实践,使用高层次的依赖可以提升系统的扩展性,可以在不改变原有代码的情况下扩展系统。
接口隔离原则
接口隔离原则的定义是客户端不应该依赖它不需要的接口,类间的依赖关系应该建立在最小的接口上。简单来说就是建立单一的接口,不要建立臃肿庞大的接口。也就是接口尽量细化,同时接口中的方法尽量少。当一个接口太大时,我没应该想办法把它分割成一些功能更简单,更细小的接口。
接口隔离原则细化了接口的职责,降低了接口的复杂度,提高了接口的内聚性。
迪米特法则
迪米特法则指的是一个对象应该对其他对象保持最少的了解。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大,迪米特法则不希望类之间建立过多直接的联系。
迪米特法则强调尽量少地建立类与类之间的关联,降低代码之间的耦合度,提升代码的复用性,避免因为代码的修改造成过多的影响。
合成/聚合复用原则
在软件开发中,尽量使用合成/聚合,而不是通过继承达到复用的目的。合成/聚合复用原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向内部持有的这些对象的委派达到复用已有功能的目的,而不是通过继承来获得已有的功能。
《Effective Java》有讲到,只有明确知道派生类和基类满IS A的时候才选用继承,当满足HAS A或者不能判断的情况下应该选用合成/聚合。因为在使用基础时,基类是完全暴露给派生类的,此时会有一个明显的问题:派生类继承自基类,如果基类的行为发生改变,将会影响到所有派生类的实现。
总结
对这七大原则的遵守并不是是和否的问题,而是多和少的问题,也就是说,我们一般不会说有没有遵守,而是说遵守程度的多少。任何事都是过犹不及,设计模式的七大设计原则也是一样,制定这七大原则的目的并不是要我们刻板的遵守他们,而需要根据实际情况灵活运用。对他们的遵守程度只要在一个合理的范围内,就算是良好的设计。
参考: