近期学习设计模式,深感其精妙。略加总结,以备不时之需。文章乃备忘只用,故不添加代码。
设计模式以面向对象为基础,把面向对象的特点(封装、继承和多态)和优点(可维护、可扩展、可复用、灵活性好)发挥的淋漓尽致。使用设计模式可以写出高内聚、低耦合的高质量代码。使用设计模式主要是为了应对变化,使原代码改动尽可能的小,我们无法预知那部分代码以后是需要变化的,但我们可以根据经验和常识,在最有可能变化的地方使用设计模式。设计模式会大大增加代码量和开发难度,所以不能滥用。
1、简单工厂模式
用一个单独的类(工厂类)来创建实例,工厂类中包含必要的逻辑判断,运用多态的时候,具体要实例化哪个对象,由工厂类根据客户端的选择条件动态实例化相关的类,而不是由客户端(主函数)来做,因此,对于客户端来说,去除了与具体产品的依赖。
不足:只解决对象的创建问题,对于经常性的需求改变,每次维护或者扩展,都要改动这个工厂类,以致代码重新编译部署。
2、策略模式
面对算法的时常变动,策略模式定义了算法家族,即先定义一个抽象算法类,具体的算法继承此抽象算法。这样可以分别封装具体的算法(或行为),让它们之间可以替换。因此算法的变化,不会影响到使用算法的客户。
策略模式与简单工厂模式的结合:
简单工厂模式让客户端认识两个类,而策略与简单工厂结合的用法,客户端只需要认识一个类就可以了,使得具体的算法彻底地与客户端分离,耦合度更低。
3、单一职责模式
就一个类而言,应该只实现一个功能,只有一种职责,因此也仅有一个原因可以引起它的变化。
4、开放封闭原则
一个软件实体(类、模块、函数等),应该是可以扩展,但不能修改的,即:对于扩展是开放的,对于修改是封闭的。
5、依赖倒转原则、李氏代换原则
李氏代换原则:子类必须能够替换掉它们的父类。在一个软件里,如果使用的是一个父类,那么一定可以使用其子类,把父类都替换成它的子类,程序的行为没有变化。子类型的可替换性使得使用父类的模块在无需修改的情况下可以扩展。
依赖倒转原则:高层模块不应该依赖于低层模块,两者都应该依赖于抽象(抽象类或者接口);抽象不应该依赖于细节,细节应该依赖于抽象。
6、装饰模式
装饰模式是为已有的功能动态地添加更多功能的一种方式。它动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更加灵活,它有效地把类的核心职责和装饰功能区分开了,简化了原有的类,而且可以去除相关类中重复的装饰逻辑。
当系统需要新功能的时候,需要向旧的类中添加新的代码,这些新加的代码通常装饰了原有类的核心职责或主要行为,而如果这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要,那么在主类中加入新的字段、方法或逻辑,会增加主类的复杂度,这并非一个好的方法。
装饰模式提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择、按顺序地(装饰顺序很重要)使用装饰功能来包装对象了。
7、代理模式
代理模式在访问对象时引入了一定程度的间接性,使其他对象通过代理访问对象。应用场合:远程代理、虚拟代理、安全代理和智能指引。
8、工厂方法模式
工厂方法模式定义一个用于创建对象的接口,让子类决定实例化哪一个类,使一个类的实例化延迟到其子类。
在简单工厂模式中,工厂类与分支耦合,根据依赖倒转原则,我们把工厂类抽象出一个接口,这个接口只有一个方法,就是创建抽象产品的工厂方法,然后把分支变成具体的类,来实现这个接口。就成为了工厂方法模式。
优点:使用多态,克服了简单工厂违背开放-封闭原则的缺点,保持了封装对象创建过程的优点。缺点:工厂方法把判断逻辑又移到了客户端,没有避免修改客户端代码。解决方法:反射。
9、原型模式
原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。即从一个对象再创建另一个可定制的对象,而且不需要知道任何创建的细节。这样既可以隐藏对象创建的细节,不用重新初始化对象,动态获得对象运行时的状态,又不需要在创建对象时执行构造函数,可以大大提高性能。
在.NET的System命名空间中提供了ICloneable接口,这个接口有个一唯一的方法Clone(),所以只需实现这个接口就可以完成原型模型了。
浅复制和深复制:浅复制中被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。例如MemberwiseClone()方法,如果字段是值类型的,则对该字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象,即原始对象及其副本引用同一对象。深复制把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。深复制要深入到多少层,需要事先就考虑好,而且要当心出现循环引用的问题。
例如,数据集对象DataSet,有Clone()方法和Copy()方法,Clone()方法用来复制DataSet的结构,但是不复制DataSet的数据,实现了原型模式的浅复制。Copy()方法不但复制结构,也复制数据,实现了原型模式的深复制。
10、模板方法模式
模板方法模式:定义一个操作中的算法的骨架,将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
我们用继承的时候,就应该使这个继承有意义,把所有重复的代码都应该要上升到父类去,成为子类的模板,而不是让每个类都去重复。如果要完成在某一细节层次一致的一个过程或一系列步骤,但个别步骤在更详细的层次上的实现可能不同时,应该考虑用模板方法模式来处理。
当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现,可以通过模板方法模式把这些不变的行为搬移到更高层次的类中,去除子类中的重复代码,使代码能很好地复用。