23种设计模式的简单实现(下 13-23)

我的上一篇文章介绍了第1-12种设计模式,现在介绍剩下的设计模式,上篇文章链接:

http://mp.blog.csdn.net/postedit/79321122

行为型模式

十三、策略模式

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

这个模式实际上在《effective java》中提及过,本质上是用宿主类模拟其他语言的函数指针来进行传递函数。

代码例子,我们需要在某个环境下使用不同的策略来实现一些东西,而且允许用户自定义策略:

package 策略模式;

//环境角色:持有一个Strategy也就是抽象策略角色的引用
class Context {
    private Strategy strategy;
    public Context(Strategy strategy){
        this.strategy = strategy;
    }
    //这个环境的某个执行方法
    public void run(){
        strategy.myStrategy();
    }
}
//抽象策略类
interface Strategy {
    void myStrategy();
}
//具体策略类
class StrategyA implements Strategy {
    @Override
    public void myStrategy() {
        //模拟策略A类的策略
        System.out.println("策略A");
    }
}
class StrategyB implements Strategy {
    @Override
    public void myStrategy() {
        //模拟策略B类的策略
        System.out.println("策略B");
    }
}
public class Main {
    public static void main(String[] args) {
        //在环境中使用策略A
        Context context1 = new Context(new StrategyA());
        context1.run();//策略A
        //在环境中使用策略B
        Context context2 = new Context(new StrategyB());
        context2.run();//策略B
        //在环境中使用自定的的策略
        Context context3 = new Context(new Strategy(){
            @Override
            public void myStrategy() {
                //模拟用户自定义的策略
                System.out.println("用户自定义的策略");
            }
        });
        context3.run();//用户自定义的策略
    }
}

优点:

1、策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。

2、使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件语句里面,比使用继承的办法还要原始和落后。

缺点:

1、客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况

2、由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。


十四、模板方法模式

定义一个操作的算法骨架,而将一些步骤延迟到子类中,模版方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

代码例子,我们简单模拟一个入职的过程,入职需要先投简历,然后再获取Offer,这个顺序是不会变的,是我们的算法框架,只需要子类确定投简历和获取Offer过程,入职过程框架是不变的:

package 模板方法模式;

//约定算法骨架
abstract class Context {
    //提交简历
    public abstract void handInResume();
    //获得Offer
    public abstract void getOffer();
    //入职过程的先投简历再获得Offer这个顺序是固定的 具体细节具体类实现
    public final void entry(){
        handInResume();
        getOffer();
    }
}
//具体实现类 百度入职过程
class BaiduContext extends Context {
    //向百度投简历
    @Override
    public void handInResume() {
        System.out.println("向百度投简历");
    }
    //获取百度的Offer
    @Override
    public void getOffer() {
        System.out.println("获得百度Offer");
    }
}
//具体实现类 阿里入职过程
class ALiContext extends Context {
    //向阿里投简历
    @Override
    public void handInResume() {
        System.out.println("向阿里投简历");
    }
    //获取阿里的Offer
    @Override
    public void getOffer() {
        System.out.println("获得阿里Offer");
    }
}
public class Main {
    public static void main(String[] args) {
        Context baiduContext = new BaiduContext();
        baiduContext.entry();//向百度投简历
                             //获得百度Offer
        Context aliContext = new ALiContext();
        aliContext.entry();  //向阿里投简历
                             //获得阿里Offer
    }
}


十五、观察者模式

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖与它的对象都得到通知并被自动更新

代码例子,几个观察者观察一个被观察者,被观察者信息发生变化可以反馈给观察者,观察者会做出响应:

package 观察者模式;

import java.util.ArrayList;
import java.util.List;

//抽象被观察者
abstract class Observerable {
    protected String message;
    //注册
    public abstract void registerObserver(Observer o);
    //删除
    public abstract void removeObserver(Observer o);
    //通知观察者
    public abstract void notifyObserver();
    //设置最新消息
    public void setMessage(String message){
        this.message = message;
        //设置完之后通知观察者
        notifyObserver();
    }
}
//被观察者
class Observed extends Observerable {
  private List<Observer> list;
  //被观察者传递的message
  public Observed(){
      list = new ArrayList<Observer>();
  }
  @Override
  public void registerObserver(Observer o) {
      list.add(o);
  }
  @Override
  public void removeObserver(Observer o) {
      list.remove(o);
  }
  @Override
  public void notifyObserver() {
      for(int i = 0; i < list.size(); i++){
          //遍历
          Observer observer = list.get(i);
          //更新
          observer.update(message);
      }
  }
}
//抽象观察者
interface Observer {
    //观察者被通知的时候update(String)就会回调
    void update(String message);
}
//具体的观察者
class ConcreteObserver implements Observer {
    private String name;
    private String message;
    public ConcreteObserver(String name){
        this.name = name;
    }
    @Override
    public void update(String message) {
        this.message = message;
        System.out.println(this.name+"更新消息为"+message);
    }
}
public class Main {
    public static void main(String[] args) {
        Observerable observed = new Observed();
        Observer observer1 = new ConcreteObserver("观察者1");
        Observer observer2 = new ConcreteObserver("观察者2");
        Observer observer3 = new ConcreteObserver("观察者3");
        //观察者注册
        observed.registerObserver(observer1);
        observed.registerObserver(observer2);
        observed.registerObserver(observer3);
        //被观察者改变信息
        observed.setMessage("new message");
        //观察者1更新消息为new message
        //观察者2更新消息为new message
        //观察者3更新消息为new message
    }
}
优点:
观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。
从而使得各自的变化都不会影响另一边的变化。
缺点:

依赖关系并未完全解除,抽象通知者依旧依赖抽象的观察者。


十六、迭代子模式

迭代子模式又叫游标(Cursor)模式,是对象的行为模式。迭代子模式可以顺序地访问一个聚集中的元素而不必暴露聚集的内部表象

这个模式在Java集合类中已经很常见了,不多说。

代码例子,迭代聚集元素:

package 迭代子模式;

//抽象迭代子角色类
interface Iterator {
    //第一个元素
    public void first();
    //下一个元素
    public void next();
    //是否为最后一个元素
    public boolean hasNext();
    //获取当前迭代位置对应的元素
    public Object getItem();
}
//具体迭代子角色类
class ConcreteIterator implements Iterator {
    //被迭代的具体聚合对象
    private Aggregate agg;
    //索引 迭代工具
    private int index = 0;
    //记录当前聚集对象大小
    private int size = 0;
    
    public ConcreteIterator(Aggregate agg){
        this.agg = agg;
        this.size = agg.size();
        index = 0;
    }
    @Override
    public void first() {
        index = 0;
    }
    //让用户来判断是否越界
    @Override
    public void next() {
        index++;
    }
    @Override
    public boolean hasNext() {
        return index<size;
    }
    @Override
    public Object getItem() {
        return agg.getItem(index);
    }
}
//抽象工厂方法 获取迭代子对象的接口
interface Aggregate {
    Iterator createIterator();
    int size();
    Object getItem(int index);
}
//具体工厂方法 获取迭代子对象
class ConcreteAggregate implements Aggregate {
    private Object[] arr = null;
    public ConcreteAggregate(Object[] arr){
        this.arr = arr;
    }
    @Override
    public Iterator createIterator() {
        return new ConcreteIterator(this);
    }
    public int size(){
        return arr.length;
    }
    @Override
    public Object getItem(int index){
        return arr[index];
    }
}
public class Main {
    public static void main(String[] args) {
        Aggregate agg = new ConcreteAggregate(new Integer[]{1,2,3,4,5});
        Iterator iterator = agg.createIterator();
        while(iterator.hasNext()){
            System.out.print(iterator.getItem()+" ");
            iterator.next();
        }//1 2 3 4 5 
    }
}

客户端当然可以自行进行迭代,不一定非得需要一个迭代子对象。但是,迭代子对象和迭代模式会将迭代过程抽象化,将作为迭代消费者的客户端与迭代负责人的迭代子责任分隔开,使得两者可以独立的演化。在聚集对象的种类发生变化,或者迭代的方法发生改变时,迭代子作为一个中介层可以吸收变化的因素,而避免修改客户端或者聚集本身。

优点:
1、它支持以不同的方式遍历一个聚合对象。
2、迭代器简化了聚合类。
3、在同一个聚合上可以有多个遍历。
4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。缺点:
对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐
迭代器模式的适用场景:
1、访问一个聚合对象的内容而无须暴露它的内部表示。
2、需要为聚合对象提供多种遍历方式。

3、为遍历不同的聚合结构提供一个统一的接口。


十七、责任链模式

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

代码例子,对于一个请求有一个处理等级,处理者也有一个等级,每个请求到了都先给最低等级的处理者,处理不了再给更高等级的处理者:

package 责任链模式;

//请求类
class Request {
  //请求的等级
  private final int level;
  String info;
  public Request(int level,String info){
      this.level = level;
      this.info = info;
  }
  public int getLevel(){
      return level;
  }
  public String getInfo(){
      return info;
  }
}
//抽象处理者
abstract class Handler {
    private final int LEVEL;
    protected Handler nextHanlder;
    protected Handler(int level,Handler nextHandler){
        this.LEVEL = level;
        this.nextHanlder = nextHandler;
    }
    public Handler getNextHandler(){
        return nextHanlder;
    }
    public void setNextHandler(Handler leader){
        this.nextHanlder = leader;
    }
    protected abstract void handlerMethod();
    public void deal(Request request){
        if(request.getLevel()>LEVEL){//自己无法处理这个请求
            if(getNextHandler()!=null){//有下一个处理者
                System.out.println("该请求当前handler无法处理");
                getNextHandler().deal(request);//交给下一个处理
            }
            else{
                System.out.println("该请求无法处理");
            }
        }
        else{//自己处理
            handlerMethod();
        }
    }
}
//具体抽象类
class ConcreteHandler1 extends Handler {
    public ConcreteHandler1(Handler nextHandler) {
        super(1, nextHandler);//设置处理最高等级为1
    }
    @Override
    protected void handlerMethod() {
        //模拟处理过程
        System.out.println("handler1进行处理");
    }
}
class ConcreteHandler2 extends Handler {
    public ConcreteHandler2(Handler nextHandler) {
        super(2, nextHandler);
    }
    @Override
    protected void handlerMethod() {
        //模拟处理过程
        System.out.println("handler2进行处理");
    }
}
class ConcreteHandler3 extends Handler {
    public ConcreteHandler3(Handler nextHandler) {
        super(3, nextHandler);
    }
    @Override
    protected void handlerMethod() {
        //模拟处理过程
        System.out.println("handler3进行处理");
    }
}
public class Main {
    public static void main(String[] args) {
        //创建责任链
        Handler handler3 = new ConcreteHandler3(null);
        Handler handler2 = new ConcreteHandler2(handler3);
        Handler handler1 = new ConcreteHandler1(handler2);
        handler1.deal(new Request(1,"request1"));//handler1进行处理
        handler1.deal(new Request(3,"request2"));//该请求当前handler无法处理
                                                 //该请求当前handler无法处理
                                                 //handler3进行处理
        handler1.deal(new Request(4,"request3"));//该请求当前handler无法处理
                                                 //该请求当前handler无法处理
                                                 //该请求无法处理
    }
}
优点:
降低耦合度
可简化对象的相互连接
增强给对象指派职责的灵活性
增加新的请求处理类很方便
缺点:
不能保证请求一定被接收。
系统性能将受到一定影响,而且在进行代码调试时不太方便;可能会造成循环调用。
使用场景:
有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。

可动态指定一组对象处理请求。


十八、命令模式

一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化; 可以对请求排队或请求日志,以及支持可撤销的操作。用于行为请求者与行为实现者解耦,对二者松耦合,以便适应变化,分离变和不变的因素。

代码例子,存在一个命令接收者执行命令,我们把命令进行封装成类,存在一个调用者,对命令进行调用:

package 命令模式;

//执行命令的接口
interface Command {
    void execute();
}
//命令接收者
class CommandReceiver {
  //命令a的执行
  public void a(){
      System.out.println("执行命令a");
  }
  //命令b的执行
  public void b(){
      System.out.println("执行命令b");
  }
}
//具体命令
class CommandA implements Command {
    private CommandReceiver receiver;
    public CommandA(CommandReceiver receiver){
        this.receiver = receiver;
    }
    @Override
    public void execute() {
        receiver.a();
    }
}
class CommandB implements Command {
    private CommandReceiver receiver;
    public CommandB(CommandReceiver receiver){
        this.receiver = receiver;
    }
    @Override
    public void execute() {
        receiver.b();
    }
}
//调用者
class Invoker {
    private Command commandA,commandB;
    public Invoker(CommandA a,CommandB b){
        this.commandA = a;
        this.commandB = b;
    }
    public void a(){
        commandA.execute();
    }
    public void b(){
        commandB.execute();
    }
}
public class Main {
    public static void main(String[] args) {
        //命令接收者
        CommandReceiver receiver = new CommandReceiver();
        //命令对象
        CommandA commandA = new CommandA(receiver);
        CommandB commandB = new CommandB(receiver);
        //调用者
        Invoker invoker = new Invoker(commandA,commandB);
        //调用对应方法
        invoker.a();
        invoker.b();
    }
}
优点:
1.他能较容易地设计一个命令队列
2.在需要的情况下,可以较容易地将命令记入日志
3.允许接收请求的一方决定是否要否决请求
4.可以容易地实现对请求的撤销和重做
5.由于加进新的具体命令类不影响其他的类 因此新的具体命令类很容易
适用场景:
1.命令的发送者和命令执行者有不同的生命周期。命令发送了并不是立即执行。相当于invoke和execute分开。
2.命令需要进行各种管理逻辑。

3.需要支持撤消\重做操作。


十九、备忘录模式

不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保持这个状态。这样以后就可将该对象恢复到原先保存的状态。

代码例子,存在一个发起者类,我们需要保存这个类对象某个状态的值,可以生成备忘录,存在一个备忘录类,记录状态,存在一个管理类管理备忘录,可以从进行备忘并且从备忘恢复:

package 备忘录模式;

//发起者角色
class Originator {
    String name;
    public Originator(String name){
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    //进行备份 返回一个备忘对象
    public Memo memo(){
        return new Memo(this);
    }
    //从备忘对象进行恢复
    public void recovery(Memo memo){
        this.name = memo.getName();
    }
}
//备忘录角色 备忘发起者角色的状态
class Memo {
    private String name;
    public Memo(Originator originator){
        this.name = originator.name;
    }
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
}
//管理者角色
class CareTaker {
    private Memo memo;
    public Memo getMemo(){
        return memo;
    }
    public void setMemo(Memo memo){
        this.memo = memo;
    }
}
public class Main {
    public static void main(String[] args) {
        //创建一个管理者
        CareTaker taker = new CareTaker();
        //发起者
        Originator originator = new Originator("name1");
        System.out.println(originator.getName());//name1
        //根据发起者生成一个memo
        taker.setMemo(originator.memo());
        //修改发起者状态
        originator.setName("name2");
        System.out.println(originator.getName());//name2
        //恢复发起者状态
        originator.recovery(taker.getMemo());
        System.out.println(originator.getName());//name1
    }
}
优点:
1.有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须要由发起人对象自己读取,这时使用备忘录模式可以把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。
2.本模式简化了发起人类。发起人不再需要管理和保存其内部状态的一个个版本,客户端可以自行管理他们所需要的这些状态的版本。
3.当发起人角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。
缺点:
1.如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很昂贵。
2.当负责人角色将一个备忘录存储起来的时候,负责人可能并不知道这个状态会占用多大的存储空间,
从而无法提醒用户一个操作是否很昂贵。
适用场合:
1.功能比较复杂的,但是需要维护或记录属性历史的类。

2.需要保存的属性只是众多属性的一小部分时。


二十、状态模式

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

代码例子,我们模拟电梯的打开、关闭、运行、停止四个动作,但是这四个动作不是在所有情况下都能发生,有的状态只能从其中一些状态转化而来,如果我们使用swicth case/ if else 语句来进行状态判断会造成程序的可拓展性非常糟糕,于是我们使用一个环境类,封装的电梯的所有状态,每个状态都有自己的动作,这个动作之后会造成环境状态的改变,环境状态改变也就导致动作的改变:

package 状态模式;

//抽象电梯属性类
abstract class LiftState {
    //电梯开启
    public abstract void open();
    //电梯关闭
    public abstract void close();
    //电梯运行
    public abstract void run();
    //电梯停止
    public abstract void stop();
}
//某个状态的具体属性类
//电梯开启状态下做的事
class OpenningState extends LiftState {
    @Override
    public void open() {
        System.out.println("电梯门一直处于打开状态");
    }
    @Override
    public void close() {
        //修改电梯状态为关闭
        System.out.println("电梯门已关闭");
    }
    @Override
    public void run() {
        System.out.println("电梯门处于打开状态无法启动");
    }
    @Override
    public void stop() {
        System.out.println("当前电梯门打开状态即为停止");
    }
}
class ClosingState extends LiftState {
    @Override
    public void open() {
        System.out.println("电梯门已打开");
    }
    @Override
    public void close() {
        System.out.println("电梯门一直处于关闭状态");
    }
    @Override
    public void run() {
        System.out.println("电梯开始运行");
    }
    @Override
    public void stop() {
        System.out.println("电梯门关闭,还没开始运行,属于停止");
    }
}
class RunningState extends LiftState {
    @Override
    public void open() {
        System.out.println("运行状态的电梯门无法打开");
    }
    @Override
    public void close() {
        System.out.println("运行状态的电梯门已关闭");
    }
    @Override
    public void run() {
        System.out.println("电梯已经在运行中");
    }
    @Override
    public void stop() {
        System.out.println("电梯已停止");
    }
}
class StoppingState extends LiftState {
    @Override
    public void open() {
        System.out.println("电梯门正在打开");
    }
    @Override
    public void close() {
        System.out.println("停止的电梯门就是关闭的");
    }
    @Override
    public void run() {
        System.out.println("正在开始运行");
    }
    @Override
    public void stop() {
        System.out.println("电梯一直处于停止状态");
    }
}
class Context {
    //定义出所有的电梯状态
    public final static OpenningState OPENNING_STATE = new OpenningState();
    public final static ClosingState CLOSING_STATE = new ClosingState();
    public final static RunningState RUNNING_STATE = new RunningState();
    public final static StoppingState STOPPING_STATE = new StoppingState();
    private LiftState liftState = STOPPING_STATE;//默认关闭状态
    public LiftState getLiftState() {
        return liftState;
    }
    public void setLiftState(LiftState liftState){
        this.liftState = liftState;
    }
    public void open(){
        liftState.open();
        liftState = OPENNING_STATE;
    }
    public void close(){
        liftState.close();
        liftState = CLOSING_STATE;
    }
    public void run(){
        liftState.run();
        liftState = RUNNING_STATE;
    }
    public void stop(){
        liftState.stop();
        liftState = STOPPING_STATE;
    }
}
public class Main {
    public static void main(String[] args) {
        Context context = new Context();
        System.out.println(context.getLiftState());//状态模式.StoppingState@15db9742
        context.open();//电梯门正在打开
        System.out.println(context.getLiftState());//状态模式.OpenningState@6d06d69c
        context.close();//电梯门已关闭
        System.out.println(context.getLiftState());//状态模式.ClosingState@7852e922
        context.run();//电梯开始运行
        System.out.println(context.getLiftState());//状态模式.RunningState@4e25154f
        context.stop();//电梯已停止
    }
}
优点:
状态模式将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。
所有状态相关的代码都存在于某个ConcereteState中,所以通过定义新的子类很容易地增加新的状态和转换。
状态模式通过把各种状态转移逻辑分不到State的子类之间,来减少相互间的依赖。
缺点:

导致较多的ConcreteState子类。


二十一、访问者模式

访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。

代码例子,访问者对象访问元素对象,这种操作施加于列表上:

package 访问者模式;

import java.util.ArrayList;
import java.util.List;

//访问者角色 为具体元素角色声明一个访问接口操作
interface Visitor {
    void visit(final ConcreteElementA elementA);
    void visit(final ConcreteElementB elementB);
}
//元素角色接口 定义accept方法 以访问者为参数
interface Element {
    void accept(final Visitor visitor);
}
//具体元素角色
class ConcreteElementA implements Element {
    @Override
    public void accept(final Visitor visitor) {
        visitor.visit(this);
    }   
}
class ConcreteElementB implements Element {
    @Override
    public void accept(final Visitor visitor) {
        visitor.visit(this);
    }   
}
class VisitorA implements Visitor {
    @Override
    public void visit(final ConcreteElementA elementA) {
        //模拟A访问的操作
        System.out.println(getClass().getName()+" visit "+elementA.getClass().getName());
    }
    @Override
    public void visit(final ConcreteElementB elementB) {
        //模拟A访问的操作
        System.out.println(getClass().getName()+" visit "+elementB.getClass().getName());
    }
}
class VisitorB implements Visitor {
    @Override
    public void visit(final ConcreteElementA elementA) {
        //模拟B访问的操作
        System.out.println(getClass().getName()+" visit "+elementA.getClass().getName());
    }
    @Override
    public void visit(final ConcreteElementB elementB) {
        //模拟B访问的操作
        System.out.println(getClass().getName()+" visit "+elementB.getClass().getName());
    }
}
//对象结构角色 能提供一个高层的接口允许该访问者访问它的元素
class ObjectStructure {
    private final List<Element> elements = new ArrayList<Element>();
    public void addElement(final Element e){
        elements.add(e);
    }
    public void removeElement(final Element e){
        elements.remove(e);
    }
    public void accept(final Visitor visitor){
        for(final Element e:elements){
            e.accept(visitor);
        }
    }
}
public class Main {
    public static void main(String[] args) {
        ObjectStructure os = new ObjectStructure();
        os.addElement(new ConcreteElementA());
        os.addElement(new ConcreteElementB());
        
        Visitor visitorA = new VisitorA();
        Visitor visitorB = new VisitorB();
        os.accept(visitorA);
        os.accept(visitorB);
    }
}
优点:
1.访问者模式使得易于增加新的操作,访问者使得增加依赖于复杂对象结构的构件的操作变得容易了。仅需增加一个新的访问者即可在一个对象结构上定义一个新的操作。相反, 如果每个功能都分散在多个类之上的话,定义新的操作时必须修改每一类。
2.访问者集中相关的操作而分离无关的操作 相关的行为不是分布在定义该对象结构的 各个类上,而是集中在一个访问者中。无关行为却被分别放在它们各自的访问者子类中。这 就既简化了这些元素的类,也简化了在这些访问者中定义的算法。所有与它的算法相关的数 据结构都可以被隐藏在访问者中。
缺点:
1.增加新的 ConcreteElement类很困难
Visitor模式使得难以增加新的 Element的子类。每 添加一个新的 ConcreteElement都要在 Vistor中添加一个新的抽象操作,并在每一个 ConcretVisitor类中实现相应的操作。有时可以在 Visitor中提供一个缺省的实现,这一实现可 以被大多数的 ConcreteVisitor继承,但这与其说是一个规律还不如说是一种例外。
所以在应用访问者模式时考虑关键的问题是系统的哪个部分会经常变化,是作用于对象结构上的算法呢还是构成该结构的各个对象的类。如果老是有新的 ConcretElement类加入进来的话, Vistor类层次将变得难以维护。在这种情况下,直接在构成该结构的类中定义这些操作可能更容易一些。如果 Element类层次是稳定的,而你不断地增加操作获修改算法,访问者模式可以帮助你管理这些改动。
2.破坏封装

访问者方法假定ConcreteElement接口的功能足够强,足以让访问者进行它 们的工作。结果是,该模式常常迫使你提供访问元素内部状态的公共操作,这可能会破坏它的封装性。

适用场景:

访问者模式的目的是要把处理从数据结构中分离出来,如果系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式是个不错的选择,因为访问者模式使的算法操作的增加变得容易。相反,如果系统的数据结构不稳定,易于变化,则此系统就不适合使用访问者模式了。


二十二、中介者模式

中介者模式就是通过中介者实现对象之间的通信。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

代码例子,将两个同事类对象之间的通信使用一个中介者类进行连接:

package 中介者模式;

//中介者抽象类
abstract class Mediator {
    protected Colleague colleagueA;
    protected Colleague colleagueB;
    public void setColleagueA(Colleague colleagueA){
        this.colleagueA = colleagueA;
    }
    public void setColleagueB(Colleague colleagueB){
        this.colleagueB = colleagueB;
    }
    public abstract void contact(String content, Colleague colleague);
}
class ConcreteMediator extends Mediator {
    @Override
    public void contact(String content, Colleague colleague) {
        if(colleague == colleagueA){
            colleagueB.getMessage(content);
        }
        else{
            colleagueA.getMessage(content);
        }
    }
}
//同事抽象类
abstract class Colleague {
  //同事名称
  protected String name;
  //中介者
  protected Mediator mediator;
  public Colleague(Mediator mediator){
      this.mediator = mediator;
  }
  //同事与中介通信的
  public abstract void contact(String message);
  public abstract String getMessage(String content);
}
//具体同事类
class ColleagueA extends Colleague {
    public ColleagueA(Mediator mediator) {
        super(mediator);
    }
    @Override
    public void contact(String message) {
        mediator.contact(message, this);
    }
    @Override
    public String getMessage(String content) {
        //模拟同事A获取并且处理文本
        System.out.println("同事A获得信息"+content);
        return content;
    }
}
class ColleagueB extends Colleague {
    public ColleagueB(Mediator mediator) {
        super(mediator);
    }
    @Override
    public void contact(String message) {
        mediator.contact(message, this);
    }
    @Override
    public String getMessage(String content) {
        //模拟获取并且处理文本
        System.out.println("同事B获得信息"+content);
        return content;
    }
}
public class Main {
    public static void main(String[] args) {
        //定义中介者
        Mediator mediator = new ConcreteMediator();
        //两个交互的同事类
        Colleague colleagueA = new ColleagueA(mediator);
        Colleague colleagueB = new ColleagueA(mediator);
        mediator.setColleagueA(colleagueA);
        mediator.setColleagueB(colleagueB);
        colleagueA.contact("我是A,发消息给B...");//同事A获得信息我是A,发消息给B...
        colleagueB.contact("我是B,发消息给A...");//同事A获得信息我是B,发消息给A...
    }
}
优点:
1.Mediator的出现减少了各个 Colleague 的耦合,使得可以独立地改变和复用各个 Colleague类和Mediator
2.由于把对象如何协作进行了抽象,将中介作为一个独立的概念并将其封装在了一个对象中,这样关注的对象就从对象各自本身的行为转移到它们之间的交互上来,也就是站在一个更宏观的角度去看待系统。
缺点:
由于ConcreteMediator控制了集中化,于是就把交互复杂性变为了中介者的复杂性,这就使得中介者会变得比任何一个ConcreteColleague 都复杂。
使用终结者模式的场合:
1.一组定义良好的对象,现在要进行复杂的通信。
2.定制一个分布在多个类中的行为,而又不想生成太多的子类。

中介对象主要是用来封装行为的,行为的参与者就是那些对象,但是通过中介者,这些对象不用相互知道。


二十三、解释器模式

解释器模式是类的行为模式。给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。

代码例子,我们对数学运算中的+、-进行解释,分别作为两个非终结符类,然后加上终结符类变量和常量,以及环境容器,负责对变量进行赋值记录:

package 解释器模式;

import java.util.HashMap;
import java.util.Map;

abstract class Expression {
    //依据环境 解释给定的表达式
    public abstract int interpret(Context ctx);
}
//非终结符表达式
//加
class Plus extends Expression {
    private Expression left,right;
    public Plus(Expression left, Expression right){
        this.left = left;
        this.right = right;
    }
    @Override
    public String toString(){
        return "("+left.toString()+"+"+right.toString()+")";
    }
    @Override
    public int interpret(Context ctx) {
        return left.interpret(ctx)+right.interpret(ctx);
    }
}
//减
class Minus extends Expression {
    private Expression left,right;
    public Minus(Expression left,Expression right){
        this.left = left;
        this.right = right;
    }
    @Override
    public String toString(){
        return "("+left.toString()+"-"+right.toString()+")";
    }
    @Override
    public int interpret(Context ctx) {
        return left.interpret(ctx)-right.interpret(ctx);
    }
}
//终结符表达式
//常量
class Constant extends Expression {
    private int value;
    public Constant(int value){
        this.value = value;
    }
    @Override
    public String toString(){
        return Integer.valueOf(value).toString();
    }
    @Override
    public int interpret(Context ctx) {
        return value;
    }
}
//变量
class Variable extends Expression {
    private String name;
    public Variable(String name){
        this.name = name;
    }
    @Override
    public boolean equals(Object obj){
        if(obj!=null&&obj instanceof Variable){
            return name.equals(((Variable)obj).name);
        }
        return false;
    }
    @Override
    public int hashCode(){
        return toString().hashCode();
    }
    @Override
    public String toString(){
        return name;
    }
    @Override
    public int interpret(Context ctx){
        return ctx.lookup(this);
    }
}
//环境角色 存放变量与对应值的映射
class Context {
    private Map<Variable,Integer> map = new HashMap<Variable,Integer>();
    //给变量赋值
    public void assign(Variable var , int value){
        map.put(var, new Integer(value));
    }
    //查找变量对应值
    public int lookup(Variable var){
        Integer value = map.get(var);
        if(value == null)
            throw new ArithmeticException();
        return value;
    }
}
public class Main {
    public static void main(String[] args) {
        //定义环境
        Context ctx = new Context();
        //变量x y
        Variable x = new Variable("x");
        Variable y = new Variable("y");
        //常量c
        Constant c = new Constant(1);
        //给变量赋值
        ctx.assign(x, 2);
        ctx.assign(y, 3);
        Expression exp = new Plus(new Plus(c,x),new Minus(y,x));
        System.out.println(exp.toString()+"="+exp.interpret(ctx));//((1+x)+(y-x))=4
    }
}

一些重复发生的事情包含固定的一系列操作类型,比较适合用解释器模式来实现。比如加减乘除四则运算,但是公式每次都不同,比如可配置,有时是a + b - c x d,有时是a x b + c - d,等等等等个,公式千变万化,但是都是由加减乘除四个非终结符来连接的,这时我们就可以使用解释器模式。

优点:
可以很容易地改变和拓展文法,因为该模式使用类来表示文法规则,你可使用集成来改变或拓展该文法。也比较容易实现文法,因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写。
缺点:
解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护。建议当文法非常复杂时,使用其他的技术如语法分析程序或编译器生成器来处理。

发布了106 篇原创文章 · 获赞 285 · 访问量 59万+

猜你喜欢

转载自blog.csdn.net/qq_35580883/article/details/79327771
今日推荐