您的位置:首页 > 其它

设计模式分类

2012-11-27 09:54 281 查看
/article/10590608.html

1. 创建型设计模式

2. 结构型设计模式

3. 行为型设计模式

1. Factory模式

a) 结构:



注: 1)Product A和Product B具有相同的父类,但是具有不同的实现。

2)Creator可根据不同的输入变量决定所需实例化的类。

b) 优点:

i. 将方法与处理分离,处理可以独立于方法名而存在。

c) 使用环境:

i. 当一个类不能静态确定她所必须创建的对象的类的时候。(Client可以在运行时,通过Creator来获得所需的Product子类)

ii. 当一个类希望由她的子类来实现她所定义的行为的时候。(由Product A和Product B来定义Product的行为)

iii. 当你希望将创建类的信息局部化的时候。(可以把if-else的选择代码,放到Creator内)

d) 代码实例:

public interface Product {

public void method();

}

public class ProductA implements Product{

public void method(){

do some thing;

}

}

public class ProductB implements Product{

public void method(){

do another thing;

}

}

public class Creator{

public Product getProduct(){

if(some condition){

return new ProductA();

}else{

return new ProductB();

}

}

}

e) 小结:

i. 可根据输入的条件,在一堆相似的类当中选择其中的一个并进行实例化。

ii. 现在的趋势是一个Creator只负责实例化一个Product,一方面可以让Creator的代码变得短小,另一方面可避免if-else结构。

2. Abstract Factory模式

a) 结构:



注: 1)Factory和ProductA,ProductB形成一个层次。

2)FactoryA和FactoryB继承Factory,并负责实例化ProductA和ProductB的子类。

b) 优点:

i. 可以通过更换Concrete Factory而实现对整个Product的替换。

c) 使用环境:

i. 一个系统要独立于她的产品的创建,组合和表示。(例如,Java中的UI,Client的LookAndFeel可以独立于控件的创建和表示。)

ii. 一个系统要由多个产品系列中的一个来配置。(一个系统可以由Product A1和Product B1或者Product A2和Product B2来配置)

iii. 当你要强调一个系列相关的产品对象的设计以便进行联合使用时。

iv. 当你提供一个产品类库,而只想显示他们的接口而不是实现时(Client通过Factory只看到了Product A和Product B的接口)

d) 代码实例

public interface Factory{

public ProductA getProductA();

public ProductB getProductB();

}

public class FactoryA implements Factory{

public PorductA getProductA(){

return new ProductA1();

}

public ProductB getProductB(){

return new ProductB1();

}

}

public class FactoryB implements Factory{

public PorductA getProductA(){

return new ProductA2();

}

public ProductB getProductB(){

return new ProductB2();

}

}

public interface ProductA{

}

public class ProductA1 implements ProductA{}

public class ProductA2 implements ProductA{}

public interface ProductB{

}

public class ProductB1 implements ProductB{}

public class ProductB2 implements ProductB{}

e) 小结:
i. 与Factory类似,但返回的是一组类而不仅是一个类,从某种程度上说是Factory模式在更高层次上的抽象。
ii. AbstractFactory将产品对象的创建延迟到他的实现类当中(FactoryA和FactoryB)
iii. AbstractFactory模式帮助你控制一个应用创建的对象的类。因为一个工厂封装创建产品对象的责任和过程。她将客户与类的实现分离。客户通过他们的抽象接口操纵实例。类名也在具体工厂的实现中被分离,他们并不出现在客户代码中。
iv. 难以生产新种类的产品,因为AbstractFactory接口限制了可以被创建的产品集合,支持新种类的产品需要扩展工厂接口。
v. Swing中的UI部分,是Abstract Factory的一个应用。

3. Singleton模式
a) 结构:



注:全局只有一个Singleton实例。

b) 优点:
i. 可对唯一的实例进行受控访问。
ii. 缩小名空间。(是对全局变量的一种替代,避免了那些存储唯一实例的全局变量污染名空间)
iii. 允许可变数目的实例。(在Singleton类内可进行实例计数)

c) 使用环境:
i. 当类只能有一个实例而且客户可以从一个众所周知的访问点访问她时。
ii. 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。(只要修改Singleton中的方法即可)

d) 代码实例
i. 抛出异常:
public class Singleton{
static Boolean flag = false;
public Singleton() throws SingletonException{
if(!flag){
flag = true;
}else{
throw new SingletonException();
}
}
}
优点:可以子类化。
缺点:不能共享实例,客户端要捕捉异常。

ii. 静态类:
public class Singleton{
public static methodA(){}
public static methodB(){}
}
优点:不用处理异常。
缺点:如果要将这个类改为可支持多实例访问时,比较麻烦。

iii. 静态方法:
public class Singleton{
private static Singleton instance = null;
private Singleton(){…}
public static Singleton getSingleton(){
if(instance == null)
instance = new Singleton();
return instance;
}
}
优点:不用担心当需要多个实例时会抛出异常,可以很容易支持共享实例和实例化多个实例。
缺点:不能子类化。(因为构造函数是私有的)

e) 小结:
i. 保证整个系统有且只有一个实例,并且在系统的各个部分都能对该实例进行访问。
ii. Java当中的Math类是一个Singleton的应用。

4. Builder模式
a) 结构:



注: 1)与Factory很相似,把对象的实例化外托到Builder中。
2)根据Director所识别出的标记,Builder可以一步步的构造出一个复杂的对象。
3)与Factory相比,程序员对构造过程可以有更多的参与。

b) 优点:
i. 程序员能更大的参与其中,构造出更复杂的对象。(在ConcreteBuilder中定义复杂的实现逻辑)
ii. 将一个复杂对象的构建与他的表示分离,使得同样的构建过程产生不同的表示。(即Director和Builder可以分开改变)

c) 使用环境:
i. 当创建复杂对象的算法应该独立于该对象的组成部分以及他们的装配方式时。
ii. 当构造过程必须允许被构造的对象有不同的表示时。

d) 代码实例:
public class Director{
private Builder builder = new Builder();
public void build(){
if(condition a){
builder.buildPardA();
}else if(condition b){
builder.buildPardB();
}else{
builder.buildPardC();
}
}
}

public interface Builder{
public void buildPartA();
public void buildPartB();
public void buildPartC();
}

public class ConcreteBuilder implements Builder{
public void buildPartA(){…}
public void buildPartB(){…}
public void buildPartC(){…}
}

e) 小结:
i. 她首先假定一个对象需要由其他的多个对象组成,但这些对象怎么进行组合和布局就要根据输入(condition)了。
ii. Director通过产生事件,Builder通过对事件作出响应,从而一步步的定制产品的构造。这就是所谓的构建与表示分离,通过Director产生或指明构建的步骤,而通过Builder进行具体的表示。
iii. XML文件的解析是Builder模式的一个应用。Parser是Director,DefaultHandler是Builder,通过扩展DefaultHandler我们可以对XML文件的解析结果进行定制。
iv. XSLT也是一个Builder模式的应用。通过提供不同的XSL文件(相当于Builder),即可以从同一个XML文件得到不同的输出,而对XML文件进行分析的Parser就相当于Director。

5. Prototype模式

待续

1. Adapter模式

a) 结构:



类适配



对象适配
注: 1)将一个类的接口转换成客户所希望的另外一个接口,使得原来由于接口不兼容而不能一起工作的那些类可以一起工作。

2)Adapter属于一个中间类,负责协调Client与Target之间的调用转发。

3)由于Java只支持单继承,所以,在Java中从Adapter到Target的继承都不出现。

b) 优点:

i. 可以很容易的把原有的(Adaptee)操作,移植到新的应用当中。

c) 使用环境:

i. 你想使用一个已经存在的类,而她的接口不符合你的要求。

ii. 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类协同工作。

iii. 你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配他们的接口。对象适配器可以适配她的父接口。

d) 代码实例:

i. 类适配:

public class Target{

public void request(){…}

}

public class Adaptee{

public void requestB(){…}

}

public class Adapter extends Adaptee{

public void request(){

super.requestB();

}

}

ii. 对象适配:

public class Target{

public void request(){…}

}

public class Adaptee{

public void requestB(){…}

}

public class Adapter extends Target{
private Adaptee adaptee = new Adaptee();
public void request(){
adaptee.requestB();
}
}

e) 小结:

i. 用于将一个接口转化为另一个。
ii. 在Adapter模式中有三个类Target,Adapter,Adaptee。Adapter要继承Target是从结构上考虑,从而在调用Target的地方均可用Adapter。而Adapter要实现Adaptee是从功能上考虑,达到重用Adaptee中功能的作用。
iii. 如果AWT应用程序要使用Swing当中新的类,可以提供一组从Swing到AWT的适配器。

2. Bridge模式
a) 结构:



注: 1)将抽象部分(Abstraction)与他的实现部分(Implementor)分离,使他们都可以独立的变化。
2)对于Client来说,Implementor部分的实现细节得到隐藏。

b) 优点:
i. 降低了各部分之间的耦合程度,隐藏了实现细节。便于各部分的独立更新。

c) 使用环境:
i. 你不希望在抽象和她的实现部分之间有一个固定的绑定关系。例如这种情况可能是因为在程序运行时刻,实现部分可以被选择或者切换。
ii. 类的抽象以及他的实现部分都应该可以通过生成子类的方法加以扩充。这时,Bridge模式使你可以对不同的抽象接口和实现部分进行组合,并分别对他们进行扩充。
iii. 对一个抽象的实现部分的修改应对客户不产生影响。
iv. 你想对客户完全隐藏抽象的实现部分。
v. 有许多类需要生成,这样一个类层次结构说明你必须将一个对象分解成两个部分。
vi. 你想在多个对象间共享实现(可使用引用计数),但同时要求客户并不知道这一点。

d) 代码实例:
public class Abstraction{
private Implementor imp = new ImplementorA();
public void operation(){
imp.operation();
}
}

public interface Implementor{
public void operation();
}

public class ImplementorA implements Implementor{
public void operation(){…}
}

public class ImplementorB implements Implementor{
public void operation(){…}
}

e) 小结:
i. 用于将客户端和实现类进行分离,使得两部分都能进行单独的修改。
ii. 对Abstraction的修改可以增加Client可使用的功能。
iii. 对Implementor的修改可以增加Abstraction可使用的操作。
iv. 对Abstraction和Implementor的修改和定制都是独立的,且对Client透明。
v. 在Java的数据库访问当中,把功能实现分成两部分,一部分是供应用程序调用的接口,另一部分是负责功能实现的SPI。

3. Composite模式

a) 结构:



注: 1)将对象组合成树形结构,以表示“整体-部分”的层次结构。

2)使得对单个对象的操作和对组合对象的操作具有一致性。

b) 优点:

i. 对节点(Composite)和对叶子(Leaf)的处理一致,使得客户的操作变得简单。(对Leaf和Composite可以使用相同的方法处理)

ii. 可以很简单的添加新的类型,但也可能导致系统过于一般化,不能加入某些特定限制。(操作都在Component中定义,子类只能修改方法的实现,但不能增加特定的方法)

c) 使用环境:

i. 你想表示对象的“整体-部分”层次结构。

ii. 你希望用户忽略组合对象与单个对象的不同。

d) 代码实例:

public abstract class Component{

public abstract void operation();

public void add(Component child){…}

public void remove(Component child){…}

public Component getChild(int index){…}

}

public class Leaf extends Component{

public void operation(){…}

}

public void Composite extends Component{

public void operation(){…}

}

e) 小结:
i. 是对象的一个集合,其中的每一个对象要么是一个Composite,要么就是一个基础类。
ii. 在实现Composite模式时,要考虑以下几个问题:
1) 该模式没有在子节点当中记录父节点的信息,可以考虑添加。
2) 共享组件:即一个组件属于多个父组件,需要引入Flyweight模式。
3) 最大化Component接口:定义一个Leaf和Composite功能的并集。
4) 声明管理子部件操作:在根声明对子节点的增删操作,可增加透明性,但是会导致安全隐患,如在Leaf中执行节点的增删操作。在子节点中声明增删操作,保证了Leaf节点的安全,但丧失了透明性。
5) 节点删除:对于共享子节点,需要特别对待。
iii. Java中的JTree中用到的Node类,DOM中用到的Node类都是Composite模式的应用。

4. Decorator

a) 结构:



注: 1)动态地给一个对象增加额外的职责。

2)不是通过继承来实现。

3)只是对某一个类实例,而不是对整个类增加功能。

b) 优点:

i. 可以将多个Decorator进行嵌套使用。

ii. 不需要通过继承,而给类实例动态增加额外的功能。(但可能产生很多额外的小类)

c) 使用环境:

i. 在不影响其他对象的情况下,以动态,透明的方式给单个对象添加职责。(因为不是通过继承等方式扩展功能)

ii. 处理那些可以撤销的职责。(由于职责只是动态的加到对象实例上,所以,当不需要该功能时,只要不增加修饰即可)

iii. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长(例如,如有子功能A,B,C则不同的组合方式将有3+6+6种)。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。(类被定义成final)

d) 代码实例:

public class Component{

public void operation(){…}

}

public class ComponentA extends Component{

public void operation(){…}

}

public class Decorator extends Component{

private Component component;

public void operation(){

component.operation();

}

}

public class DecoratorB extends Decorator{
public void operation(){
super.operation();
addedOperation();
}
private void addedOperation(){…}
}

e) 小结:
i. 环绕在一个给定类的外围,为给定的类添加额外的功能,而对于不需要改变的行为则仍由给定类完成。
ii. Decorator改变对象的职责,Adapter改变对象的接口。
iii. Decorator改变对象的外观,Strategy改变对象的内核。
iv. Decorator可以看作退化了的,仅有一个组件的Composite。然而,Decorator在于给对象添加一些额外的功能,Composite在于对象聚集。
v. Java的IO包和Swing中的Border包是Decorator的一个具体应用。

5. Facade模式

a) 结构:



注: 1)为子系统中的一组接口提供一个一致的界面。Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

b) 优点:

i. 屏蔽掉底层子系统的复杂性,给用户一个比较简洁的调用界面。

ii. 使用户代码和底层实现分离,降低了耦合度。

iii. 减少了客户端与子系统之间的通信次数。

c) 使用环境:

i. 当为一个复杂子系统提供一个简单接口时。Facade模式提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过Facade层。

ii. 当客户程序与抽象类的实现部分之间存在着很大的依赖性,通过引入Facade,可以将子系统与客户端进行分离,降低耦合性,提高独立性和移植性。

iii. 当你需要构建一个层次结构的子系统时,使用Facade模式,可以定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让他们仅通过Facade进行通信,从而简化了他们之间的依赖关系。

d) 代码实例:

public class Facade{

private SubsystemA subA = new SubsystemA();

private SubsystemB subB = new SubsystemB();

public void operation(){

subA.doSomething();

subB.doAnotherthing();

}

}

public class SubsystemA{

public void doSomething(){}

}

public class SubsystemB{

public void doAnotherthing(){}

}

e) 小结:
i. 将一个复杂的对象结构进行组合,并形成一个新的,简化了的界面。
ii. 可以用抽象类实现Facade,而他的具体子类对应于不同的子系统实现,这可以进一步降低客户与子系统间的耦合度。(可进行整个子系统的替换)
iii. 在J2EE中,Facade模式用于代替客户端实现对多个Session Bean的访问。

6. Flyweight模式
a) 结构:



注: 1)运用共享技术有效的支持大量细粒度的对象。
2)被共享的对象有可能是一个Sigleton。
3)共有的属性保存在类内部,个别的属性放在类外部的数组或者Vector中。

b) 优点:
i. 减少了所需要的实例。

c) 使用环境:
i. 一个应用程序使用了大量对象。
ii. 完全由于使用大量的对象,造成很大的存储开销。(通过共享实例,减少存储开销)
iii. 对象的大多数状态都可变为外部状态。

d) 代码实例:
public class FlyweightFactory{
private Flyweight[] fly;
public Flyweight getFlyweight(int key){
if(fly[key] != null){
return fly[key];
}else{
fly[key] = new FlyweightA();
return fly[key];
}
}
}

public class Flyweight{
public void operation(){…}
}

public class FlyweightA extends Flyweight{
public void operation(){…}
}

e) 小结:
i. 通过将类的某些数据移到类的外部,并通过各种方法将这些数据传到类的内部来提供一种方法,以限制一些小的,相似的类实例的生成。(有点类似Prototype模式)
ii. Flyweight不能对他所运行的场景作出任何假设,类似于EJB中的Stateless Session Bean。
iii. Flyweight对象必须是可共享的,她所存储的状态必须是内部的,即她必须独立于Flyweight对象的场景。因此,一般由一个Flyweight Pool对Flyweight进行生命期管理。
iv. 可以用两种方法来节约存储:用共享减少内部状态的消耗,用计算时间换取对外部状态的存储。
v. 共享还意味着某种形式的引用计数和垃圾回收。

7. Proxy模式
a) 结构:



注: 1)为其他对象提供一种代理以控制这个对象的访问。
2)Proxy与该对象具有一致得接口,她介入对所代理对象的请求中,执行一些额外的操作。

b) 优点:
i. 缩短反应时间。(可以在Proxy内启动一个线程,使方法调用可以马上返回)
ii. 可进行一定的权限管理。(所有对RealSubject的访问,都需要先经过Proxy进行过滤)

c) 使用环境:
i. 远程代理:为一个对象在不同的地址空间提供局部代表。(类似于RMI中的Stub)
ii. 虚代理:根据需要创建一个开销很大的对象。(延迟大对象的初始化时刻,由虚代理暂时代表实际对象)
iii. 保护代理:控制对原始对象的访问,保护代理用于对象应该有不同的访问权限的时候。
iv. 智能代理:取代了简单的指针,她在访问对象时执行一些附加的操作。她的典型用途包括对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放她。
v. 当第一次引用一个持久对象时,将他装入内存。(延迟持久对象的装载时刻)
vi. 在访问一个实际对象前,检查是否已经锁定了她,以确保其他对象不能改变她。(由于对RealSubject的访问需要先经过Proxy,所以,可以在Proxy内以访问计数或者synchronized等方式加锁)
vii. Java在处理图片时,使用了该模式。(虚代理)

d) 代码实例:
public interface Subject{
public void request();
}
public class RealSubject implements Subject{
public void request(){}
}
public class Proxy implements Subject{
private void RealSubject real;
public void request(){
real.request();
}
}
e) 小结:
i. 为一个复杂的,要实例化非常昂贵的类提供一个简单的占位类。

6. 结构型模式小结
a) Adapter主要是为了解决两个已有接口间的不匹配,而不考虑这些接口的实现和变化。
b) Bridge则对抽象接口与她的实现部分进行分离。
i. Adapter的目的是为了避免代码重复,发生在类设计好以后
ii. Bridge的目的是为了降低耦合,发生在类设计以前。
c) Decorator旨在使你能够不需要子类化即可给对象添加额外的职责。
d) Composite旨在构造类,使多个相关的对象能够以统一的方式处理,她重点不在于修饰,而在于表示。
e) Proxy模式不能动态添加或分离性质,他也不是为递归组合而设计,其目的是当直接访问一个实体不方便或不符合需要时,为这个实体提供一个替代者。

1. Chain of Responsibility模式

a) 结构:



注: 1)使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理她为止。

b) 优点:

i. 降低耦合程度。每个对象仅需知道如何将请求往后转发。(每个对象只需保留一个后继的引用)

ii. 在职责分发方面提供了额外的灵活性,可以在运行期间对链和职责进行修改。(通过修改后继引用,动态调整链)

iii. 当没有一个对象能处理某个请求时,则最后一个对象丢弃该请求。(提供对请求的默认处理)

c) 使用环境:

i. 有多个对象可以处理一个请求时,哪个对象处理该请求,在运行时刻自动确定。

ii. 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。

iii. 可处理一个请求的对象集合应被动态指定。

d) 代码实例:

public abstract class Handler{

protected Handler successor;

public abstract void handleRequest();

}

public class HandlerA extends Handler{

public void handleRequest(){

if(can handle){

dosomething;

}else{

successor.handleRequest();

}

}

}

public class HandlerB extends Handler{

public void handleRequest(){

if(can handle){

doanotherthing;

}else{

successor.handleRequest();

}

}

}

e) 小结:

i. 要自己维护后继者链。

ii. 对于请求,可以用硬编码表示,或者把请求封装起来。

iii. 帮助系统就是一个Chain of Responsibility的一个应用。

2. Command模式

a) 结构:



注: 1)将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

b) 优点:

i. 把请求的处理封装成一个特定的类,而不是通过一串if-else语句进行判定和指派。(JDK1.2中的事件模型是通过if-else实现,从J2SDK开始,事件模型是采用Command模式的形式)

ii. 可记录请求序列,实现undo操作。(由于是在Invoker触发事件,所以,可以在Invoker处对所发生过的事件进行记录。或者通过提供一个全局的事件日志,每当Command执行其execute()方法时,都先在该日志中进行填写,实现请求队列的记录)

c) 使用环境:

i. 在不同的时刻指定,排列和执行请求。一个Commmand对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传送给另一个不同的进程,并在那儿实现该请求。

ii. 支持取消操作。(在Command内部,在execute方法的开始部分,进行状态的记录)

iii. 支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。(因为记录了请求队列,所以可以进行重做)

iv. 用构建在原语操作上的高层操作构造一个系统,这样一种结构在支持事务的信息系统中很常见。一个事务封装了对数据的一组变动,Commmand模式提供了对事务进行建模的方法。Commmand有一个公共的接口,使得你可以用一种方式调用所有的事务。同时,使用该模式也易于添加新事务以扩展系统。

d) 代码实例:

public class Invoker{

private Command[] commands;

public void onEvent(Event e){

if(e){

commands[i].execute();

}

}

}

public interface Command{

public void execute();

}

public class Receiver{

public void action(){}

}

public class CommandA implements Command{
private Receiver rec;
public void execute(){
rec.action();
}
}

e) 小结:
i. Command模式可能导致生成很多小型的类。(可采用内部类解决)
ii. Java事件模型是Command模式的一个应用,其中的ActionListener相当于上述中的Receiver。

3. Iterator模式

a) 结构:



注: 1)提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示。

b) 优点:

i. 在不需要暴露集合的结构的情况下,提供一种遍历集合的方法。(由于是由集合负责创建Iterator,所以,只有Iterator知道集合的内部结构,而这个集合的结构对用户是透明的)

c) 使用环境:

i. 访问一个集合对象的内容,而无需暴露她的内部表示。

ii. 支持对聚合对象的各种遍历。

iii. 为遍历不同的聚合结构提供一个统一的接口。(所有的操作都定义在Iterator中)

d) 代码实例:

public interface Aggregate{

public Iterator createIterator();

public int size();

public Node getNode(int index);

}

public interface Iterator{

public void first();

public void next();

public boolean isDone();

public Node current();

}

public class AggregateA implements Aggregate{
public Iterator createIterator(){
return new IteratorA(this);
}
}

public class IteratorA implements Iterator{
private Aggregate agg;
private int current;
private int size;
public void first(){
current = 0;
}
public void last(){
current = size - 1;
}
public void isDone(){
if(current == size)
return true;
else
return false;
}
public Node current(){
return agg.getNode(current);
}
}

e) 小结:

i. 谁控制该遍历。一般有两种方法。一是由客户程序负责,另一种是由遍历器负责。前一种方式比较灵活,但是代码重用的几率小,且耦合度大。后一种灵活性虽然不如前一种,但他可以方便的重用,优化,而且降低了客户程序与集合间的耦合度。
ii. 谁定义遍历算法。一般有两种选择。一是由集合自身定义遍历算法,然后集合把状态保存到遍历器中,另一种是由遍历器负责。前一种方法,便于保存集合的私有信息,不向遍历器暴露。后一种方法,比较灵活,而且相同的遍历算法可以在不同的集合上重用。
iii. 处理同步操作。当一个遍历器在遍历集合时,如果对集合进行了增删操作,可能会导致重复访问,或者少访问了某个集合的元素,所以,需要提供一种手段来保证不发生以上情况。
iv. 空遍历器。当对Composite对象进行递归遍历时。通过在叶子节点提供一个空遍历器(isDone方法总是返回true),即可完成对树形集合的遍历访问。
v. Java的Util包中的Iterator是对该模式的一个应用。

4. Mediator模式

a) 结构:



注: 1)用一个对象来封装一系列的对象交互。Mediator使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变他们之间的交互。

b) 优点:

i. 降低了对象之间的耦合,将对象的行为局部化在对象内部。(所有Colleague的通信都集中到Mediator当中)

ii. 通过修改Mediator的代码,可以很容易改变对象间的交互。

iii. 可以很容易的在系统中增加新的Colleague,而不需要改变已有的代码。(Colleague只与Mediator通信,他们之间互相不知道)

iv. Mediator解决了Command对象在执行代码时需要过多的了解其他对象的细节的问题。

c) 使用环境:

i. 一组对象以定义良好,但是复杂的方式进行通信,产生的相互依赖关系结构很乱且难以维护。(Mediator用于解耦对象间的通信)

ii. 一个对象引用其他很多对象,并且直接与这些对象通信,导致难以复用该对象。

iii. 想定制一个分布在多个类中的行为,而又不想生成太多的子类。

d) 代码实例:

public interface Mediator{}

public class MediatorA implements Mediator{

private Colleague a;

private Colleague b;

public Mediator(){

a = new ColleagueA(this);

b = new ColleagueB(this);

}

public void doSomething(Colleague c){

b.doAnotherthing();

}

}

public abstract class Colleague{

protected Mediator m;

public Colleague(Mediator m){

this.m = m;

}

}

public class ColleagueA extends Colleague{

public ColleagueA(Mediator m){

super(m);

}

public void doSomething(){

m.doSomething(this);

}

}

public class ColleagueB extends Colleague{

public ColleagueB(Mediator m){

super(m);

}

public void doAnotherthing(){



}

}

e) 小结:
i. Mediator模式可以减少子类生成。她把各Colleague中负责通信的部分集中到一起,改变这些行为只需生成Mediator的子类即可,这样各Colleague类可被重用。
ii. 她将各Colleague子类进行解耦。
iii. 她简化了对象协议。从原来可能的n-n改成1-n.
iv. 她对对象间的协作进行了抽象。
v. 她使控制集中化。
vi. Facade模式也是对系统进行抽象,从而提供一个方便的接口,但是Facade模式的通信是单向的,而Mediator是双向的。
vii. 自动机模型可以考虑采用Mediator模式来实现。

5. Observer模式
a) 结构:



注: 1)定义对象之间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于他的对象都得到通知,并被自动更新。

b) 优点:
i. 定义了一个抽象的对象间通信框架。

c) 使用环境:
i. 当一个抽象模型有两个方面,其中一个方面依赖于另一方面,将这两者封装在独立的对象中以使他们可以各自独立地改变和复用。(这方面类似于Builder模式和Bridge模式。Builder模式用于对象生成,Bridge模式用于抽象出通用接口,Observer模式用于对状态改变进行通知)
ii. 当对一个对象地改变需要同时改变其他对象,而不知道具体有多少对象有待改变。(这里与Chain of Responsibility相似)
iii. 当一个对象必须通知其他对象,而她又不能假定其他对象是谁,换言之,你不希望这些对象是紧密耦合的。(两者之间的唯一关系,就是Observer必须实现update()方法)

d) 代码实例:
public class Subject{
private Vector observers;
public void addObserver(Observer observer){
observers.add(observer);
}
public void removeObserver(Observer observer){
observers.remove(observer);
}
public void notify(){
for(Observer o:observers){
o.update(this);
}
}
}

public class SubjectA extends Subject{
private int state;
public void stateChanged(){
super.notify();
}
}

public interface Observer{
public void update(Subject sub);
}

public class ObserverA implements Observer{
public void update(Subject sub){
doSomething;
}
}

e) 小结:
i. 目标和观察者之间的抽象耦合。目标只需要知道所有的观察者都实现了update()方法。
ii. 支持广播通信。目标与观察者之间是1-n的关系。
iii. 在Java中,由于Observerable是类,而Java不支持多继承,因此,当一个类如果她已继承了某个类,而又需要当一个Observerable时,可以使用一个内部类,让该内部类继承Observerable类。

6. State模式

a) 结构:



注: 1)允许一个对象在其内部状态改变时改变她的行为,对象看起来似乎修改了她的类。

2)Context通过替换她的state,达到修改其行为的目的。

b) 优点:

i. 把状态信息局部化到一个单独的类当中。(所有的State类)

ii. 她把状态变换外部化。(如果仅以一些内部数据值来定义当前状态时,状态改变就仅表示为一些变量赋值。但是,为不同的状态引入独立的对象,可以使得变换更加明确)

iii. 状态对象可以共享。(如果State对象没有实例变量,则该State对象可以共享,他们避让是没有内部状态,只有行为的轻量级对象)

c) 使用环境:

i. 一个对象的行为取决于她的状态,并且她必须在运行时刻根据状态改变她的行为。(类似于自动机模型,把行为与状态绑定到一起)

ii. 一个操作中含有庞大的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立化。

d) 代码实例:

public class Context{

private State state;

public void request(){

if(conditionA){

state = new StateA();

}else if(conditionB){

state = new StateB();

}

state.handle();

}

}

public interface State{

public void handle();

}

public class StateA implements State{

public void handle(){…}

}

public class StateB implements State{

public void handle(){…}

}

e) 小结:
i. 谁定义状态转换:如果转换规则是固定的,则可以由Context来定义。另一种方法是由每个State负责记录她的后继State,当发生转换时,由State设置Context的状态。

7. Strategy模式

a) 结构:



注: 1)定义一系列的算法,把他们一个个封装起来,并且使他们可相互替换,本模式使得算法可独立于使用他们的客户而变化。

2)Strategy模式与State模式非常相似,但是Strategy模式的目标是实现算法的替换,算法之间不存在顺序关系,State模式的目标是实现状态的转换,状态之间有一定的先后次序。

b) 优点:

i. 用户可以动态的选择策略。(替换strategy实例)

ii. 可以添加新的算法而不需修改客户代码。(所有的Strategy子类都有相同的接口)

c) 使用环境:

i. 许多相关的类仅仅是行为有异,“策略”提供了一种用多个行为中的一个来配置一个类的行为。

ii. 需要使用一个算法的不同变体。

iii. 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的,与算法相关的数据结构。

iv. 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件移入他们各自的Strategy类中以代替这些语句。

d) 代码实例:

public class Context{

private Strategy strategy;

public void selectStrategy(String name){

if(name == conditionA)

strategy = new StrategyA();

else

strategy = new StrategyB();

}

public void request(){

strategy.handle();

}

}

public interface Strategy{

public void handle();

}

public class StrategyA implements Strategy{

public void handle(){…}

}

public class StrategyB implements Strategy{

public void handle(){…}

}

e) 小结:
i. Strategy类层次为Context定义了一系列可供重用的算法或行为。
ii. 将Context中使用的算法从Context中抽取出来,使得Context更加容易理解和重用。
iii. 消除了复杂的if-else语句。
iv. Strategy可以提供相同行为的不同实现。客户可以根据时间/空间权衡取舍,从不同的策略中进行选择。
v. 如果Strategy模式没有内部状态,则该实例可以在各Context间进行共享。
vi. 排序的算法,一般单独实现为一个Strategy,方便共享和重用。

8. Template模式
a) 结构:



注: 1)定义一个操作中的算法步骤,而将一些步骤的实现延迟到子类中。Template模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

b) 优点:
i. 对于公共方法可以在基类实现,而其他的可以在子类实现。

c) 使用环境:
i. 一次性实现一个算法的不变部分,并将可变部分留给子类来实现。(子类可重载或重写父类的方法)
ii. 各子类中公共的行为应该提取出来并集中到一个公共父类中以避免代码重复。
iii. 控制子类扩展,Template方法只在特定点调用hook操作,这样就只允许在这些点进行扩展。(在templateMethod中调用的方法,即为扩展点)

d) 代码实例:
public abstract class AbstractClass{
public void templateMethod(){
actionA();
actionB();
}
public abstract void actionA();
public abstract void actionB();
}

public class ConcreteClass extends AbstractClass{
public void actionA(){…}
public void actionB(){…}
}

e) 小结:
i. Template模式定义了一种方向的控制结构,与Framework的要求一致。

9. Visitor模式

a) 结构:



注: 1)表示一个作用于某对象结构中的元素的操作。她使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。(只要定义新的Visitor对象,然后遍历的visit每个element,即可定义新的操作)

b) 优点:

i. 在不同的对象间执行相同的操作。(不同的Element都具有相同的accept接口)

ii. 增加新的操作很方便。

iii. 访问者集中相关的操作而分离无关的操作。相关的操作都集中到Visitor中,而其他的负责维护列表或树形关系的操作,还是放在Element中。

c) 使用环境:

i. 一个对象的结构包含很多类对象。他们有不同的接口,而你想对这些对象实施一致的,依赖于具体类的操作。(在visit操作中,需要把Element传送到Visitor中,以使得Visitor能获取她所需要的,关于Element的信息)

ii. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作。而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中,当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。(特定的操作,都在visit方法中定义)

iii. 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。

d) 代码实例:

public interface Visitor{

public void visitA(Element e);

public void visitB(Element e);

}

public class VisitorA implements Visitor{

public void visitA(Element e){…}

public void visitB(Element e){…}

}

public class VisitorB implements Visitor{

public void visitA(Element e){…}

public void visitB(Element e){…}

}

public interface Element{

public void accept(Visitor v);

}

public class ElementA implements Element{

public void accept(Visitor v){

v.visitA(this);

}

}

public class ElementB implements Element{

public void accept(Visitor v){

v.visitB(this);

}

}

e) 小结:
i. 增加新的Element子类很困难。因为,这需要修改整个Visitor的结构,并添加新的visit方法。
ii. 破坏封装。因为,Visitor要成功执行他的visit方法时,需要了解Element的内部信息,这导致了Element需要提供一些方法,来暴露其内部细节。

10. 行为模式讨论
a) 封装变化:
i. 一个Stratergy对象封装一个算法。
ii. 一个State对象封装一个与状态相关的行为。
iii. 一个Mediator对象封装对象间的协议。
iv. 一个Iterator对象封装访问和遍历一个聚集对象中的各个构件的方法。
v. 一个Memento对象封装了对象的内部状态。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: