Skip to content

设计模式(三)

约 8349 字大约 28 分钟

2017-06-12

行为型模式。

策略模式

实现一个商场收银软件,营业员根据客户购买的商品单价和数量收费。

如果要去商场对商品搞活动,所有商品打八折呢。 那不就是totalPrices后面乘以0.8么? 那难道商场活动结束了,再改造一次代码么?然后再用改后的程序把所有机器全部安装一次么?还有可能周年庆,打五折的情况,如何处理? 那再加一个下拉框,选择商品活动呢?

这比刚才灵活性上是好多了,不过重复代码很多,像Convert.ToDouble(),你这里就写了8遍,而且4个分支要执行的语句除了打折多少以外几乎没什么不同,应该考虑重构一下。不过这还不是最主要的,现在我的需求又来了,商场的活动加大,需要有满300返100的促销算法,怎么办?

简单工厂实现

面向对象的编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。打一折和打九折只是形式的不同,抽象分析出来,所有的打折算法都是一样的,所以打折算法应该是一个类。好了,空话已说了太多,写出来才是真的懂。

简单工厂模式虽然也能解决这个问题,但这个模式只是解决对象的创建问题,而且由于工厂本身包括了所有的收费方式,商场是可能经常性地更改打折额度和返利额度,每次维护或扩展收费方式都要改动这个工厂,以致代码需重新编译部署,这真的是很糟糕的处理方式,所以用它不是最好的办法。面对算法的时常变动,应该有更好的办法。

策略模式实现

策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

写的CashSuper就是抽象策略,而正常收费CashNormal、打折收费CashRebate和返利收费CashReturn就是三个具体策略,也就是策略模式中说的具体算法。

模仿策略模式的基本代码,改写一下你的程序。 其实不麻烦,原来写的CashSuper、CashNormal、CashRebate和CashReturn都不用更改了,只要加一个CashContext类,并改写一下客户端就行了。

代码是模仿着写出来了。但我感觉这样子做不又回到了原来的老路了吗,在客户端去判断用哪一个算法? 有没有什么好办法,把这个判断的过程从客户端程序转移走呢? 难道简单工厂就一定要是一个单独的类吗?难道不可以与策略模式的Context结合?

策略与简单工厂结合

原来简单工厂模式并非只有建一个工厂类的做法,还可以这样子做。此时比刚才的模仿策略模式的写法要清楚多了,客户端代码简单明了。

观察一下,找出它们的不同之处。

简单工厂模式我需要让客户端认识两个类, CashSuper和CashFactory,而策略模式与简单工厂结合的用法,客户端就只需要认识一个类CashContext就可以了。耦合更加降低。

在客户端实例化的是CashContext的对象,调用的是CashContext的方法GetResult,这使得具体的收费算法彻底地与客户端分离。连算法的父类CashSuper都不让客户端认识了。


观察者模式

观察者模式是一种行为设计模式,用于定义对象之间的一对多依赖关系,当一个对象的状态发生变化时,其所有依赖对象都会自动收到通知并进行相应的更新。

一个软件系统里面包含了各种对象,就像一片欣欣向荣的森林充满了各种生物一样。在一片森林中,各种生物彼此依赖和约束,形成一个个生物链。一种生物的状态变化会造成其他一些生物的相应行动,每一个生物都处于别的生物的互动之中。

同样,一个软件系统常常要求在某一个对象的状态发生变化的时候,某些其他的对象做出相应的改变。做到这一点的设计方案有很多,但是为了使系统能够易于复用,应该选择低耦合度的设计方案。减少对象之间的耦合有利于系统的复用,但是同时设计师需要使这些低耦合度的对象之间能够维持行动的协调一致,保证高度的协作。观察者模式是满足这一要求的各种设计方案中最重要的一种。

下面以一个简单的示意性实现为例,讨论观察者模式的结构。

观察者模式所涉及的角色有:

●  抽象主题(Subject)角色: 抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。

●  具体主题(ConcreteSubject)角色: 将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。

●  抽象观察者(Observer)角色: 为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。

●  具体观察者(ConcreteObserver)角色: 存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。

抽象主题角色类

public abstract class Subject {
    /**
     * 用来保存注册的观察者对象
     */
    private List<Observer> list = new ArrayList<Observer>();
    /**
     * 注册观察者对象
     * @param observer    观察者对象
     */
    public void attach(Observer observer){
        
        list.add(observer);
        System.out.println("Attached an observer");
    }
    /**
     * 删除观察者对象
     * @param observer    观察者对象
     */
    public void detach(Observer observer){
        
        list.remove(observer);
    }
    /**
     * 通知所有注册的观察者对象
     */
    public void notifyObservers(String newState){
        
        for(Observer observer : list){
            observer.update(newState);
        }
    }
}

具体主题角色类

public class ConcreteSubject extends Subject{
    
    private String state;
    
    public String getState() {
        return state;
    }

    public void change(String newState){
        state = newState;
        System.out.println("主题状态为:" + state);
        //状态发生改变,通知各个观察者
        this.nodifyObservers(state);
    }
}

抽象观察者角色类

public interface Observer {
    /**
     * 更新接口
     * @param state    更新的状态
     */
    public void update(String state);
}

具体观察者角色类

public class ConcreteObserver implements Observer {
    //观察者的状态
    private String observerState;
    
    @Override
    public void update(String state) {
        /**
         * 更新观察者的状态,使其与目标的状态保持一致
         */
        observerState = state;
        System.out.println("状态为:"+observerState);
    }

}

客户端类

public class Client {

    public static void main(String[] args) {
        //创建主题对象
        ConcreteSubject subject = new ConcreteSubject();
        //创建观察者对象
        Observer observer = new ConcreteObserver();
        //将观察者对象登记到主题对象上
        subject.attach(observer);
        //改变主题对象的状态
        subject.change("new state");
    }

}

输出结果

在运行时,这个客户端首先创建了具体主题类的实例,以及一个观察者对象。然后,它调用主题对象的attach()方法,将这个观察者对象向主题对象登记,也就是将它加入到主题对象的聚集中去。

这时,客户端调用主题的change()方法,改变了主题对象的内部状态。主题对象在状态发生变化时,调用超类的notifyObservers()方法,通知所有登记过的观察者对象。

推模型和拉模型

在观察者模式中,又分为推模型和拉模型两种方式。 ●  推模型 主题对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。 ●  拉模型 主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。

根据上面的描述,发现前面的例子就是典型的推模型,下面给出一个拉模型的实例。

拉模型的抽象观察者类

public interface Observer {
    /**
     * 更新接口
     * 拉模型通常都是把主题对象当做参数传递。
     * @param subject 传入主题对象,方面获取相应的主题对象的状态
     */
    public void update(Subject subject);
}

拉模型的具体观察者类

public class ConcreteObserver implements Observer {
    //观察者的状态
    private String observerState;
    
    @Override
    public void update(Subject subject) {
        /**
         * 更新观察者的状态,使其与目标的状态保持一致
         */
        observerState = ((ConcreteSubject)subject).getState();
        System.out.println("观察者状态为:"+observerState);
    }

}

拉模型的抽象主题类

/**
  拉模型的抽象主题类主要的改变是nodifyObservers()方法。在循环通知观察者的时候,也就是循环调用观察者的update()方法的时候,传入的参数不同了。
*/
public abstract class Subject {
    /**
     * 用来保存注册的观察者对象
     */
    private    List<Observer> list = new ArrayList<Observer>();
    /**
     * 注册观察者对象
     * @param observer    观察者对象
     */
    public void attach(Observer observer){
        
        list.add(observer);
        System.out.println("Attached an observer");
    }
    /**
     * 删除观察者对象
     * @param observer    观察者对象
     */
    public void detach(Observer observer){
        
        list.remove(observer);
    }
    /**
     * 通知所有注册的观察者对象
     */
    public void nodifyObservers(){
        
        for(Observer observer : list){
            observer.update(this);
        }
    }
}

拉模型的具体主题类

/**
  跟推模型相比,有一点变化,就是调用通知观察者的方法的时候,不需要传入参数了。
*/
public class ConcreteSubject extends Subject{
    
    private String state;
    
    public String getState() {
        return state;
    }

    public void change(String newState){
        state = newState;
        System.out.println("主题状态为:" + state);
        //状态发生改变,通知各个观察者
        this.nodifyObservers();
    }
}

两种模式的比较

■  推模型是假定主题对象知道观察者需要的数据;而拉模型是主题对象不知道观察者具体需要什么数据,没有办法的情况下,干脆把自身传递给观察者,让观察者自己去按需要取值。

■  推模型可能会使得观察者对象难以复用,因为观察者的update()方法是按需要定义的参数,可能无法兼顾没有考虑到的使用情况。这就意味着出现新情况的时候,就可能提供新的update()方法,或者是干脆重新实现观察者;而拉模型就不会造成这样的情况,因为拉模型下,update()方法的参数是主题对象本身,这基本上是主题对象能传递的最大数据集合了,基本上可以适应各种情况的需要。


命令模式

命令模式是一种行为设计模式,它允许您将请求封装成对象,使得请求的发送者和接收者彻底解耦。在命令模式中,请求被封装成一个命令对象,这个命令对象可以被传递和存储,从而允许对请求进行参数化和延迟执行。

命令模式包含以下关键角色:

  • Command(命令):定义了执行请求的接口。
  • ConcreteCommand(具体命令):实现了 Command 接口,并封装了请求的接收者。
  • Receiver(接收者):执行实际操作的对象。
  • Invoker(调用者):负责调用命令对象执行请求。
  • Client(客户端):创建命令对象并设置其接收者。

下面是一个简单的 Java 代码示例,演示了如何使用命令模式来实现一个简单的遥控器控制灯泡的场景:

// Command 接口
interface Command {
    void execute();
}

// ConcreteCommand 类
class LightOnCommand implements Command {
    private Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.turnOn();
    }
}

// Receiver 类
class Light {
    public void turnOn() {
        System.out.println("Light is on");
    }

    public void turnOff() {
        System.out.println("Light is off");
    }
}

// Invoker 类
class RemoteControl {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void pressButton() {
        command.execute();
    }
}

// Client 类
public class Main {
    public static void main(String[] args) {
        // 创建接收者对象
        Light light = new Light();

        // 创建命令对象并设置其接收者
        Command lightOnCommand = new LightOnCommand(light);

        // 创建调用者对象并设置命令对象
        RemoteControl remoteControl = new RemoteControl();
        remoteControl.setCommand(lightOnCommand);

        // 模拟按下按钮,执行命令
        remoteControl.pressButton(); // 输出:Light is on
    }
}

在这个示例中,我们创建了一个遥控器(Invoker),并设置了一个命令(Command)来控制灯泡(Receiver)。当我们按下按钮时,调用者会执行命令,命令又会调用接收者执行相应的操作。

命令模式是一种非常有用的设计模式,它可以将请求封装成对象,从而允许您以参数化的方式进行方法调用、队列请求、日志记录和撤销操作等。

命令模式非常灵活,允许您轻松地扩展和修改功能。例如,您可以添加新的具体命令类来实现不同的操作,而不需要修改现有的代码。这种可扩展性使得命令模式在面对变化需求时非常有用。


模板方法模式

模板方法模式是一种行为设计模式,它在一个方法中定义了一个算法的骨架,而将一些步骤的具体实现延迟到子类中。这样,可以在不改变算法结构的情况下,重新定义算法的某些步骤。模板方法模式提供了一种代码复用的方式,同时允许更容易地对算法进行定制。

模板方法模式包含以下关键角色:

  • AbstractClass(抽象类):定义了一个模板方法,其中包含算法的骨架和一些步骤的具体实现。
  • ConcreteClass(具体类):实现了抽象类中的某些步骤,完成算法的具体实现。

下面是一个简单的示例,演示了如何使用模板方法模式来制作咖啡和茶:

// 抽象类:饮料
abstract class Beverage {
    // 模板方法,定义了制作饮料的步骤
    public final void prepareBeverage() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }

    // 抽象方法,由子类实现
    abstract void brew();
    abstract void addCondiments();

    // 具体方法,共享实现
    void boilWater() {
        System.out.println("Boiling water");
    }

    void pourInCup() {
        System.out.println("Pouring into cup");
    }

    // 钩子方法,子类可以选择性地覆盖
    boolean customerWantsCondiments() {
        return true;
    }
}

// 具体类:咖啡
class Coffee extends Beverage {
    @Override
    void brew() {
        System.out.println("Dripping Coffee through filter");
    }

    @Override
    void addCondiments() {
        System.out.println("Adding Sugar and Milk");
    }
}

// 具体类:茶
class Tea extends Beverage {
    @Override
    void brew() {
        System.out.println("Steeping the tea");
    }

    @Override
    void addCondiments() {
        System.out.println("Adding Lemon");
    }

    @Override
    boolean customerWantsCondiments() {
        return false; // 茶不加调料
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        Beverage coffee = new Coffee();
        System.out.println("Making coffee...");
        coffee.prepareBeverage();

        System.out.println();

        Beverage tea = new Tea();
        System.out.println("Making tea...");
        tea.prepareBeverage();
    }
}

在这个示例中,Beverage 是抽象类,定义了制作饮料的模板方法 prepareBeverage(),以及一些共享的具体方法。CoffeeTea 是具体类,分别实现了 brew()addCondiments() 方法,以完成制作咖啡和茶的过程。

JDK 中的应用

在 JDK 中,模板方法模式被广泛应用。其中一个典型的例子就是在 java.util.AbstractList 类中。这个类实现了 java.util.List 接口,提供了 equals()hashCode() 方法的默认实现,这些方法都是模板方法。以下是 AbstractList 类中的部分核心代码:

// java.util.AbstractList 类中的部分代码
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    // 省略其他方法

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }
        if (!(o instanceof List)) {
            return false;
        }

        Iterator<E> e1 = iterator();
        ListIterator<?> e2 = ((List<?>) o).listIterator();
        while (e1.hasNext() && e2.hasNext()) {
            E o1 = e1.next();
            Object o2 = e2.next();
            if (!(o1==null ? o2==null : o1.equals(o2))) {
                return false;
            }
        }
        return !(e1.hasNext() || e2.hasNext());
    }

    public int hashCode() {
        int hashCode = 1;
        for (E e : this) {
            hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode());
        }
        return hashCode;
    }

    // 省略其他方法
}

在这个例子中,equals()hashCode() 方法就是模板方法,它们定义了算法的骨架,具体的比较和哈希计算操作由子类实现。

模板方法模式是一种非常有用的设计模式,它提供了一种代码复用的方式,并允许在不改变算法结构的情况下,重新定义算法的某些步骤。在 JDK 中,模板方法模式被广泛应用于各种类库和框架中,如集合框架、IO 操作等。


状态模式

状态模式(State Pattern)是一种行为设计模式,它允许对象在其内部状态改变时改变其行为。这种模式的关键是将对象的状态封装成不同的类,并将行为委托给当前状态对象。状态模式可以使得对象的状态转换更加清晰、简单,并且避免了使用大量的条件语句。

角色介绍

  • Context(上下文):定义客户端感兴趣的接口,维护一个当前状态对象。
  • State(状态):定义一个接口,用于封装与 Context 相关的行为。
  • ConcreteState(具体状态):实现 State 接口的具体状态类,负责处理与状态相关的操作。

应用示例

下面是一个简单的示例,演示了如何使用状态模式来实现一个电梯控制器:

// 上下文类:电梯
class Elevator {
    private State state;

    public Elevator() {
        state = new ClosedState(); // 初始化为关闭状态
    }

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

    public void open() {
        state.open(this);
    }

    public void close() {
        state.close(this);
    }

    public void move() {
        state.move(this);
    }
}

// 状态接口
interface State {
    void open(Elevator elevator);
    void close(Elevator elevator);
    void move(Elevator elevator);
}

// 具体状态类:关闭状态
class ClosedState implements State {
    @Override
    public void open(Elevator elevator) {
        System.out.println("Opening the door...");
        elevator.setState(new OpenState());
    }

    @Override
    public void close(Elevator elevator) {
        System.out.println("The door is already closed.");
    }

    @Override
    public void move(Elevator elevator) {
        System.out.println("Moving the elevator...");
        elevator.setState(new MovingState());
    }
}

// 具体状态类:打开状态
class OpenState implements State {
    @Override
    public void open(Elevator elevator) {
        System.out.println("The door is already open.");
    }

    @Override
    public void close(Elevator elevator) {
        System.out.println("Closing the door...");
        elevator.setState(new ClosedState());
    }

    @Override
    public void move(Elevator elevator) {
        System.out.println("Cannot move while the door is open.");
    }
}

// 具体状态类:运行状态
class MovingState implements State {
    @Override
    public void open(Elevator elevator) {
        System.out.println("Cannot open the door while the elevator is moving.");
    }

    @Override
    public void close(Elevator elevator) {
        System.out.println("Cannot close the door while the elevator is moving.");
    }

    @Override
    public void move(Elevator elevator) {
        System.out.println("The elevator is already moving.");
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        Elevator elevator = new Elevator();

        elevator.open();
        elevator.close();
        elevator.move();
        elevator.open();
        elevator.move();
        elevator.close();
    }
}

在这个示例中,Elevator 类是上下文类,负责维护当前状态,并将状态相关的操作委托给当前状态对象。State 接口定义了状态的行为,具体状态类实现了该接口,并负责处理与状态相关的操作。

JDK 中的应用

在 JDK 中,状态模式被广泛应用。其中一个典型的例子就是在 java.awt.Component 类中。这个类表示可视化组件,其中的 setVisible() 方法就是使用了状态模式。以下是 Component 类中的部分核心代码:

// java.awt.Component 类中的部分代码
public class Component {
    private boolean visible;

    public void setVisible(boolean visible) {
        this.visible = visible;
        if (visible) {
            // 显示组件的相关操作
        } else {
            // 隐藏组件的相关操作
        }
    }
    // 其他方法
}

在这个例子中,Component 类维护了一个 visible 属性,用于表示组件的可见状态。根据这个状态的改变,setVisible() 方法会执行不同的操作,从而改变组件的行为。

状态模式是一种非常有用的设计模式,它可以使得对象在其内部状态改变时改变其行为。通过将状态封装成不同的类,并委托给当前状态对象处理相关操作,状态模式使得代码更加清晰、简单,并且避免了使用大量的条件语句。


责任链模式

责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。在责任链模式中,请求沿着一个链条传递,直到有一个对象处理它为止。

角色介绍

  • Handler(处理器):定义了处理请求的接口,并维护了一个指向下一个处理器的引用。
  • ConcreteHandler(具体处理器):实现了处理器接口的具体处理器类,负责处理它所能处理的请求,并在不能处理时将请求传递给下一个处理器

应用示例

考虑一个简单的购买审批系统,其中包含三个级别的审批人:经理、副总裁和总裁。如果购买金额小于等于1000元,经理可以直接批准;如果金额在1000到5000元之间,需要副总裁审批;如果金额超过5000元,需要总裁审批。

// 处理器接口
interface Approver {
    void processRequest(PurchaseRequest request);
}

// 具体处理器类
class Manager implements Approver {
    private Approver nextApprover;

    @Override
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() <= 1000) {
            System.out.println("Manager approves purchase request #" + request.getId());
        } else if (nextApprover != null) {
            nextApprover.processRequest(request);
        } else {
            System.out.println("No one can handle the purchase request #" + request.getId());
        }
    }

    public void setNextApprover(Approver nextApprover) {
        this.nextApprover = nextApprover;
    }
}

class VicePresident implements Approver {
    private Approver nextApprover;

    @Override
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() <= 5000) {
            System.out.println("Vice President approves purchase request #" + request.getId());
        } else if (nextApprover != null) {
            nextApprover.processRequest(request);
        } else {
            System.out.println("No one can handle the purchase request #" + request.getId());
        }
    }

    public void setNextApprover(Approver nextApprover) {
        this.nextApprover = nextApprover;
    }
}

class President implements Approver {
    @Override
    public void processRequest(PurchaseRequest request) {
        System.out.println("President approves purchase request #" + request.getId());
    }
}

// 请求类
class PurchaseRequest {
    private int id;
    private double amount;

    public PurchaseRequest(int id, double amount) {
        this.id = id;
        this.amount = amount;
    }

    public int getId() {
        return id;
    }

    public double getAmount() {
        return amount;
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        // 构建责任链
        Approver manager = new Manager();
        Approver vicePresident = new VicePresident();
        Approver president = new President();

        manager.setNextApprover(vicePresident);
        vicePresident.setNextApprover(president);

        // 创建购买请求
        PurchaseRequest request1 = new PurchaseRequest(1, 800);
        PurchaseRequest request2 = new PurchaseRequest(2, 3500);
        PurchaseRequest request3 = new PurchaseRequest(3, 7000);

        // 处理购买请求
        manager.processRequest(request1);
        manager.processRequest(request2);
        manager.processRequest(request3);
    }
}

JDK 中的应用

在 JDK 中,责任链模式被广泛应用。其中一个典型的例子是 Servlet 中的过滤器链。Servlet 过滤器链是一系列过滤器按照顺序链接在一起,每个过滤器负责处理请求或者将请求传递给下一个过滤器。以下是 Servlet 过滤器链的核心代码:

public interface Filter {
    void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;
}

public interface FilterChain {
    void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;
}

在这个例子中,Filter 接口代表一个过滤器,其中的 doFilter() 方法负责处理请求或者将请求传递给下一个过滤器。而 FilterChain 接口表示一个过滤器链,其中的 doFilter() 方法将请求传递给下一个过滤器。


解释器模式

解释器模式(Interpreter Pattern)是一种行为设计模式,用于定义一个语言的文法,并且提供解释器来解释该语言中的语句。这种模式将语言中的语句表示为抽象语法树,并提供一种方式来递归地解释该树中的节点。

角色介绍

  • AbstractExpression(抽象表达式):声明了一个抽象的解释方法 interpret(),所有的具体表达式都继承自该抽象类。
  • TerminalExpression(终结符表达式):实现了抽象表达式接口,表示语言中的终结符,即不可再分解的最小单元。
  • NonterminalExpression(非终结符表达式):实现了抽象表达式接口,表示语言中的非终结符,即可再分解的复合单元。
  • Context(上下文):包含解释器需要的一些全局信息或状态。

应用示例

假设我们需要实现一个简单的表达式解析器,可以解析简单的四则运算表达式,例如 1 + 2 - 3 * 4。我们可以使用解释器模式来实现这个功能。

// 抽象表达式接口
interface Expression {
    int interpret(Context context);
}

// 终结符表达式:数字
class NumberExpression implements Expression {
    private int number;

    public NumberExpression(int number) {
        this.number = number;
    }

    @Override
    public int interpret(Context context) {
        return number;
    }
}

// 非终结符表达式:加法
class AddExpression implements Expression {
    private Expression left;
    private Expression right;

    public AddExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret(Context context) {
        return left.interpret(context) + right.interpret(context);
    }
}

// 非终结符表达式:减法
class SubstractExpression implements Expression {
    private Expression left;
    private Expression right;

    public SubstractExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret(Context context) {
        return left.interpret(context) - right.interpret(context);
    }
}

// 非终结符表达式:乘法
class MultiplyExpression implements Expression {
    private Expression left;
    private Expression right;

    public MultiplyExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret(Context context) {
        return left.interpret(context) * right.interpret(context);
    }
}


// 上下文类
class Context {
    // 如果需要的话,可以在这里添加一些上下文信息
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        // 构建解释器表达式树:1 + 2 - 3 * 4
        Expression expression = new SubstractExpression(
            new AddExpression( 
	            new NumberExpression(1), 
	            new NumberExpression(2) 
            ), 
            new MultiplyExpression( 
	            new NumberExpression(3), 
	            new NumberExpression(4) 
            )
        );

        // 创建上下文对象
        Context context = new Context();

        // 解释表达式并输出结果
        int result = expression.interpret(context);
        System.out.println("Result: " + result); // 输出:-9
    }
}

解释器模式是一种在特定场景下非常有用的设计模式,它可以帮助我们构建简单的语言解释器,实现对特定语言的解析和执行。通过将语言表示为抽象语法树,并提供一种递归的解释方式,解释器模式使得对复杂语言的处理变得简单、清晰。


中介者模式

中介者模式(Mediator Pattern)是一种行为设计模式,它允许对象之间通过一个中介对象进行通信,而不直接相互引用。这种模式可以减少对象之间的耦合,使系统更易于维护和扩展。

角色介绍

  • Mediator(中介者):定义了一个接口用于与各个同事对象通信。
  • ConcreteMediator(具体中介者):实现了中介者接口,负责协调各个同事对象之间的交互。
  • Colleague(同事):定义了一个接口用于与中介者进行通信。
  • ConcreteColleague(具体同事):实现了同事接口,每个具体同事对象都知道中介者对象,并通过中介者对象来与其他同事对象通信。

应用示例

考虑一个简单的聊天室系统,其中有多个用户可以在聊天室中发送消息。我们可以使用中介者模式来实现该聊天室系统。

// 中介者接口
interface ChatMediator {
    void sendMessage(String message, User user);
}

// 具体中介者类
class ChatRoom implements ChatMediator {
    @Override
    public void sendMessage(String message, User user) {
        System.out.println(user.getName() + " sends message: " + message);
    }
}

// 同事接口
abstract class User {
    protected ChatMediator mediator;
    protected String name;

    public User(ChatMediator mediator, String name) {
        this.mediator = mediator;
        this.name = name;
    }

    public abstract void sendMessage(String message);
    public abstract void receiveMessage(String message);

    public String getName() {
        return name;
    }
}

// 具体同事类
class BasicUser extends User {
    public BasicUser(ChatMediator mediator, String name) {
        super(mediator, name);
    }

    @Override
    public void sendMessage(String message) {
        mediator.sendMessage(message, this);
    }

    @Override
    public void receiveMessage(String message) {
        System.out.println(name + " receives message: " + message);
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        ChatMediator mediator = new ChatRoom();

        User user1 = new BasicUser(mediator, "Alice");
        User user2 = new BasicUser(mediator, "Bob");

        mediator.sendMessage("Hello, everyone!", user1);
        user2.sendMessage("Hi, Alice!");
    }
}

中介者模式是一种非常有用的设计模式,它可以帮助我们降低系统中对象之间的耦合度,提高代码的可维护性和可扩展性。通过引入一个中介者对象来管理对象之间的通信,中介者模式使得系统更易于理解和修改。


访问者模式

访问者模式(Visitor Pattern)是一种行为设计模式,用于在不修改现有代码的情况下,向对象结构中添加新操作。该模式将操作封装到访问者对象中,并在对象结构中的每个元素上应用访问者,从而实现新的操作。

角色介绍

  • Visitor(访问者):定义了一个访问方法,用于接受不同类型元素的访问。
  • ConcreteVisitor(具体访问者):实现了访问者接口,提供了对元素的具体访问逻辑。
  • Element(元素):定义了一个接受访问者的方法,用于接受访问者的访问。
  • ConcreteElement(具体元素):实现了元素接口,提供了对访问者的具体接受方法。
  • ObjectStructure(对象结构):维护了一个元素的集合,并提供了一个接受访问者的方法。

应用示例

假设我们有一个简单的图形界面系统,其中包含不同类型的图形元素,如圆形、矩形等。我们希望对这些图形元素进行不同的操作,比如计算面积、绘制等。我们可以使用访问者模式来实现这个功能。

// 访问者接口
interface ShapeVisitor {
    void visit(Circle circle);
    void visit(Rectangle rectangle);
}

// 具体访问者类
class AreaVisitor implements ShapeVisitor {
    @Override
    public void visit(Circle circle) {
        double area = Math.PI * Math.pow(circle.getRadius(), 2);
        System.out.println("Area of circle: " + area);
    }

    @Override
    public void visit(Rectangle rectangle) {
        double area = rectangle.getWidth() * rectangle.getHeight();
        System.out.println("Area of rectangle: " + area);
    }
}

// 元素接口
interface Shape {
    void accept(ShapeVisitor visitor);
}

// 具体元素类
class Circle implements Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double getRadius() {
        return radius;
    }

    @Override
    public void accept(ShapeVisitor visitor) {
        visitor.visit(this);
    }
}

class Rectangle implements Shape {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    public double getWidth() {
        return width;
    }

    public double getHeight() {
        return height;
    }

    @Override
    public void accept(ShapeVisitor visitor) {
        visitor.visit(this);
    }
}

// 对象结构类
class ShapeCollection {
    private List<Shape> shapes = new ArrayList<>();

    public void addShape(Shape shape) {
        shapes.add(shape);
    }

    public void accept(ShapeVisitor visitor) {
        for (Shape shape : shapes) {
            shape.accept(visitor);
        }
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        ShapeCollection collection = new ShapeCollection();
        collection.addShape(new Circle(5));
        collection.addShape(new Rectangle(3, 4));

        ShapeVisitor areaVisitor = new AreaVisitor();
        collection.accept(areaVisitor);
    }
}

访问者模式是一种非常有用的设计模式,它可以帮助我们在不修改现有代码的情况下,向对象结构中添加新的操作。通过将操作封装到访问者对象中,并在对象结构中的每个元素上应用访问者,访问者模式使得代码更加灵活和易于扩展。


备忘录模式

备忘录模式(Memento Pattern)是一种行为设计模式,用于捕获对象的内部状态,并在不破坏对象封装性的情况下将其保存在外部,并在需要时恢复对象状态。这种模式有助于实现撤销操作和历史记录功能。

角色介绍

  • Originator(发起人):负责创建备忘录对象,并可以使用备忘录对象恢复自身状态。
  • Memento(备忘录):负责存储发起人对象的内部状态。
  • Caretaker(管理者):负责保存备忘录对象,并在需要时将其返回给发起人对象。

应用示例

假设我们有一个简单的文本编辑器,用户可以在其中输入文本,并且可以通过撤销功能回到之前的状态。我们可以使用备忘录模式来实现这个功能。

// 备忘录类
class TextMemento {
    private final String text;

    public TextMemento(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
}

// 发起人类
class TextEditor {
    private String text;

    public void setText(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }

    public TextMemento save() {
        return new TextMemento(text);
    }

    public void restore(TextMemento memento) {
        this.text = memento.getText();
    }
}

// 管理者类
class History {
    private Stack<TextMemento> stack = new Stack<>();

    public void push(TextMemento memento) {
        stack.push(memento);
    }

    public TextMemento pop() {
        return stack.pop();
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        History history = new History();

        editor.setText("Hello, world!");
        history.push(editor.save());
        System.out.println("Current text: " + editor.getText());

        editor.setText("Java Design Patterns");
        history.push(editor.save());
        System.out.println("Current text: " + editor.getText());

        // 撤销一次
        editor.restore(history.pop());
        System.out.println("Current text after undo: " + editor.getText());
    }
}
  • 在这个示例中,TextEditor 类充当了发起人角色,负责创建备忘录对象并保存和恢复状态。
  • TextMemento 类充当了备忘录角色,负责存储发起人对象的内部状态。
  • History 类充当了管理者角色,负责保存备忘录对象,并在需要时将其返回给发起人对象。

备忘录模式是一种非常有用的设计模式,它可以帮助我们实现撤销操作和历史记录功能,而不破坏对象的封装性。通过将对象的状态保存在外部,并在需要时恢复状态,备忘录模式使得程序更加灵活和易于维护。