编程来源生活,则生活也是编程的体现,几大原则亦是提炼于我门的生活。为了使程序更加可维护、可复用、可扩展、灵活性好,设计模式从几大原则出发并将其实现,达到代码复用,增强可维护性的目的。
1.开-闭原则(Open-Closed Principle)
“软件实体(类、模块、函数等等)应该可以扩展,但是不可修改。”
OCP具有理想主义色彩,它是面向对象的终极目标,其他原则可看做是OCP的实现方法。生活中也常见这样的例子,为了应对变化我们往往做几手准备,不至于难以应对,比如说为了应对因突发状况而导致的迟到现象,我们可以将自己的上课或上班时间提前于规定时间,这样我们就防止了上班途中的突发状况导致的晚到情况。规定是死的,人是活的,我们的设计也不例外,能够充分应对需求的变动就是好的设计,这样的原则也就更能指导我们。
2.单一职责原则(Single Resonpsibility Principle)
“就一个类而言,应该仅有一个引起它变化的原因。”
SRP原则描述如果一个职责过多类应对变化发生,则其耦合在一起的多个职能将会大幅度更改。就像做事情一样,不应三心二意,专注于一件事情并能够很好的完成就很了不起了,将很多事情同时压向自己时,我们只剩下纠结了。古人尚且知道“术业有专攻”的道理,将其纳入原则之列又何尝不可。
因此编程时应多在类的分离上多思考,这样的代码才能易维护、易扩展、易复用、灵活多样。
3.里氏代换原则(Liskov Substitution Principle)
“子类型必须能够替换掉他们的父类型。”
LSP体现继承思想,子类在父类出现的地方能够取代并替换之,正因LSP的存在,继承复用才变为可能。
LSP的思想很难举出例子,难道说干裁缝一行的儿子一定能够去做他父亲的本行司机?
虽然在我们的生活中不能很好的体现这种“不想当厨子的裁缝不是好司机”的思想,但在编程中却是我们经常用到的,比如说声明父类型,而赋值时却能够使用子类型,这样的思想才能使我们的设计更容易维护与复用,所以我们要求做裁缝的儿子一定要继承父业做一个“好司机”。
4.依赖倒转原则(Dependence Inversion Principle)
“高层模块不应该依赖低层模块。两个都应该依赖抽象。”
“抽象不应该依赖细节。细节应该依赖抽象。”
通俗道来:“针对接口编程,而不要对实现编程”,由里氏代换原则我们可定义出接口以及抽象类,然后通过子类去具体实现,子类的灵活性也就增强,以此实现扩展。电脑比收音机容易修理就在于它的内部定义了统一的接口,熟悉了容易出现的问题之后,便很容易的就能够找到出现问题的部件。而收音机实现的内部耦合度过高,不懂得专业知识是无法着手修理的。
DIP以抽象编程出发,通过抽象去实现,程序中所有的依赖关系都是终止于抽象类或者借口,则可将DIP成为面向对象设计的标志。
5.迪米特法则(Law Of Demeter)
也叫“最少知识原则”,“如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用”
LOD强调类之间的松耦合,当我们需要完成功能,但不需要知道具体是谁完成的时候就体现了该法则。就像我们去超市买东西或者去餐厅吃饭时我们并不关心是哪位服务员为我们服务,也不需要知道他们具体的姓名、他们的具体交接班,只需知道这个超市有我想要买的东西,这个餐厅有我爱吃的食物就够了。这样也就实现了松耦合。
“类之间的耦合越弱,越有利于复用,一个处于弱耦合的类被修改,不会对有关系的类造成波及。”
6.合成/聚合复用原则(Composite/Aggregate Reuse Principle)
“尽量使用合成/聚合,尽量不要使用类继承。”
继承是我们经常需要使用到的,但过度的使用又会导致类的结构过于复杂,关系太多,难以维护,扩展性非常差。当我们遇到继承类中往往是在两个或更多的方向上变化是,可考虑通过对象组合的方式,把多个角色之间的继承关系改为组合关系,从而更好的应对各自的变化。
House的继承可通过地址(HouseAddress)实现,也可以根据内部房间(HouseRoom)实现,如果将这两个方向的继承搅在一起,那可就这是乱了,这时候就可以考虑通过CARP来解决问题。
下图为各个原则在设计模式中的体现:
当然每个模式体现的不仅仅是一种原则,它通常会将几种甚至全部的原则都有所表示,因此这里仅仅是从原则上出发,将能够明显表示出该原则的模式做了大概整理。
六大原则与面向对象的基本思想并不冲突,而是对面向对象思想的具体阐述。其中处处可见封装、继承、多态的体现,为我们能够更好的去学习、应用面向对象提供了有力指导。