📜  使用备忘录的水壶问题(1)

📅  最后修改于: 2023-12-03 15:22:23.454000             🧑  作者: Mango

使用备忘录的水壶问题

介绍

使用备忘录模式(Memento Pattern)可以解决水壶问题。水壶问题是指有一只水壶,容量为8升,另有两个容量分别为3升和5升的瓶子,如何利用这两个瓶子得到恰好为4升的水。

备忘录模式是一种行为设计模式,它允许在不破坏封装性的前提下捕获对象的内部状态,并在对象外部保存这个状态供以后使用。本文将介绍如何使用备忘录模式解决水壶问题。

代码实现

下面是使用备忘录模式的水壶问题解决方案的代码实现。

首先定义备忘录类Memento,它保存当前水壶状态的各个参数:

public class Memento {
    private int aCapacity;
    private int bCapacity;
    private int aWater;
    private int bWater;

    public Memento(int aCapacity, int bCapacity, int aWater, int bWater) {
        this.aCapacity = aCapacity;
        this.bCapacity = bCapacity;
        this.aWater = aWater;
        this.bWater = bWater;
    }

    public int getACapacity() {
        return aCapacity;
    }

    public int getBCapacity() {
        return bCapacity;
    }

    public int getAWater() {
        return aWater;
    }

    public int getBWater() {
        return bWater;
    }
}

然后定义发起者类Kettle,它有两个成员变量ab,分别表示两个瓶子,它提供以下方法:

  • fillA():将瓶子a填满;
  • fillB():将瓶子b填满;
  • emptyA():将瓶子a倒空;
  • emptyB():将瓶子b倒空;
  • pourAToB():将瓶子a中的水倒入瓶子b;
  • pourBToA():将瓶子b中的水倒入瓶子a;
  • getState():获取当前水壶状态;
  • setState(Memento):设置当前水壶状态。
public class Kettle {
    private int a;
    private int b;

    public Kettle(int a, int b) {
        this.a = a;
        this.b = b;
    }

    public void fillA() {
        a = 8;
    }

    public void fillB() {
        b = 8;
    }

    public void emptyA() {
        a = 0;
    }

    public void emptyB() {
        b = 0;
    }

    public void pourAToB() {
        if (a + b <= 5) { // 8+0<=5
            b = a + b;
            a = 0;
        } else { // 8+0>5
            a = a - (5 - b);
            b = 5;
        }
    }

    public void pourBToA() {
        if (a + b <= 3) { // 0+8<=3
            a = a + b;
            b = 0;
        } else { // 0+8>3
            b = b - (3 - a);
            a = 3;
        }
    }

    public Memento getState() {
        return new Memento(8, 5, a, b);
    }

    public void setState(Memento m) {
        a = m.getAWater();
        b = m.getBWater();
    }
}

最后定义管理者类Caretaker,它保存备忘录对象,并提供以下方法:

  • addMemento(Memento):保存备忘录对象;
  • getMemento():获取最后一个备忘录对象。
import java.util.ArrayList;

public class Caretaker {
    private ArrayList<Memento> mementos = new ArrayList<>();

    public void addMemento(Memento m) {
        mementos.add(m);
    }

    public Memento getMemento() {
        if (mementos.size() > 0) {
            Memento m = mementos.get(mementos.size() - 1);
            mementos.remove(mementos.size() - 1);
            return m;
        } else {
            return null;
        }
    }
}

使用备忘录模式的水壶问题解决方案如下:

public class Main {
    public static void main(String[] args) {
        Kettle kettle = new Kettle(0, 0);
        Caretaker caretaker = new Caretaker();
        int a, b;

        // 将瓶子a中的水倒入瓶子b
        kettle.fillA();
        a = kettle.getState().getAWater();
        b = kettle.getState().getBWater();
        caretaker.addMemento(kettle.getState());
        kettle.pourAToB();
        a = kettle.getState().getAWater();
        b = kettle.getState().getBWater();
        caretaker.addMemento(kettle.getState());

        // 将瓶子a中的水倒入瓶子b
        kettle.emptyB();
        a = kettle.getState().getAWater();
        b = kettle.getState().getBWater();
        caretaker.addMemento(kettle.getState());
        kettle.pourAToB();
        a = kettle.getState().getAWater();
        b = kettle.getState().getBWater();
        caretaker.addMemento(kettle.getState());

        // 将瓶子b倒空
        kettle.emptyB();
        a = kettle.getState().getAWater();
        b = kettle.getState().getBWater();
        caretaker.addMemento(kettle.getState());

        // 将瓶子a中的水倒入瓶子b
        kettle.pourAToB();
        a = kettle.getState().getAWater();
        b = kettle.getState().getBWater();
        caretaker.addMemento(kettle.getState());

        // 将瓶子a中的水倒入瓶子b
        kettle.emptyB();
        a = kettle.getState().getAWater();
        b = kettle.getState().getBWater();
        caretaker.addMemento(kettle.getState());
        kettle.pourAToB();
        a = kettle.getState().getAWater();
        b = kettle.getState().getBWater();
        caretaker.addMemento(kettle.getState());

        // 恢复最近的一次备忘录
        kettle.setState(caretaker.getMemento());

        System.out.println("瓶子a中的水为:" + kettle.getState().getAWater());
        System.out.println("瓶子b中的水为:" + kettle.getState().getBWater());
    }
}
分析

使用备忘录模式解决水壶问题的关键在于保存每一次瓶子的状态,并在求解过程中需要回溯到过去的某个状态。这时候备忘录模式可以非常方便地帮我们实现这一功能。

具体来说,每当进行一步操作时,我们都可以将水壶的当前状态保存下来,然后当需要回溯到过去某个状态时,只需要从备忘录管理者Caretaker中获取上一个备忘录即可。

结论

使用备忘录模式可以很方便地解决水壶问题以及其他需要记录对象状态并支持回溯的问题。