七、状态模式

最重要的是与策略模式的区别:

策略模式中,是Client提供了不同的策略给Context;状态模式中,状态转移由Context或State自己管理。

状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了他的类。

适用场景:
一个对象的行为取决于他的状态,并且它必须在运行时根据状态改变它的行为;
一个操作中含有庞大的多分支结构,并且这些分支决定于对象的状态。

类图:



模式的组成:
环境类Context: 定义客户感兴趣的接口。维护一个ConcreteState子类的实例,这个实例定义当前状态;
抽象状态类State: 定义一个接口以封装与Context的一个特定状态相关的行为;
具体状态类ConcreteState: 每一子类实现一个与Context的一个状态相关的行为;

实例:push显示红-〉黄-〉绿,pull显示绿-〉黄-〉红
public interface State
{
    public void handlePush(Context context);

    public void handlePull(Context context);

    public String getColor();
}

public class Context
{
    private State state = null;

    public State getState()
    {
        return state;
    }

    public void setState(final State state)
    {
        this.state = state;
    }

    public void push()
    {
        state.handlePush(this);
    }

    public void pull()
    {
        state.handlePull(this);
    }

}

//RED
public class ConcreteStateA implements State
{
    @Override
    public void handlePush(final Context context)
    {
        System.out.print(this.getColor());

        final State state = new ConcreteStateB();
        context.setState(state);
        state.handlePush(context);
    }

    @Override
    public void handlePull(final Context context)
    {
        System.out.print(this.getColor());
    }

    @Override
    public String getColor()
    {
        return "RED";
    }
}

//YELLOW
public class ConcreteStateB implements State
{
    @Override
    public void handlePush(final Context context)
    {
        System.out.print(this.getColor());

        final State state = new ConcreteStateC();
        context.setState(state);
        state.handlePush(context);

    }

    @Override
    public void handlePull(final Context context)
    {
        System.out.print(this.getColor());

        final State state = new ConcreteStateA();
        context.setState(state);
        state.handlePull(context);
    }

    @Override
    public String getColor()
    {
        return "YELLOW";
    }

}

//green
public class ConcreteStateC implements State
{
    @Override
    public void handlePush(final Context context)
    {
        System.out.print(this.getColor());
    }

    @Override
    public void handlePull(final Context context)
    {
        System.out.print(this.getColor());

        final State state = new ConcreteStateB();
        context.setState(state);
        state.handlePull(context);

    }

    @Override
    public String getColor()
    {
        return "GREEN";
    }
}

public class Client
{
    public static void main(final String[] args)
    {
        // YTODO Auto-generated method stub
        final Context context = new Context();
        context.setState(new ConcreteStateA());
        System.out.println("PUSH:");
        context.push();
        System.out.println("\nPULL:");
        context.pull();
    }
}


结果:
PUSH:
REDYELLOWGREEN
PULL:
GREENYELLOWRED




八、桥模式

类图:


  根据业务的功能要求,业务的变化具有两个维度,一个维度是抽象的消息,包括普通消息、加急消息和特急消息,这几个抽象的消息本身就具有一定的关系,加急消息和特急消息会扩展普通消息;另一个维度是在具体的消息发送方式上,包括系统内短消息、邮件和手机短消息,这几个方式是平等的,可被切换的方式。

  

  

  现在出现问题的根本原因,就在于消息的抽象和实现是混杂在一起的,这就导致了一个纬度的变化会引起另一个纬度进行相应的变化,从而使得程序扩展起来非常困难。

  要想解决这个问题,就必须把这两个纬度分开,也就是将抽象部分和实现部分分开,让它们相互独立,这样就可以实现独立的变化,使扩展变得简单。抽象部分就是各个消息的类型所对应的功能,而实现部分就是各种发送消息的方式。按照桥梁模式的结构,给抽象部分和实现部分分别定义接口,然后分别实现它们就可以了。

  

 源代码

  抽象消息类

复制代码
public abstract class AbstractMessage {     //持有一个实现部分的对象     MessageImplementor impl;     /**      * 构造方法,传入实现部分的对象      * @param impl  实现部分的对象      */     public AbstractMessage(MessageImplementor impl){         this.impl = impl;     }     /**      * 发送消息,委派给实现部分的方法      * @param message    要发送消息的内容      * @param toUser    消息的接受者      */     public void sendMessage(String message , String toUser){         this.impl.send(message, toUser);     } }
复制代码

 

  普通消息类

复制代码
public class CommonMessage extends AbstractMessage {      public CommonMessage(MessageImplementor impl) {         super(impl);     }     @Override     public void sendMessage(String message, String toUser) {         // 对于普通消息,直接调用父类方法,发送消息即可         super.sendMessage(message, toUser);     } }
复制代码

  加急消息类

复制代码
public class UrgencyMessage extends AbstractMessage {      public UrgencyMessage(MessageImplementor impl) {         super(impl);     }     @Override     public void sendMessage(String message, String toUser) {         message = "加急:" + message;         super.sendMessage(message, toUser);     }     /**      * 扩展自己的新功能,监控某消息的处理状态      * @param messageId    被监控的消息编号      * @return    监控到的消息的处理状态      */     public Object watch(String messageId) {         // 根据消息id获取消息的状态,组织成监控的数据对象,然后返回         return null;     } }
复制代码

  实现发送消息的统一接口

复制代码
public interface MessageImplementor {     /**      * 发送消息      * @param message 要发送消息的内容      * @param toUser  消息的接受者      */     public void send(String message , String toUser); }
复制代码

  系统内短消息的实现类

复制代码
public class MessageSMS implements MessageImplementor {      @Override     public void send(String message, String toUser) {                  System.out.println("使用系统内短消息的方法,发送消息'"+message+"'给"+toUser);     }  }
复制代码

  邮件短消息的实现类

复制代码
public class MessageEmail implements MessageImplementor {      @Override     public void send(String message, String toUser) {         System.out.println("使用邮件短消息的方法,发送消息'"+message+"'给"+toUser);     }  }
复制代码

  客户端类

复制代码
public class Client {      public static void main(String[] args) {         //创建具体的实现对象         MessageImplementor impl = new MessageSMS();         //创建普通消息对象         AbstractMessage message = new  CommonMessage(impl);         message.sendMessage("加班申请速批","李总");                  //将实现方式切换成邮件,再次发送         impl = new MessageEmail();         //创建加急消息对象         message = new UrgencyMessage(impl);         message.sendMessage("加班申请速批","李总");     }  }
复制代码

  观察上面的例子会发现,采用桥梁模式来实现,抽象部分和实现部分分离开了,可以相互独立的变化,而不会相互影响。因此在抽象部分添加新的消息处理(特急消息),对发送消息的实现部分是没有影响的;反过来增加发送消息的方式(手机短消息),对消息处理部分也是没有影响的。

桥梁模式的优点

  ●  分离抽象和实现部分

  桥梁模式分离了抽象部分和实现部分,从而极大地提供了系统的灵活性。让抽象部分和实现部分独立出来,分别定义接口,这有助于对系统进行分层,从而产生更好的结构化的系统。

 

  ●  更好的扩展性

  桥梁模式使得抽象部分和实现部分可以分别独立地扩展,而不会相互影响,从而大大提高了系统的可扩展性。




九、适配器模式

适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法在一起工作的两个类能够在一起工作。

类图:




Adapter类实现了Target接口,并继承自Adaptee,Adapter类的Request方法重新封装了Adaptee的SpecificRequest方法,实现了适配的目的。

因为Adapter与Adaptee是继承的关系,所以这决定了这个适配器模式是类的。

该适配器模式所涉及的角色包括:

目标(Target)角色:这是客户所期待的接口。
源(Adaptee)角色:需要适配的类。
适配器(Adapter)角色:把源接口转换成目标接口。这一角色必须是类。


适配器模式有2种,第一种是“面向类的适配器模式”,第二种是“面向对象的适配器模式”。

 

先说“面向类的适配器模式”。顾名思义,这类适配器模式就是主要用于,单一的为某个类而实现适配的这样一种模式,为什么说只为某个类去实现,一会提到,我们先展示这种类适配模式的代码实现。

 

源的代码如下:

[c-sharp] view plaincopy
  1. public class Person {  
  2.       
  3.     private String name;  
  4.     private String sex;  
  5.     private int age;  
  6.       
  7.     public void speakJapanese(){  
  8.         System.out.println("I can speak Japanese!");  
  9.     }  
  10.       
  11.     public void speakEnglish(){  
  12.         System.out.println("I can speak English!");  
  13.     }  
  14.     ...//以下省略成员变量的get和set方法  
  15. }  

 

 目标接口的代码如下:

[c-sharp] view plaincopy
  1. public interface Job {  
  2.       
  3.     public abstract void speakJapanese();  
  4.     public abstract void speakEnglish();  
  5.     public abstract void speakFrench();  
  6.       
  7. }  

 适配器的代码如下:

[c-sharp] view plaincopy
  1. public class Adapter extends Person implements Job{  
  2.   
  3.     public void speakFrench() {  
  4.           
  5.     }  
  6.       
  7. }  

 

好了,代码看完然后要做一些说明了,之前遗留的一个问题,为什么称其为类适配模式呢?很显然的,Adapter类继承了Person类,而在Java这种单继承的语言中也就意味着,他不可能再去继承其他的类了,这样也就是这个适配器只为Person这一个类服务。所以称其为类适配模式。

 

说完类的适配模式,我们要开始说第2种对象的适配器模式了。对象适配器模式是把“源”作为一个对象聚合到适配器类中。同样的话不多说,贴上代码:

 

源的代码以及目标代码同上,再次不再赘述。

仅贴出适配器代码:

[c-sharp] view plaincopy
  1. public class Adapter implements Job {  
  2.   
  3.     Person person;  
  4.   
  5.     public Adapter(Person person) {  
  6.         this.person = person;  
  7.     }  
  8.   
  9.     public void speakEnglish() {  
  10.         person.speakEnglish();  
  11.     }  
  12.   
  13.     public void speakJapanese() {  
  14.         person.speakJapanese();  
  15.     }  
  16.   
  17.     //new add  
  18.     public void speakFrench() {  
  19.           
  20.     }  
  21.   
  22. }  

 

对象的适配器模式,把“源”作为一个构造参数传入适配器,然后执行接口所要求的方法。这种适配模式可以为多个源进行适配。弥补了类适配模式的不足。

 

现在来对2种适配模式做个分析:

1.类的适配模式用于单一源的适配,由于它的源的单一话,代码实现不用写选择逻辑,很清晰;而对象的适配模式则可用于多源的适配,弥补了类适配模式的不足,使得原本用类适配模式需要写很多适配器的情况不复存在,弱点是,由于源的数目可以较多,所以具体的实现条件选择分支比较多,不太清晰。

2.适配器模式主要用于几种情况:(1)系统需要使用现有的类,但现有的类不完全符合需要。(2)讲彼此没有太大关联的类引进来一起完成某项工作(指对象适配)。

 

最后,再来顺带谈谈默认适配器模式:这种模式的核心归结如下:当你想实现一个接口但又不想实现所有接口方法,只想去实现一部分方法时,就用中默认的适配器模式,他的方法是在接口和具体实现类中添加一个抽象类,而用抽象类去空实现目标接口的所有方法。而具体的实现类只需要覆盖其需要完成的方法即可。代码如下:

接口类:

[c-sharp] view plaincopy
  1. public interface Job {  
  2.       
  3.     public abstract void speakJapanese();  
  4.     public abstract void speakEnglish();  
  5.     public abstract void speakFrench();  
  6.     public abstract void speakChinese();  
  7.       
  8. }  

抽象类: 

[c-sharp] view plaincopy
  1. public abstract class JobDefault implements Job{  
  2.   
  3.     public void speakChinese() {  
  4.           
  5.     }  
  6.   
  7.     public void speakEnglish() {  
  8.           
  9.     }  
  10.   
  11.     public void speakFrench() {  
  12.           
  13.     }  
  14.   
  15.     public void speakJapanese() {  
  16.           
  17.     }  
  18.   
  19. }  

实现类:

[c-sharp] view plaincopy
  1. public class JobImpl extends JobDefault{  
  2.       
  3.     public void speakChinese(){  
  4.         System.out.println("I can speak Chinese!");  
  5.     }  
  6.       
  7. }