设计模式启示录 (一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qingyixiaoxia/article/details/78386625

设计模式启示录(一)

如需转载请注明出处:http://blog.csdn.net/qingyixiaoxia  微信号:qingyixiaoxia

一)设计模式的用意

1.   软件设计有两个层面:High Level的架构设计,Detail Level的实现设计:

架构设计:对软件 层次 -> 子系统 -> 组件的静态设计,以及它们运行时交互关系的动态设计。特殊业务领域,对分布式和大数据运算开源框架的运用也在架构设计的范畴。架构设计并非本文讨论的重点,仅概念性带过。

实现设计:在架构设计的基础上,对架构设计中的每个待编码实现的component予以实现上的设计。这项设计工作,可剖析为两个方面:其一,业务逻辑设计;其二,灵活性设计。业务逻辑面的设计focus于logic,基本目标是:精准严密,简洁顺畅。工程面的设计focus于flexible,基本目标是:高易修改易扩展,高可复用。工程面的灵活性设计,目的是为业务需求可能发生的变化做铺路,以便在变化来临时以用比较小的代价完成需求变更。灵活性,是软件设计原则和软件设计模式被提出的动因。接下来,我们重点看一下各种软件设计模式是怎样巧妙的“灵活”的。

 

2.   工程面设计的目的有二:易变更,易复用。

对于易变更之目的,设计的灵魂是将可能发生变化的部分抽取和隔离起来,以便在因素生变时,仅需做非常局部的且尽量不影响其他部分的改动。

对易复用之目的,设计的灵魂是将不同调用者各自有异的部分提取出来,并让调用者自行去实现这些各自不同的部分。

抓住了工程面设计的灵魂本质,思路就很开阔了。可变因素或者有异因素的内涵是多种多样的,隔离可变因素或者有异因素的手法也是多种多样的。设计模式仅仅是众多手法中的一种。我们用解耦来描述这种隔离:

Logic与data value解耦:变量

Logic与data type解耦:模板

Logic与logic解耦 之 面向过程:回调 callback

Logic与logic解耦 之 面向对象:interface method & 多态 polymorphism

Logic与映射关系解耦:各种table

... ...

设计模式,就是在面向对象领域,logic们之间解耦的各种方法。

 

3.   Callback VS polymorphism

从语法实现的角度,二者可谓是一回事,本质都是callback。

从设计含义的角度,二者大有不同。Callback面向过程编程而设计,此语法的设计本意是,在被调用模块的逻辑行进的过程中,回调到调用者模块的某个逻辑方法。polymorphism面向对象编程而设计,此语法的设计本意是,继承时,子类可以按需定制化实现某些方法过程。面向对象的polymorphism最重要的意义,是可以基于此机制来架设很多的概念性的抽象。这些概念性的抽象让面向对象的设计具有了某些哲学意味。

 

4.   设计模式的核心

不同的设计模式,从语法角度剖析,相似度是很高的。然而,不同的设计模式,最核心也是最有趣的地方,是所做的抽象,和架设出的概念。抽象,是技术层面的理解和解释。概念,是哲学层面的理解和解释。概念是技术方法的升华,是思想的东西,是可以推而广之到用于指导技术之外领域的。本文讨论暂时限于技术层面,后面对设计模式所有的讨论,都围绕着一个词:抽象 !

 

不少讲述设计模式的材料,均已小鸭子呱呱叫,小鸡吱吱叫之类所谓形象易懂的案例引入模式。笔者不以为然,形象移动的案例读起来有趣易懂,但是落实到自己的项目,未见得容易带入应用。笔者讲述的设计模式,所有案例均围绕物联网领域的开发需求场景展开,包括多设备,多协议,多平台。需求概要如下图:

 

后续对设计模式的所有讨论,都围绕上述需求场景展开。话说,follow大师的轨迹不是丢人的事情,笔者虔诚的follow着GOF几位大师所提出的设计模式们。接下来,笔者带各位进入大师们的设计模式世界。如有砖头,请毫不留情的向笔者拍来~

 

特别的,虽然面向对象的设计不提倡用module这个词汇,我们后面的讨论中依然使用这个单词,{ module }构成component。如此方便在对设计模式的讨论中,区分一个设计模式运用于“大的component”间,还是“小的module”间。

 

如需转载请注明出处:http://blog.csdn.net/qingyixiaoxia  微信号:qingyixiaoxia

 

二)设计模式的核心 -抽象

GOF设计模式提出了6条设计原则,和23个设计模式。笔者对GOF提出的原则和模式做了一个重塑性的提炼,如下图:

1.  设计之核心:抽象

23中设计模式涵盖了我们常用的设计需求。尚未与设计模式有过深交的新新程序员,虽然未见得有以模式为专业指导进行代码的设计,但其实很可能在不知不觉以“非正规”的方式应用着模式们。比如Observer模式,比如Singleton模式。所有的设计模式,围绕的中心是一个词:抽象(Abstract)。抽象的目的有二:其一,解耦,其二,复用。设计模式告诉我们的,恰恰将常用的抽象做了总结归纳,并且告诉我们怎样以更好的方式完成这些抽象,以达成解耦和复用之目标。

 

例如Adapter模式,是将对某类component的调用需求抽象为一组接口,从而达成对具体component依赖的解耦。例如Facade模式,是将某component提供的能力抽象为一组接口,从而达成对具体调用者的解耦。例如Strategy模式,是将module内部特定多变的算法做抽象,从而达成module对具体算法实现的解耦。再如Template模式,是将某个module块所应用的数据类型做抽象,使得module块适用于多种数据类型,从而达成逻辑对具体数据类型的解耦。抽象,是一个设计模式的灵魂,也是程序员们的灵魂~

 

2.  设计模式之分类:以抽象为中心的分类

GOF将模式按照“创建型模式”,“结构型模式”,“行为行模式”进行划分。这样的划分方式是按照模式对应的设计领域来划分的:创建型模式应用于对象的创建;结构型模式用于component间或者module间交互和衔接的设计;“行为型模式”用于module内部逻辑细节上的一些设计。而笔者的设计模式世界中,一切以抽象为中心,对设计模式的归类亦是按照不同类型的抽象进行。分离详细方式如下:

 

1)  Interface Abstract分类:囊括对接口抽象的模式,包含Adapter,Facade,Proxy三种经典的设计模式。

 

2)  Logic Combine分类:囊括对逻辑块间之整合方式抽象的模式,包含Bridge,Mediator两种经典的设计模式。

 

3)  Logic Extract分类:囊括对某多变逻辑块分离方法抽象的模式,包含Strategy,Command,Visitor,Iterator三种经典的设计模式。

 

4)  Event Notify分类:囊括了逻辑块间通知的模式,包含Observer,Responsibility Chain两种经典的设计模式。

 

5)  Logic Reuse分类:包含了Template这样一种对数据类型做抽象和解耦的设计模式。

 

6)  Logic Models分类:囊括了一些特定场景下应用的设计模式们,包含Composite,Decorator,State,Interpreter,Memonto几种经典的设计模式。

 

7)  Create Objects分类:囊括了对象创建的模式,包含Abstract Factory,Factory Method,Builder,Prototype,Singleton几种经典的设计模式。

 

如需转载请注明出处:http://blog.csdn.net/qingyixiaoxia  微信号:qingyixiaoxia

 

三)七类设计模式

接下来我们进一步讨论前章提及的七个设计模式分类,看一下每类设计模式分别是为了解决什么样的问题,以及能达成什么样的效果。在下一篇文章中,我们会详细的对具体的设计模式展开讨论。

1)  Interface Abstract分类:

A)  Before:

 

问题描述:

图示中的Component A直接调用Component B的Interface。以下两种情况:一,随着软件需求变化,如果Component B的能力无法满足Component A的需求了,那么Component A需要针对新使用的Component C重新编程;二,Component A需要Component B提供简洁易用的接口,但是如果Component B仅仅提供Component A所需的简单接口,又会降低Component B的通用度和接口调用的灵活性。

以上所述问题对应的典型编码场景,诸如我们代码中对Windows/Linux平台的Socket/Thread接口的滴啊用,或者对某个第三方库的调用。

 

B)  After:

 

设计效果:

Component A调用Component B的角度看,Component A对Component B的需求抽象为一组Interface API。这样在Component B需要被替换时,仅需对新使用的Component C基于Interface API做一层封装即可实现切换。

Component B被Component A调用的角度看,Component B呈献给Component A的接口被封装为一组简单适用的Interface API。同样的,Component B呈献给其它调用者的接口,也可以被封装为另一组简单适用的Interface API。由此避免了Component B的实现受制于各个调用者。

 

2)  Logic Combine分类:

A)  Before:

 

问题描述:

不同内容的逻辑Module被杂糅于一起,或者不同功能Module虽然被分离出来了,但是彼此间直接的互相调用,功能Module间严重耦合。

 

B)  After:

 

设计效果:

图示的设计中,用一个专门的Mediator来协调各个Module之间的调用关系。这样各个module是相对解耦的。如果某个Module内部实现发生变化,可以将这种变化孤立在该module内部,而避免与之有业务交互的Module随之调整。

 

3)  Logic Extract分类:

A)  Before:

 

问题描述:

其一,如果子功能模块有多个实现版本,最简单的处理方法,在Module内部直接实现此块子功能的多个版本,每个版本对应一组不同的接口,按需通过宏定义或者一系列if else来决定所调用的版本。此种方式的弊端:一,子功能深植于具体业务Module内部,通用度较差;二,如果有新功能版本加入又需要到处更改if else。

其二,模块的功能应保持单一性,避免杂糅N多内容的超级模块。一个复杂模块的的某些子功能应该提取出来,这样这些子功能可以被复用,而且提升了软件的可复用度。

B)  After:

 

设计效果:

其一,将某些子功能从大模块中提取和独立出来;

其二,将子功能提供的能力抽象为Abstract接口,Module仅依赖抽象的接口。这样的好处,一,只需指定Module所使用子功能版本的具体实现类,即可切换Module所用的子功能版本;二,大大提升了子功能各个版本的通用度。

4)  Notify分类:

A)  Before:

N/A

问题描述:

N/A

 

B)  After:

 

设计效果:

通知有两类经典逻辑模型:观察者,责任链。前者用于在某事件发生时,通知到所有事件的注册者。后者用于在某请求发生时,通知给相关责任链,责任链中会传递该请求直至找到一个合适的处理者。这两种模型相比大家都是熟悉而且常常在用的,只不过我们回头来看GOF设计模式对此的描述,可以完美化我们对此的使用方法。

 

5)  Logic Reuse分类:

A)  Before:

同样的算法需要针对不同的数据类型分别实现一遍。

问题描述:

容器,算法,这些都面临着多数据类型。如果没有模板方法,最糟糕的状况下,我们需要针对每个数据类型分别实现一遍容器,算法。Copy past,修修改改,这显然是程序员最不愿意做的事情。(实际也是常常做的事情)

 

B)  After:

 

设计效果:

设计实现对类型“通用”的模板容器,模板算法。调用者需要某个特定数据类型的容器或者算法时,可以动态的按需去具体化模板容器和模板算法。C++的template最先支持了这样的设计方法。Java现在也实现了和C++的template极为相似的模板机制。

 

6)  Logic Models分类:

A)  Before:

N/A

问题描述:

N/A

B)  After:

 

设计效果:

这几种特定逻辑场景所需的设计模式,我们放到后面的篇章去做具体的讨论。这里仅仅予以列举。

 

7)  Create Objects分类:

A)  Before:

 

问题描述:

面向对象设计强调依赖于抽象,而不去依赖具体实现,也就是应对基于抽象接口编程(依赖倒置)。但是无论怎样抽象,有一块内容是避无可避的要基于具体的实现类来编程,这就是对象的创建。如果对象的使用者,内部直接的去撰写对象的创建逻辑,那么对象一旦发生变化,使用者要随之发生调整。

 

B)  After:

设计效果:

将对象的创建或着隔离起来,或者抽象为Abstract接口。调用者仅依赖创建对象的“Objects Creation Impl”,或者依赖抽象的Creation方法。这样具体的对象发生变更时,仅需调整“Objects Creation Impl”,或者调整Creation方法具体实现即可。避免了在此种情况下去改动调用者。

 

以上,是对GOF设计模式核心和分类的概要讨论,在后面的篇章中,将会详细的介绍23中设计模式。

如需转载请注明出处:http://blog.csdn.net/qingyixiaoxia  微信号:qingyixiaoxia

猜你喜欢

转载自blog.csdn.net/qingyixiaoxia/article/details/78386625