本文内容整理自《Android之大话设计模式》一书
五大设计原则:
- 单一职责原则
- 开放封闭原则
- 里氏代换原则
- 迪米特原则
- 合成聚合复用原则
单一职责原则Single Responsibility Principle
就一个类而言,应该仅有一个引起它变化的原因。换句话说,一个类的功能要单一,只做与它相关的事情。
如果一个完成额外的不太相关的功能或者完成其它类的功能,这就会使得一个引起一个类变化的因素太多,如果类的一处需要修改,其它和它相关连的代码都会受到影响,这就直接导致一旦系统出现了问题就难以调试困境,同时这样也非常不利于维护。
现在软件开发的经典模式MVC模式,也非常好的体现了单一职责原则。
MVC(Model-View-Control)就是模型、视图、控制器三层架构模式,其中
M是指数据模型、V是指用户界面、C则是控制器。采用MVC模式使得数据和表现相分离,同一个数据层可以有不同的显示层。数据层和显示层的改变互不影响。这就非常有利于提高软件的可维护性和可复用性,同时也方便了软件的管理工作和提高软件开发效率。
优点:
代码整洁(可读性强)、便于修改、可维护性、可扩展性强
开放封闭原则Open-Closed Principle
开放封闭原则是所有面向对象原则的核心。
一个软件实体应当对扩展开放,则修改关闭。对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况;对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。
如何做到开放封闭原则呢?答案是:封装变化,依赖接口和抽象类,而不要依赖具体实现类。因为接口和抽象类是稳定的,他们是一种对客户端的一种承诺,是相对不变的。如果以后需要扩展或者开发新的功能,只需要实现或者继承接口或者抽象类即可覆盖或者扩展新的功能,这样做同时也不回影响新的功能。这就很好的做到了对扩展开放、对修改关闭。
一个系统总会有要变化的地方,“世界上没有一个不边的软件”、“需求总是在改变”。我们要做的不是消灭变化,而是把变化隔离开来,并对其进行封装。我们无法控制变化,但是我们可以预则哪里会发生变法。把要变化的地方抽象起来,这样以后再面临变化的时候我们就可以尽量的扩展,而无须改变以后的代码。
优点:
- 封装变化,可扩展性强
- “需求总是在变化。”
- “世界上没有一个软件是不变的。”
- “针对抽象编程,不要针对实现编程。”
里氏代换原则Liskov Substitution Principle
一个软件实体如果使用的是基类的话,那么也一定适用于其子类,而且它根本觉察不出 使用的是基类对象还是子类对象;反过来的代换这是不成立的,即:如果一个软件实体使用一个类的子类对象,那么它不能够适用于基类对象。
里氏代换原则由Liskov女士提出,后来软件工程大师Robert C. Martin
把里氏代换原则最终简化为一句话:“Subtypes must besubstitutable for their base types”。也就是,子类必须能够替换成它们的基类。
里氏代换原则讲的是基类和子类的关系,只有这种关系存在的时候里氏代换原则才能够成立。 里氏代换原则是实现开放封闭原则的具体规范。这是因为:实现开放封闭原则的关键是进行抽象,而继承关系又是抽象的一种具体实现,这样LSP就可以确保基类和子类关系的正确性,进而为实现开放封闭原则服务。
优点:
- 可扩展性
里氏代换原则是很多其它设计模式的基础。
迪米特原则Law of Demeter
迪米特法则(Law of Demeter,简写LoD)又叫做最少知识原则(LeastKnowledge Principle简写LKP),也就是说,一个对象应当对其他对象尽可能少的了解,不和陌生人说话。
狭义的迪米特法则是指:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一类的某一个方法的话,可以通过第三者转发这个调用。
广义的迪米特法则是指:一个模块设计的好坏的一个重要标志就是该模块在多大程度上讲自己的内部数据与实现的有关细节隐藏起来。
在运用迪米特法则到系统的设计中时,要注意以下几点:
第一:在类的划分上,应当创建弱耦合的类,类与类之间的耦合越弱,就越有利于实现可复用的目标。
第二:在类的结构设计上,每个类都应该降低成员的访问权限。
第三:在类的设计上,只要有可能,一个类应当设计成不变的类。
第四:在对其他类的应用上,一个对象对其他类的对象的应用应该降到最低。
第五:尽量限制局部变量的有效范围。
但是过度使用迪米特法则,也会造成系统的不同模块之间的通信效率降低,使系统的不同模块之间不容易协调等缺点。如果类之间需要通信就通过第三方转发的方式,这就直接导致了系统中存在大量的中介类,这些类存在的唯一原因是为了传递类与类之间的相互调用关系,这就毫无疑问的增加了系统的复杂度。解决这个问题的方式是:使用依赖倒转原则(通俗的讲就是要针对接口编程,不要针对具体编程),这要就可以是调用方和被调用方之间有了一个抽象层,被调用方在遵循抽象层的前提下就可以自由的变化,此时抽象层成了调用方的朋友。
合成聚合复用原则(Composite Aggregate Reuse Principle,简称CARP)
合成聚合复用原则是指在一个新的对象中使用原来已经存在的一些对象,是这些原来已经存在的对象称为新对象的一部分,新的对象通过向这些原来已经具有的对象委派相应的动作或者命令达到复用已有功能的目的。
合成复用原则跟简洁的表述是:要尽量使用合成和聚合,尽量不要使用继承。
为何“要尽量使用合成和聚合,尽量不要使用继承”呢?这是因为:第一,继承复用破坏包装,它把超类的实现细节直接暴露给了子类,这违背了信息隐藏的原则;第二:如果超类发生了改变,那么子类也要发生相应的改变,这就直接导致了类与类之间的高耦合,不利于类的扩展、复用、维护等,也带来了系统僵硬和脆弱的设计。而是用合成和聚合的时候新对象和已有对象的交互往往是通过接口或者抽象类进行的,就可以很好的避免上面的不足,而且这也可以让每一个新的类专注于实现自己的任务,符合单一职责原则。