设计模式总结
创建时间:2014/03/19 10:13
更新时间:2014/05/03 09:51
作 者:hacke2
来 源:http://blog.csdn.net/hacke2
本系列主要记录设计模式的意图是什么,它要解决一个什么问题,什么时候可以使用它;它是如何解决
的,掌握它的结构图,记住它的关键代码;能够想到至少两个它的应用实例,一个生活中的,一个软件中
的;这个模式的优缺点是什么,其有哪些使用场景,在使用时要注意什么。
来自 晔阳的博客 blog.csdn.net/hacke2
1.单例模式
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点
主要解决:生产一系列产品
什么时候使用:当你想控制实例数目,节省系统资源的时候
如何解决 :判断系统是否是否已经有这个单例,如果有则返回,如果没有,则创建
结构图:
关键代码:构造函数式私有的
应用实例:1.一个党只能有一个主席.2.Windows是多进程多线程的,在操作一个文件的时候,就不可避免
地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。3.一些
设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打
印同一个文件。
优点:1.在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页
面缓存) 2.避免对资源的多重占用(比如写文件操作)
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,儿不关心外面怎么样来
实例化
使用场景:1.要求生产唯一序列号 2.WEB中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存
起来 3.创建的一个对象需要消耗的资源过多,比如I/O与数据库的连接等
注意事项:getInstance()方法中需要使用同步锁synchronized (Singleton.class)防止多线程同时进入造成
instance被多次实例化
2.工厂模式
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到
子类进行
主要解决:主要解决接口选择的问题
什么时候使用:我们明确的计划不同条件下创建不同实例时
如何解决 :让其子类实现工厂接口,返回的也是一个抽象的产品
结构图:
关键代码:创建过程在其子类执行
应用实例:1.你需要一辆汽车,你可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这
个汽车里面的具体实现 2.Hibernate换数据库只需换方言和驱动就可以
优点:1.一个调用者想创建一个对象,只要知道其名称就可以了 2.扩展性高,如果想增加一个产品,只要扩
展一个工厂类就可以 3.屏蔽产品的具体实现,调用者只关心产品的接口
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,是的系统中类的个数成倍增加,在
一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
使用场景:1.日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到
什么地方。2.数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时3.设计一个
连接服务器的框架,需要三个协议,"POP3", "IMAP", "HTTP",可以把这三作为产品类,共同实现一个接
口
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需
要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过new就可以完成创建的对
象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
3.抽象工厂
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
主要解决:主要解决接口选择的问题
什么时候使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品
如何解决 :在一个产品族里面,定义多个产品
结构图:
关键代码:在一个工厂里聚合多个同类产品
应用实例:工作了,为了参加一些聚会,肯定有两套或多套衣服吧,比如说有商务装(成套,一系列具体
产品)、时尚装(成套,一系列具体产品),甚至对于一个家庭来说,可能有商务女装、商务男装、时尚
女装、时尚男装,这些也都是成套的,即一系列具体产品.咱们假设一种情况(现实中是不存在的,要不然,没
法进入共产主义了,但有利于说明抽象工厂模式),在你的家中,某一个衣柜(具体工厂)只能存放某一种这
样的衣服(成套,一系列具体产品),每次拿这种成套的衣服时也自然要从这个衣柜中取出了.用OO的思想
去理解,所有的衣柜(具体工厂)都是衣柜类的(抽象工厂)某一个,而每一件成套的衣服又包括具体的
上衣(某一具体产品),裤子(某一具体产品),这些具体的上衣其实也都是上衣(抽象产品),具体的
裤子也都是裤子(另一个抽象产品).
优点:1当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对
象。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的Creator里加代码,又要在具体的
里面加代码
使用场景:1.QQ换皮肤,一整套一起换 2.生成不同操作系统的程序
注意事项:产品族难扩展,产品等级易扩展
4.建造者模式
意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
主要解决:主要解决在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子
对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们
组合在一起的算法确相对稳定。
什么时候使用:一些基本部件不会变,而其组合经常变化的时候
如何解决 :将变与不变分离开
结构图:
关键代码:建造者:创建和提供实例,导演:管理建找出来的实例的依赖关系
应用实例:1.去肯德基,汉堡,可乐,薯条,炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓
的“套餐”2.JAVA中的StringBuilder
优点:1建造者独立,易扩展 2.便于控制细节风险
缺点:1.产品必须有共同点,范围有限制 2.如内部变化复杂,会有很多的建造类
使用场景:1.需要生成的对象具有复杂的内部结构 2.需要生成的对象内部属性本身相互依赖
注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序
5.原型模式
意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
主要解决:在运行期建立和删除原型
什么时候使用:1.当一个系统应该独立于它的产品创建,构成和表示时;2.当要实例化的类是在运行时
刻指定时,例如,通过动态装载;3.为了避免创建一个与产品类层次平行的工厂类层次时;4.当一个类
的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手
工实例化该类更方便一些
如何解决 :利用已有的一个原型对象,快速的生成和原型对象一样的实例
结构图:
关键代码:1.实现克隆操作,在JAVA继承
Cloneable,重写clone(),在.NET中可以使用Object类的MemberwiseClone()方法来实现对象的浅表拷贝
或通过序列化的方式来实现深拷贝。2.Prototype模式同样用于隔离类对象的使用者和具体类型(易变
类)之间的耦合关系,它同样要求这些“易变类”拥有稳定的接口。
应用实例:1.细胞分裂 2.JAVA中的Object clone()方法
优点:1 性能提高 2,逃避构造函数的约束
缺点:1.配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很
容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。2. 必须实现Cloneable
接口 3,逃避构造函数的约束
使用场景:1.资源优化场景 2.类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。 3.性能和
安全要求的场景 4.通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。 5.
一个对象多个修改者的场景 6.一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值
时,可以考虑使用原型模式拷贝多个对象供调用者使用。7. 在实际项目中,原型模式很少单独出现,一般
是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已
经与Java融为浑然一体,大家可以随手拿来使用。
注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新
对象的.浅拷贝实现Cloneable,重写,深拷贝实通过实现Serializable读取二进制流,
6.适配器模式
意图:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起
工作的那些类可以一起工作。
主要解决:主要解决在软件系统中,常常要将一些“现存的对象”放到新的环境中,而新环境要求的接口
是现对象不能满足的
什么时候使用:1.系统需要使用现有的类,而此类的接口不符合系统的需要。2.想要建立一个可以重复使用
的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不
一定有与一致的接口。3.通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞
虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口),
如何解决 :继承或依赖(推荐)
结构图:
关键代码:适配器继承或依赖已有的对象,实现想要目标接口
应用实例:1.美国电器110V,中国220V,就要有一个适配器将110V转化为220V 2.JAVA JDK 1.1提供了
Enumeration接口,而在1.2中提供了Iterator接口,想要使用1.2 的JDK,则要将以前系统的Enumeration
接口转化为Iterator接口,这时就需要适配器模式 3.在LINUX上运行WINDOWS程序 4.java中的jdbc
优点:1可以让任何两个没有关联的类一起运行 2.提高了类的复用 3.增加了类的透明度 4.灵活性好
缺点:1.过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其
实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必
要,可以不使用适配器,而是直接对系统进行重构。2.由于JAVA至多继承一个类,所以至多只能适配一个
适配者类,而且目标类必须是抽象类
使用场景:有动机的修改一个正常运行的系统的接口,这是应该考虑使用适配器模式
注意事项:适配器不是在详细设计师添加的,而是解决正在服役的项目的问题
7.组合模式
意图:将对象组合成树形结构以表示“部分-整体”的层次结构。Composite模式使得用户对单个对象和组
合对象的使用具有一致性。
主要解决:它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元
素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
什么时候使用:1.你想表示对象的部分-整体层次结构(树形结构) 2.你希望用户忽略组合对象与单个对象
的不同,用户将统一地使用组合结构中的所有对象。
如何解决 :树枝和叶子实现统一接口,树枝内部组合该接口
结构图:
关键代码:树枝内部组合该接口,并且含有内部属性List,里面放Component
应用实例:1.算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作符也可以是操作树,操作
符和另一个操作数 2.在JAVA AWT和SWING中,对于Button和Checkbox是树叶,Container是树枝
优点:1. 高层模块调用简单 2.节点自由增加
缺点:在使用组合模式时,起叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则
使用场景:部分、整体场景,如树形菜单,文件、文件夹的管理
注意事项:定义时为具体类
8.装饰模式
意图:动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。
主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着
扩展功能的增多,子类会很膨胀
什么时候使用:在不想增加很多子类的情况下扩展类
如何解决 :将具体功能职责划分,同时继承装饰者模式
结构图:
关键代码:1.Component类充当抽象角色,不应该具体实现2 修饰类引用和继承Component类,具体扩
展类重写父类方法
应用实例:1.孙悟空有72变,当他变成“庙宇”后,他的根本还是一只猴子,但是他又有了庙宇的功能 2.
不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在
墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。
优点:1. 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可
以动态扩展一个实现类的功能
缺点:多层装饰比较复杂
使用场景:1. 扩展一个类的功能 2.动态增加功能,动态撤销
注意事项:可代替继承
9.代理模式
意图:为其他对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上.在面向对象系统中,有
些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),
直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层
什么时候使用:想在访问一个类时做一些控制