Ericson's blog Time is limited, To be a better better man

享元模式

享元模式,运用共享技术有效的支持大量细粒度的对象。享元模式结构图如下所示: flyweight Flyweight类,它是所有具体享元类的超类或接口,通过这个接口,Flyweight可以接受并作用于外部状态。

abstract class Flyweight{
    public abstract void Operation(int extrinsicstate);
}

ConcreteFlyweight是继承Flyweight超类或实现Flyweight接口,并未内部状态增加存储空间。

class ConcreteFlyweight extends Flyweight{
    public void Operation(int extrinsicstate){
        System.out.println("具体Flyweight "+extrinsicstate);
    }
}

UnsharedConcreteFlyweight是指那些不需要共享的Flyweight子类,因为Flyweight接口共享成为可能,但它不强制共享。

class UnsharedConcreteFlyweight extends Flyweight{
    public void Operation(int extrinsicstate){
        System.out.println("不共享具体Flyweight"+extrinsicstate);
    }
}

FlyweightFactory,是一个享元工厂,用来创建并管理Flyweight对象,它主要是用来确保合理的共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个。

class FlyweightFactory{
    private HashMap<String,ConcreteFlyweight> flyweight=new HashMap<String,ConcreteFlyweight>();
    public FlyweightFactory(){
        flyweight.put("X",new ConcreteFlyweight());
    }
    public Flyweight GetFlyweight(String key){
        return flyweight.get(key);
    }
}

享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实例来表示数据,如果能发现这些实例除了几个参数外基本上都是相同的,就能大幅度减少实例化类的数量,如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度减少单个实例的数目。

享元模式分为内部状态和外部状态:
内部状态:在享元状态内部,不随环境改变而改变的共享部分。
外部状态:在享元状态外部,随着环境改变而改变不能共享的部分。

享元模式优缺点:
优点:

  • 能够极大的减少系统中对象的个数
  • 享元模式使用了外部状态,外部状态相对独立,不会影响到内部状态,所以享元模式使得享元对象在不同环境中被共享

缺点:

  • 由于享元模式需要区分外部状态和内部状态,使得应用程序在某种程度上更加复杂化
  • 为了使对象可以共享,享元模式需要将对象的状态外部化,读取外部状态使得运行时间变长

中介者模式

中介者模式,用一个中介对象去封装一系列对象的交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 Mediator类,抽象中介类:

abstract class Mediator{
    public abstract void Send(String message,Colleague colleague);
}

Colleague类,抽象同事类:

abstract class Colleague{
    protected Mediator mediator;
    public Colleague(Mediator mediator){
        this.mediator=mediator;
    }
}

ConcreteMediator类,具体中介类:

class ConcreteMediator extends Mediator{
    private ConcreteColleague1 colleague1;
    private ConcreteColleague2 colleague2;
    public void Send(String message,Colleague colleague){
        if(colleague==colleague1){
            colleague2.Notify(message);
        }else{
            colleague1.Notify(message);
        }
    }
}

ConcreteColleague1和ConcreteColleague各种同事类:

class ConcreteColleague1 extends Colleague{
    public void Send(String message){
        mediator.Send(message,this);
    }
    public void Notify(String message){
        System.out.println("colleague1 handle "+message);
    }
}
class ConcreteColleague2 extends Colleague{
    public void Send(String message){
        mediator.Send(message,this);
    }
    public void Notify(String message){
        System.out.println("colleague2 handle "+message);
    }
}

客户端代码:

static void main(String[] args){
    ConcreteMediator m=new ConcreteMediator();
    ConcreteColleague1 c1=new ConcreteColleague1();
    ConcreteColleague2 c2=new ConcreteColleague2();
    m.colleague1=c1;
    m.colleague2=c2;
    c1.Send("A");
    c2.Send("B");
}

中介者模式优缺点:
优点:Mediator的出现减少了各个Colleague的耦合,使得可以独立地改变和复用各个Colleague类和Mediator,由于把对象如何协作进行了抽象,将中介作为一个独立的概念,并将其封装在一个对象中,这样关注的对象就从对象各自本身的行为转移到它们之间的交互上,也就是站在一个更宏观的角度去看待系统。

缺点:由于ConcreteMediator控制集中化,于是就把交互的复杂性变为了中介者的复杂性,这就使得ConcreteMediator比任何一个ConcreteColleague都复杂。

职责链模式

职责链模式,使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。职责链模式的结构图如下所示: chainresponsibility Handler类,定义一个处理请示的接口:

abstract class Handler{
    protected Handler successor;
    public void SetSuccessor(Handler successor){
        this.successor=successor;
    }
    public abstract void HandleRequest(int request);
}

ConcreteHandler类,具体处理者类,处理它所负责的请求,可访问它的后继者,如果可处理该请求,就处理之,否则就将该请求转发给它的后继者。ConcreteHandler1,当请求数在0到10之间则有权处理,否则就转到下一位。

class ConcreteHandler1 extends Hander{
    public void HandleRequest(int request){
        if(request>=0&&request<=10){
            System.out.println("handle1");
        }else if(successor!=null){
            successor.HandleRequest(request);
        }
    }
}

ConcreteHandler2,当请求数在10到20之间时有权处理,否则转到下一位。

class ConcreteHandler2 extends Handler{
    public void HandleRequest(int request){
        if(request>=10&&request<=20){
            System.out.println("handle2");
        }else if(successor!=null){
            successor.HandleRequest(request);
        }
    }
}

客户端代码:

static void main(String[] args){
    Handler h1=new ConcreteHandler1();
    Handler h2=new ConcreteHandler2();
    h1.SetSuccessor(h2);
    int[] request={12,3,6,17,15};
    for(int i=0;i<request.length;i++){
        h1.HandleRequest(request[i]);
    }
}

职责链模式优缺点:
优点:

  • 降低耦合度,它将请求的发送者和接收者解耦
  • 简化了对象,使得对象不需要知道链的结构
  • 增强给对象指派职责的灵活性,通过改变链内成员或者调动它们的次序,允许动态地新增或者删除职责
  • 增加新的请求处理类很方便

缺点:

  • 不能保证请求一定被接收
  • 系统性能将受到一定的影响,而且在代码调试时不太方便,可能会造成循环调用
  • 可能不容易观察运行时的特征,有碍于排错

命令模式

命令模式,将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。命令模式结构图如下所示: command Command类,用来声明执行操作的接口:

abstract class Command{
    protected Receiver receiver;
    public Command(Receiver receiver){
        this.receiver=receiver;
    }
    abstract public void Execute();
}

ConcreteCommand类,将一个接收者对象绑定于一个动作,调用接收者相应的操作,以实现Execute:

class ConcreteCommand extends Command{
    public ConcreteCommand(Receiver receiver){
        super(receiver);
    }
    public void Execute(){
        receiver.Action();
    }
}

Invoker类,要求该命令执行这个请求:

class Invoker{
    private Command command;
    public void SetCommand(Command command){
        this.command=command;
    }
    public void ExecuteCommand(){
        command.Execute();
    }
}

Receiver类,知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接收者:

class Receiver{
    public void Action(){
        System.out.println("action");
    }
}

客户端代码:

static void main(String[] args){
    Receiver r=new Receiver();
    Command c=new ConcreteCommand(r);
    Invoker i=new Invoker();
    i.SetCommand(c);
    i.ExecuteCommand();
}

命令模式的优缺点:
首先命令模式的封装性很好:每个命令都被封装起来,对于客户端来说,需要什么功能就去调用相应的命令,而无需知道命令具体是怎么执行的。其次,命令模式的扩展性很好,在命令模式中,在接收者类中一般会对操作进行最基本的封装,命令类则通过对这些基本的操作进行二次封装,当增加新命令的时候,对命令类的编写一般不是从零开始的,有大量的接收者类可供调用,也有大量的命令类可供调用。如果命令很多,不管多么简单的命令都会用一个命令类来封装,开发困难。

桥接模式

对象的继承关系是在编译时就定义好了,所以无法在运行时改变从父类继承的实现。子类实现与它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。当你需要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。

合成/聚合复用原则,尽量使用合成/聚合,尽量不要使用类继承。聚合表示一种弱的“拥有”关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;合成则是一种强的“拥有”关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。合成/聚合复用原则的好处是,优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。

桥接模式,将抽象部分与它的实现部分分离,使它们都可以独立地变化。桥接模式结构图如下所示: bridge Implementor类:

abstract class Implementor{
    public abstract void Operation();
}

ConcreteImplementorA和ConcreteImplementorB等派生类:

class ConcreteImplementorA extends Implementor{
    public void Operation(){
        System.out.println("method A");
    }
}
class ConcreteImplementorB extends Implementor{
    public void Operation(){
        System.out.println("method B");
    }
}

Abstraction类:

class Abstraction{
    protected Implementor implementor;
    public void SetImplementor(Implementor implementor){
        this.implementor=implementor;
    }
    public virtual void Operation(){
        implementor.Operation();
    }
}

RefinedAbstraction类:

class ReginedAbstraction extends Abstraction{
    public void Operation(){
        implementor.Operation();
    }
}

客户端实现:

static void Main(String[] args){
    Abstraction ab=new RefinedAbstraction();
    ab.SetImplementor(new ConcreteImplementorA());
    ab.Operation();
    ab.SetImplementor(new ConcreteImplementorB());
    ab.Operation();
}

实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。桥接模式应用于多维变化。

效果及实现要点:

  • Bridge模式使用“对象间的组合关系”解耦了抽象与实现之间固有的绑定关系,使得抽象与实现可以沿着各自的维度来变化
  • 所谓抽象和实现沿着各自维度的变化,即“子类化”它们,得到各个子类之后,便可以任意它们,从而获得不同路上的公共汽车
  • Bridge模式有时候类似于多继承方案,但是多继承方案往往违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法
  • Bridge模式的应用一般在“两个非常强的变化维度”,有时候即使有两个变化的维度,但是某个方向的变化维度并不剧烈——换言之两个变化不会导致纵横交错的结构,并不一定要使用Bridge模式

适用性:

  • 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系
  • 设计要求实现化角色的任何改变不应当影响客户端,或者说实现化角色的改变对客户端是完全透明的
  • 一个构件有多于一个的抽象化角色和实现化角色,系统需要它们之间进行动态耦合
  • 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者

桥接模式和装饰模式区别:
装饰模式:这两个模式在一定程度上都为了减少子类的数目,避免出现复杂的继承关系,但是它们解决的方法却各有不同,装饰模式把子类中比基类多出来的部分放到单独的类里面,以适应新功能增加的需要,当我们把描述新功能的类封装到基类对象里面时,就得到了所需要的子类对象,这些描述新功能的类通过组合可以实现很多的功能组合。

桥接模式:桥接模式则把原来的基类的实现化细节抽象出来,在构造到一个实现化的结构中,然后再把原来的基类改造成一个抽象化的等级结构,这样就实现系统在多个维度的独立变化。