设计模式浅谈——抽象和解耦
Posted luyi84895838
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式浅谈——抽象和解耦相关的知识,希望对你有一定的参考价值。
设计模式(Design Pattern)一般指的是面向对象的二十三种设计模式,模式的解释是在某些场景下,针对某类问题的某种通用的解决方案。所谓设计模式就是一套被反复使用、经过分类的、代码设计经验的总结。使用设计模式的目的无非是为了在构架系统时保持高可扩展,高内聚,低耦合的特性,清晰,灵活,稳定的框架。
为何要让系统保持如何使系统保持高可扩展,高内聚,低耦合的特性的呢?
首先高扩展意味着系统在开发过程中和开发完成后的升级中都能简单易行,可以比较容易的适应需求的变化改进系统。
内聚和耦合是模块独立程度的两个定性衡量标准,模块的独立使得软件开发,测试和维护都较为容易。模块化的设计使得系统功能可以分割,接口可以简化,分配给开发组中不同的人实现不同的功能;模块化的设计能使得修改设计和程序的工作量比较小,系统发生错误传播范围被限制,需要扩充时能插入模块。
什么耦合和内聚:
耦合是一个软件结构内模块之间的互连程度的度量,模块间的耦合程度强烈影响系统的可理解性,可测试性,可靠性和可维护性。耦合度从高到低:
内容耦合(content coupling):直接使用另一模块的内部数据/通过非正常入口进入模块内部。(一个模块可以通过非正常入口访问另一个模块内部,一个模块可以访问另一个模块的内部数据,两个模块一部分代码重叠,一个模块有多个入口)
公共耦合(common coupling):通过公共数据环境互相作用的那些模块间的耦合,耦合度随着模块的个数增加而增加。公共环境可以是全局变量,共享通讯区等。
外部耦合(external coupling):两个模块共用一个外加数据格式,通讯协议或者设备面。
控制耦合(control coupling):一个模块调用另外一个模块;传递的是控制变量,被调用模块通过该值执行另外一个模块的功能。(中等耦合,往往是多余的可以把模块适当分解之后用数据耦合代替)。
特征耦合(stamp coupling):如果被调用模块需要使用作为参数传递的数据结构中的所有元素,则是数据耦合。但是只使用传入的一部分数据时就是特征耦合,被调用模块可以使用的数据多余实际需要的数据,将导致对数据的访问失去控制。
数据耦合(data coupling):值传递,调用函数将实参复制一份传入到函数中,函数对参数的修改不会影响到实参。(低耦合,系统中至少得出现这种耦合)
内聚指的是一个模块内各个元素彼此结合的紧密程度,理想的内聚模块里面的元素都为了做一件事而组件在一起,高内聚往往意味着模块间的低耦合。
高内聚:
功能内聚:模块内所有的处理元素属于一个整体完成一个单一功能功能
顺序内聚:一个处理元素输出数据是下一个处理元素输入数据
中内聚:
通讯内聚:模块所有元素使用同一个输入数据和(或者)产生同一个输出数据
过程内聚:处理元素相关,需要按特定次序执行
低内聚:
时间内聚:一个模块包含的任务必须在同一个时间段执行(例如模块的初始化工作)
逻辑内聚:一个模块完成的任务在逻辑上相同或者相似(例如一个模块产生各种类型的全部输出)
偶然内聚:一个模块完成一组任务,这些任务有很松散的联系
设计中耦合要尽量使用数据耦合,少用控制耦合和特征耦合,限制公共耦合,完全不用内容耦合,内聚尽量使用高内聚。
设计模式是如何使系统保持这些特性的呢?
设计模式有六大原则,设计模式中的二十三种都围绕这七大原则:
1.单一原则(Single Responsibility Principle):就一个类而言,应该仅有一个引起它变化的原因。也就是一个类之中应该是一组相关性很高的函数,数据的封装。
2.开闭原则(Open Close Principle):软件的对象(类,模块,函数等)应该对扩展是开放的,但是对于修改是封闭的,遵循开闭原则重要的手段就是通过抽象。当软件需要变化时,我们应该尽量通过扩展的方式来实现变化,而不是通过修改已有代码。
3.里氏替换原则(Liskov Substitution Principle):核心原理就是“抽象”,建立抽象,通过抽象建立规范,具体的实现在运行时替换掉抽象,保证系统的灵活性,扩展性。开闭原则和里式替换原则往往是生死相依,不离不弃的,通过里式替换原则,达到对扩展的开放,对修改的关闭。
4.依赖倒置原则(Dependence Inversion Principle):一种特定的解耦形式,关键点:
1.高层模块(调用端)不应该依赖低层模块(具体实现类),两者都应该依赖其抽象。
2.抽象(接口或者抽象类)不该依赖细节(实现类)。
3.细节应该依赖抽象。
在java语言的表现是:模块之间的依赖通过抽象来发生,具体的实现类之间不发生直接的依赖关系,其依赖通过接口或者抽象类来发生。概括为一句话就是面向接口编程(面向抽象编程)。
5.接口隔离原则(Interface Segregation Principle):客户端不应该依赖它不需要的接口。原则是将庞大臃肿的接口拆分为更小的更加具体的接口,目的是解开耦合,更加容易重构,更改和重新部署。
6.迪米特原则(Law of Demeter):也称为最少知识原则(Least Knowledge Principle),一个对象应该是对其他对象有最少的了解。一个类对自己需要耦合或者调用的类知道最少,类的内部如何实现与调用者或依赖者没关系,调用者只需要知道他所需要的方法即可,其他的一概不管。
7、组合/聚合复用原则(Composition/Aggregation Reuse Principle(CARP) ):尽量使用组合和聚合少使用继承的关系来达到复用的原则.
概括为:抽象(DIP+LSP+OCP),单一职责(SRP),最小化(ISP),封装(LOD)。OCP+LSP与可扩展性相关,SRP与高内聚相关,LOD+DIP+ISP与低耦合相关。
这就解释清楚了为什么使用设计模式能达到可扩展,高内聚,低耦合。基于java,抽象就是接口和抽象类,单一职责+最小化+封装就是类的概念了。六大原则大部分都与抽象+解耦(五个)有关,剩下一个单一职责为了高内聚,高内聚往往也意味着低耦合(解耦就是通过一定方法把高耦合变为低耦合),可扩展的核心也是抽象。所以千言万语汇成一句话:抽象和解耦!
抽象和解耦有什么联系呢?我用里氏替换举个例
这是观察者模式的类图,西面这段代码是改编的ConcreteSubject中的 notifyObservers方法,其中list里面存储的是ConcreteObserver的实例:
@Override
public void notifyObservers() {
Observer observer = list.get(i); ----------------------解耦
observer.update(message);
}
为什么这一段是解耦?
Observer observer=new ConcreteObserver(); 利用里氏替换
ConcreteOberserver observer= new ConcreteObserver(); 没有使用里氏替换,没有合理利用抽象,Observer接口如同摆设
解耦在于:同样为了执行observer.update(message),第一段是数据耦合,第二段是内容耦合,数据耦合为最低的耦合,内容耦合为高耦合。
下面简单介绍一下二十三种设计模式:
总体来说设计模式分为三大类:
创建型模式:对象实例化的模式,创建型模式用于解耦对象的实例化过程。(想想Spring的控制反转啊!就是工厂模式的应用)
结构型模式:把类或对象结合在一起形成一个更大的结构。
行为型模式:类和对象如何交互,及划分责任和算法。
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
以上是关于设计模式浅谈——抽象和解耦的主要内容,如果未能解决你的问题,请参考以下文章