设计模式总结
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
设计模式(Design Patterns)笔记
如果你有一定的面向对象编程经验,你会发现其中某些设计模式你已经无意识的使用过了;如果你是一个新手,那么从开始就培养自己良好的编程习惯(让你的的程序使用通用的模式,便于他人理解;让你自己减少重复性的编程工作),这无疑是成为一个优秀程序员的必备条件.
整个设计模式贯穿一个原理:面对接口编程,而不是面对实现.目标原则是:降低耦合,增强灵活性.
一些基本的设计模式
Abstract Factory:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
(使用得非常频繁。
)
Adapter:将一个类的接口转换成客户希望的另外一个接口。
A d a p t e r模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
Bridge:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
Builder:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
Chain of Responsibility:为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。
将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。
Command:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。
Composite:将对象组合成树形结构以表示“部分-整体”的层次结构。
它使得客户对单个对象和复合对象的使用具有一致性。
Decorator:动态地给一个对象添加一些额外的职责。
就扩展功能而言, 它比生成子类方式更为灵活。
Facade:为子系统中的一组接口提供一个一致的界面, F a c a d e模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
Factory Method:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。
Factory Method 使一个类的实例化延迟到其子类。
Flyweight:运用共享技术有效地支持大量细粒度的对象。
Interpreter:给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子。
Iterator:提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。
Mediator:用一个中介对象来封装一系列的对象交互。
中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
Memento:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
这样以后就可将该对象恢复到保存的状态。
Observer:定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
Prototype:用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。
Proxy:为其他对象提供一个代理以控制对这个对象的访问。
Singleton:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
State:允许一个对象在其内部状态改变时改变它的行为。
对象看起来似乎修改了它所属的类。
Strategy:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
本模式使得算法的变化可独立于使用它的客户。
Template Method:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
Template Method 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
Visitor:表示一个作用于某对象结构中的各元素的操作。
它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
前言:呵呵,拿了第一页出来,还没反应过来,就被兄弟们惨骂一顿,前面实在象目录,^_^, 不过大家别急嘛,好歹给我点休息时间。
不多说,下面开始记笔记!^_^
设计模式(Design Patterns)笔记之一:Abstract Factory
定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
提供创建对象的接。
工厂模式是我们最常用的模式,是用来创建对象的,它就相当于创建对象的new。
例如我们有一个类Example,我们要创建Example的对象:
Example example=new Example();
或
Example example=new Example(参数);
如果创建Example时做的事情不是如赋值这样简单的事,可能是很长一段代码,如果也写入构造
函数中,那明显的就违背了面向对象的原则.
我们需要将创建实例的责任与使用实例的责任分开, 使得
Example example=new Example(参数);
就是简单的责任:使用Example这个实例;而创建Example的任务就交给了Factory工厂模式.
按照传统方法,我们如果需要继承Example类,生成MyExample,就需要定义Example为Interface,然后,不断继承这个Interface,生成许多子类,造成以后代码维护是如此的困难。
如果,我们开始就采用了工厂模式,也许就没有这么多的烦恼了。
我们生成一个Factory,它专门负责生成Example的实例。
public class Factory {
public static Example getIntance() {
......
if(flag == 1)
return new MyExample();
if(flag == 2)
return new YourExample();
}
}
然后,那么在你的程序中,如果要实例化MyExample时.就使用
Example example=Factory.getIntance();//还有其它标志,不具体写了。
具体使用:
工厂模式有以下三种:
1. 简单工厂(Simple Factory)
2. 工厂方法(Factory Method);
3. 抽象工厂(Abstract Factory)。
我们先看一段代码
public abstract class Factory{
public abstract MyExample creator();
public abstract YourExample creator();
}
public class FirstFactory extends Factory{
public MyExample creator(){
......
}
public YourExample creator(){
......
}
}
public class SecondFactory extends Factory{
public MyExample creator(){
......
}
public YourExample creator(){
......
}
}
这里,我们需要创建多个对象,复杂性提高,于是需要采用Abstract Factory来封装共同属性和方法,而将不同的属性和方法通过子类来实现。
简而言之,工厂模式的目的就是用来实现实例分发。
工厂模式中几种类型的一些差别:
简单工厂模式是根据参数来决定产生实例;
简单工厂模式, 核心是一个实类;
工厂方法模式的核心是一个抽象工厂类;
工厂方法模式可以允许很多实的工厂类从抽象工厂类继承下来, 从而可以在实际上成为多个简
单工厂模式的综合;
工厂方法一般只创建一个实例,而Abstract Factory创建多个实例。
前言:呵呵,从这起开始,我们换种记录方法怎么样?学学人家的,寓教于乐。
第一次尝试,见笑了。
^_^
定义:Adapter:将一个类的接口转换成客户希望的另外一个接口。
Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
角色设定:
Helen,初次接触设计模式的美女程序员(没有BF)。
AndyTao,高手,战斗力达到N级,对设计模式有相当程度的了解。
问题的提出:
Helen埋首在公司自己的小空间里写着代码,(旁白:她虽然是个美女,但是写代码的水平还是不错的。
)有点问题正在埋头苦想,这时,AndyTao从旁边冒出来,“嗨,美女,到点了,一起吃饭吧?”“不行,我正烦着呢!”Helen皱着眉头说。
“哇,谁,谁欺负咱们Helen???”“切,谁敢欺负我啊。
”“那怎么了?”AndyTao一副义愤填膺的样子(这小子,司马昭之心)。
“没有啦,我这里程序不好处理。
”“噢,我看看!”AndyTao不知什么时候已经在Helen的身后了。
屏幕上一段代码:
public class DrawCircle {
public void DrawCircle() {
……
}
public void insert(String msg){
System.out.println("DrawCircle insert():"+msg);
}
……
}
public class DrawAngle {
public void DrawAngle() {
……
}
public void insert(String msg){
System.out.println("DrawAngle insert():"+msg);
}
……
}
“我现在要在DrawCircle中调用DrawAngle的方法,可是类的已经分发出去了,不能修改,我该怎么办?”
看着美女皱眉头的样子,AndyTao一阵心痛,“这样啊,我给你提示,再写个Adapter类吧。
”“什么是Adapter?”
“呵呵,在实际编码过程中,由于某种需要,我们经常要将两个毫无关系的类交互。
于是我们需要进行某种修正,修改各个类的接口,这通常不符合编码要求,而且,如果没有源代码,我们又该怎么办?于是,我们就要用到Adapter了,也就是适配器的意思。
象你上面的程序,我们不能修改类接口,于是,我们再写一个Adapter类。
”
public class DrawAdapter extends DrawCircle {
private DrawAngle drawangle;
public DrawAdapter(DrawAngle angle) {
this.drawangle=angle;
}
public void insert(String msg) {
drawangle.insert(str);
}
……
}
“在上面代码中,DrawAngle属于Adaptee,是被适配者。
DrawAdapter是Adapter,将Adaptee(被适配者DrawAngle)和Target(目标DrawCircle)进行适配。
实际上这是将组合方法(composition)
和继承(inheritance)方法综合运用的一个例子。
这样,你就无需对原来的类修改,甚至无需要知道其内部结构和源代码。
”
“上面我们说的是对象适配器(Object Adapter),它允许client和Adaptee完全无关,只有适配器知道它们两者的存在。
另外,还有一种是类适配器(Class Adapter),它使用多重继承来实现。
象对象适配器(Object Adapter)中,就是上个例子,DrawAdapter继承了DrawCircle,但是,DrawAdapter同样可以继承DrawAngle。
由于Java不支持多重继承,这就是说,我们的某个类必须定义为接口(interface)。
我们可以把DrawAngle定义为接口IDrawAngle,这样代码如下。
”
public interface IDrawAngle {
public void insert(String msg);
……
}
public class DrawAngle implements IDrawAngle {
public void DrawAngle() {
……
}
public void insert(String msg){
System.out.println("DrawAngle insert():"+msg);
}
……
}
public class DrawAdapter extends DrawCircle implements IDrawAngle {
private DrawAngle drawangle;
public DrawAdapter(DrawAngle angle) {
this.drawangle=angle;
}
public void insert(String msg) {
drawangle.insert(str);
}
……
}
“看到了么,这样我们就实现了类适配器(Adapter)模式。
不过,类适配器(Adapter)有一个问题,如果我们的Target和Adaptee有一个相同名字的方法,Adapter不能保证这两个方法具有相同的含义或者行为,这是非常危险的。
所以要注意这种情况!”
“哦,我知道了,Adapter模式的用处就在于使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
从而解决我现在的问题。
”
“美女不笨嘛。
这顿你请,以后我请,嘿嘿……”
“想追我?这顿我请,以后嘛,呵呵,再说吧。
谢谢,走吧。
”“OK!”
欲知后事如何,请待下回。
^_^
前言:不好意思,前段时间由于各方面的原因,没顾上写了。
这就加油补上。
^_^
概念:
Bridge:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
--------------------------------------
烈日,当空;没有一丝风,真的让人感觉透不过气来。
想起去年夏天在没有空调的房子里写代码,^_^,真是对人性的一种考验。
AndyTao正想着,不觉笑了。
午休时间也快过了,继续写我的代码吧。
“Andy,过来帮我看看嘛!”一串银铃声传了过来。
“唉,美女相邀,怎能不动啊。
”AndyTao心里想着,没敢说出口。
“我说,你又怎么了?就你事多。
”
“上次技术讨论会上听你说过,如果一个抽象类或者接口有多个具体的实现类(concrete subclass)的时候,为了不至于由于使用单纯继承进行抽象类或接口的具体实现而导致代码混乱
和可重用性不强,你说应当采用Bridge设计模式,这是怎么一回事啊?你看我现在这个例子采用继承不是很好吗?”
“哦,我看看。
”
public interface Draw {
public void paint();
}
public class DrawCircle implements Draw {
public void paint(){
System.out.println("paint Circle");
……
}
……
}
public class DrawAngle implements Draw {
public void paint(){
System.out.println("paint Angle");
……
}
……
}
“你看看,我这里不是各干其事,做得挺好嘛。
”
“呵呵,听我细细讲来。
通常,当一个抽象类或接口有多个具体实现(concrete subclass),这些concrete之间关系可能有以下两种情况:第一种是,这多个具体实现之间恰好是并列关系,就像你的这段代码,有两个concrete class:画圆和画三角;这两个形状上的图形是并列的,没有相对概念上的重复,那么我们只要使用继承就可以了。
……”
“别卖关子了好不好!”“……”“好啦好啦,我请你喝可乐可以吧?”
嘿嘿,奸计得逞,AndyTao继续说道,“但是,我们要考虑到第二种情况,如果我们两个或多个具体实现之间有概念重复,那么需要我们把抽象共同部分和行为共同部分各自独立开来,原来是准备放在一个接口里,现在需要设计两个接口,分别放置抽象部分和行为部分。
”
“好抽象啊,我听不懂!”
“那好,我们来举个例子,嗯……,就拿可乐来说吧,我们喝的可乐有大杯和小杯之分,而又有加冰和不加冰之分,这样,如果我们采用单纯继承来实现这四个具体实现(大杯加冰,大杯不加冰,小杯加冰,小杯不加冰),那么很容易造成相互之间的概念重叠,而且代码混乱,不容易维护。
所以……”
“所以,怎么?继续继续。
”
“所以啊,我们就要采用Bridge模式来重新设计类的结构。
如果采用Bridge模式,我们就需要定义两个接口或抽象类,为的是把抽象部分和行为部分分隔开来。
”
“稍等稍等,喝口水先。
”“来来来,用我的吧。
”“那……,真不好意思了,嘿嘿……”
“我们就用可乐作例子吧。
将可乐定义为抽象类,有一部分共同的实现代码可以放到里面,加冰和不加冰属于行为,那么我们就把它定义成为行为接口。
”
“然后,我们可以实现下面的抽象类。
”
public abstract class Coke {
CokeImp cokeImp;
public void setCokeImp(CokeImp cokeImp) {
this.cokeImp = cokeImp;
}
public CokeImp getCokeImp() {
return this.cokeImp;
}
public abstract void distributeCoke();
}
public abstract class CokeImp {
public abstract void distributeCokeImp();
}
“现在,我们就有了两个抽象类(或接口)。
上面,Coke是抽象部分,CokeImp是定义的行为抽象类。
为了实现我们所说的四种类动态结合的功能,我们需要在具体实现类上下点功夫罗。
”
“这是大可乐:”
public class BigCoke extends Coke
{
public BigCoke() {}
public void distributeCoke()
{
System.out.print("BigCoke ");
CokeImp cokeImp = this.getCokeImp();
cokeImp.distributeCokeImp();
}
}
“这是小可乐:”
public class SmallCoke extends Coke
{
public SmallCoke() {}
public void distributeCoke()
{
System.out.print("SmallCoke ");
CokeImp cokeImp = this.getCokeImp();
cokeImp.distributeCokeImp();
}
}
“我要加冰:”
public class IceCokeImp extends CokeImp
{
IceCokeImp() {}
public void distributeCokeImp()
{
System.out.print("Ice added");
}
}
“不要冰了:”
public class NonIceCokeImp extends CokeImp {
NonIceCokeImp() {}
public void distributeCokeImp()
{
System.out.print("Havn't ice");
}
}
“这里需要注意了,由于我们的CokeImp和Coke是一对一的关系,所以要从我们的用例中找到这个*Imp是一个比较关键和困难的事情。
”
“好啦,现在,你想喝哪种可乐?”
“我要小杯可乐加冰!”“这个简单,给你……”
Coke coke = new SmallCoke();
coke.setCokeImp(new IceCokeImp());
coke.distributeCoke();
“我要大杯可乐不加冰!”“Oh,Here!”
Coke coke = new BigCoke();
coke.setCokeImp(new NonIceCokeImp());
coke.distributeCoke();
“差不多Bridge模式就讲完了,另外,Bridge被广泛的应用于GUI和其它图形图象应用程序种,包括Java AWT。
另外,Abstract Factory(抽象工厂模式)经常用来创建和设定一个Bridge,Bridge模式类似于Adapter(适配器模式)中的对象适配器模式。
上面的两个模式上次不是都对你讲过了嘛。
”
“哦,你一说我到想起来了,”Helen咬着手指,非常可爱的模样。
“哇,迷死人了!”AndyTao 只有偷偷想的份。
^_^
“不过,你还要注意了,你在设计Bridge类的时候,要注意对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译。
另外,Bridge将会有许多类要生成,这样一种
类层次结构说明你必须将一个对象分解成两个部分,抽象部分和实现部分。
总之一句话,将抽象部分与它的实现部分分离,使它们都可以独立地变化。
这就是Bridge模式的精髓所在了,还有……”
“还有什么,跟个唐僧似的。
”“还有啊,别忘了你的可乐!哈哈哈!!”
“你呀,什么都记不住,就这在行。
”
“哇,你这么了解我啊。
”“去死吧你……”“哈哈哈!!!”
前言:看着csdn渐渐让人失望,本来打算不写了。
可是老师们教我善始善终,呵呵,那就坚持一回吧。
概念:
Builder:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
---------------------------------------------------------------------------------------
“嘿,Andy,来帮帮忙,帮我装装机子吧。
”,Helen又在那喳喳呼呼了。
“好的,好的,喔……,新机诶!”,走过去一看,AndyTao眼一亮。
唉,看到人家的新配置,他就有一种据为己有的冲动。
说实在的,咱玩电脑的,没几个不这山望着那山高的。
“这么大个丫头,到现在还是不会装机器。
你怎么学的啊?”AndyTao嘴上这么说,心里想,“最好永远学不会!”
“好的,好的,谢谢你嘛!不过,如果能把你的脑子也装过来就好了。
”
“嘿,不是吧,要求过分。
这样吧,嗯,顺便给你说说设计模式吧!”
“这装机也是个设计模式?”
“听下去!”
“哦。
”,Helen噘嘴的样子真TMD可爱,AndyTao不觉想入非非了。
“我就给你说说设计模式里的Builder模式好了。
Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们。
用户不知道内部的具体构建细节。
”
“诶,这个和抽象工厂模式不是很相似么?”
“Builder模式是非常类似抽象工厂模式,细微的区别大概只有在反复使用中才能体会到。
待会我讲讲Builder和Abstract Factory模式之间的一些区别好了。
”
“它一般用在下面两种情况下:
1、当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
2、当构造过程必须允许被构造的对象有不同的表示时。
上面的说法太抽象。
简单点来说,它是为了将构建复杂对象的过程和它的部件解耦,从而达到将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
”
“不懂……具体的来说呢?”
“因为一个复杂的对象,有大量组成部分,有时候多得让人头晕,就拿PC机来说吧:PC机有很多部件:
主板,CPU,硬盘,显示器……还有各种小零件,配件等等;部件很多,如何将这些部件装配成一台PC?
这个装配过程也很复杂,需要很好的组装技术(还吓不着你?),那么我们为了更好的组织PC机这个大对象,就需要一种相应的方法来进行装配。
我们要说的Builder模式也就是为了将部件和组装过程分开而产生的。
”
“不明白……”
“唉,笨笨,拿你没办法……”
“这样吧,我们说说具体怎么使用先!”
“首先假设一个复杂对象是由多个部件组成的,Builder模式是把复杂对象的创建和部件的创建分别开来,分别用Builder类和Director类来表示。
”
“首先,需要一个接口,它定义如何创建复杂对象的各个部件:
public interface Builder {
//创建部件A 比如生产主板
void buildPartA();
//创建部件B 比如生产CPU
void buildPartB();
//创建部件C 比如生产硬盘
void buildPartC();
……
//返回最后组装成品结果 (返回最后装配好的PC)
//成品的组装过程不在这里进行,而是转移到下面的Director类中进行。
//从而实现了过程和部件的分离
Product getProduct();
}
用Director构建最后的复杂对象,而在上面Builder接口中封装的是如何创建一个个部件(复杂对象是由这
些部件组成的),也就是说Director的内容是如何将部件最后组装成成品:
public class Director {
private Builder builder;
public Director( Builder builder ) {
this.builder = builder;
}
// 将部件partA partB partC最后组成复杂对象
//这里是将主板、CPU和硬盘组装成PC的过程
public void construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
}
}
Builder的具体实现ConcreteBuilder:
通过具体完成接口Builder来构建或装配产品的部件; 定义并明确它所要创建的是什么具体东西;
提供一个可以重新获取产品的接口:
public class ConcreteBuilder implements Builder {
Part partA, partB, partC;
public void buildPartA() {
//这里是具体如何构建partA的代码
};
public void buildPartB() {
//这里是具体如何构建partB的代码
};
public void buildPartC() {
//这里是具体如何构建partB的代码
};
public Product getProduct() {
//返回最后组装成品结果
};
}
复杂对象:产品Product:
public interface Product { }
复杂对象的部件:
public interface Part { }
我们看看如何调用Builder模式:
ConcreteBuilder builder = new ConcreteBuilder();
Director director = new Director( builder );
director.construct();
Product product = builder.getProduct(); ”
“上面所讲的就是Builder模式的一般性使用方法了。
至于Builder和Abstract Factory模式的区别,Builder强调的是整体是由部分组成的这个概念,比如你想要得到PC这个复杂对象,那你一定要先准备好主板,CPU,硬盘等等这些构造PC所必须的简单对象,之后调用Builder
的get操作得到PC这个最终产品;
而Abstract Factory模式则并不强调这种关系,或者可以说是构造上的‘松耦合’。
”
“嗯,咱们来个形象点的比喻吧:Builder模式是用来制造产品的,你的角色相当于产品制造商,知道怎么用部件来制造产品,而部件来自部件制造商,你可以利用不同种类的部件,也可以用同种类的部件采用不同的制造方法。
而Abstract Factory是从用户角度来考虑的,主要是给用户提供产品系列,用户可以方便的使用这些产品系列,但是用户没有可能去改变产品,只能使用已有的产品系列。
例如:Builder模式适用于计算机发烧友,经常DIY的;而Abstract Factory模式适用于喜欢稳定的品牌机,像IBM,联想之类的。
”
“你的意思是不是,constructor依次调用Builder的buildPart方法,全部完成后再调用getProduct方法得到整个product?那为什么不在Builder中加一个construct方法,依次调用
自己的buildPart方法呢?这样还可以减少constructor和Builder之间的耦合。
或者说,这种调用是运行时确定的,所以无法写在Builder中间吗?”
“还是上面所说的,如果将构造过程放在Builder里来作,那就失去的构造的灵活性。
还拿PC 来说吧,如果你在Builder里构造一种PC,80G硬盘的;但是你又想构造另外一种PC,100G硬盘的;你绝对不愿意再修改你的Builder吧,所以Builder用来构造Part,而整体的结构就由另外的一个Director来得到。
简单点来说,就是由你自己去组合这些零件,最后调用get方法得到你要的product。
”
“在Java实际使用中,我们经常用到‘池’(Pool)的概念,当资源提供者无法提供足够的资源,并且这些资源需要被很多用户反复共享时,就需要使用池了。
‘池’实际是一段内存,当池中有一些复杂的资源的‘残余’(比如数据库的连接池,也许有时一个连接会中断),如果循环再利用这些‘残余’,将提高内存使用效率,提高池的性能。
重在组装!”
“哦也,我有点明白了……你真行啊!”
“哪里哪里!!!”
“你以为我夸你哪???我说你弄半天还没有帮我把机子装好,你真行啊!!!”
“我晕……倒!你个小妮子!”。