设计模式六大原则分别是单一职责原则(SPR)、开放-封闭原则、里氏代换原则(LSP)、依赖倒转原则、迪米特原则(LoD)和合成/聚合复用原则(CARP)。
1.单一职责原则,就一个类而言,应该仅有一个引起它变化的原因。
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合导致的设计,当变化发生时,设计会遭受到意想不到的破坏。
软件设计真正要做的许多内容,就是如果发现职责并把那些职责相互分离。其实这个要去判断是否应该分离出类来,也不难,那就是如果能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责,就应该考虑类的职责分离。
整合当然是一种好的思想,但是当我们编程时,我们却是在类的职责分离上多思考,做到单一职责,这样我们的代码才是真正的易维护、易扩展、易复用、灵活多样。
2.开放-封闭原则,是说软件实体(类、模块、函数等等)应该可以扩展,但是不可修改。
这个原则其实有两个特点:一个是“对于扩展是开放的”;“对于更改是封闭的”。
在软件设计阶段,怎样的设计才能面对需求的改变却可以保持相对稳定,从而使得系统可以在第一个版本以后不断推出新的版本呢?开放-封闭原则给了我们答案。
无论模块是多么的“封闭”,都会存在一些无法对之封闭的变化。既然不可能完全封闭,设计人员必须对于他设计的模块应该对哪种变化封闭做出选择。他必须先猜测出最有可能发生改变的种类,然后构造抽象来隔离那些变化,等到变化时立即采取行动。
在我们最初编程写代码时,假设变化不会发生。当变化发生时,我们就创建抽象来隔离以后发生的同类变化。
面对需求,对程序的改动是通过增加新代码来进行的,而不是更改现有的代码。
下面来举一个课本上的例子:编写一个运算方法程序,满足基本的运算。
上面就是这个例子的概况代码,若有新的功能需要加入,在代码中加入相应的类就可以了。
3.里氏代换原则,子类型必须能够替换掉它们的父类型。
一个软件实体如果使用的是一个父类的话,那么一定使用于其子类,而且它察觉不出父类对象和子类对象的区别。也就是说,在软件里,把父类都替换成它的子类,程序的行为没有变化。
也正是因为有了这个原则,使得继承复用成为了可能,只有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而对子类也能够在父类的基础上增加新的行为。比如说,猫是继承动物类的,以动物的身份拥有吃、喝、跑、叫等行为,可当某一天,我们需要狗、牛、羊也拥有类似的行为,由于它们都是继承于动物,所以除了更改实例化的地方,其它地方则不需要。
4.依赖倒转原则,A:高层模块不应该依赖底层模块。两个模块都应该依赖抽象。
B:抽象不应该依赖细节,细节应该依赖抽象。
原话解释是抽象不应该依赖细节,细节应该依赖抽象。说白了,就是针对接口编程,不要对实现编程。
依赖倒转原则其实可以说是面向对象设计的标志,用哪种语言编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止与抽象类或者接口,那就是面相对象的设计,反之就是面向过程化的设计了。
5.迪米特法则,也叫最少知识原则,如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
前提是在类的结构设计上,每一个类都应当尽量降低成员的访问权限。
其根本思想是强调了类之间的松耦合,解耦---才是它们最为重要的应用,这也是所有原则的根本。类之间的耦合越低,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。
6.合成/聚合复用原则,尽量使用合成/聚合,尽量不要使用类的继承。
合成(也翻译成组合)和聚合都是关联的特殊种类。聚合表示一种弱的“拥有”关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;合成则是一种强的“拥有”关系,体现了严格的部分和整体 的关系,部分和整体的生命周期一样。比如说,大雁有两个翅膀,翅膀与大雁是部分和整体的关系,并且他们的生命周期是相同的,于是大雁和翅膀就是合成关系。而大雁是群居动物,所以每只大雁都是属于一个雁群,一个雁群可以有多只大雁,所以大雁和雁群是聚合关系。
合成/聚合原则的好处就是,优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上,这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。
PS: 学习小结:
在进行设计模式的学习的时候,总是感觉这是一种让我们更好的进行软件设计的模式,而正是这些模式的存在才使得我们的软件结构以更好的形式存在,而且可复用性和灵活性都很强。
这部分的知识,但从课本上来学习,效果不是很大,还是要不断地在实践中不断地去练习使用,才能更加灵活的使用这些模式,以编出更好的软件。