备忘录模式:如何兼顾Memento对象的封装性与复用性?
备忘录模式:封装与复用的解决方案
问题代码
class Originator { private String state; Originator(String state) { this.state = state; } public void setState(String newState) { this.state = newState; } public Memento createMemento() { return new Memento(state); } public void restoreState(Memento memento) { // HOW to access private state of memento?? } } class Memento { private String state; Memento(String state) { this.state = state; } } class CareTaker { public static void main(String[] args) { Originator originator = new Originator("state"); Memento memento = originator.createMemento(); originator.setState("new state"); originator.restoreState(memento); } }
问题描述
实现备忘录模式时,将Memento类的state设为私有以阻止Caretaker访问,但导致Originator也无法访问Memento的私有state。曾考虑三种方案但均有弊端:
- 将Memento的state设为公有:违背内部状态封装原则。
- 将Memento设为Originator内部类:Memento会与Originator绑定,无法被其他Originator复用。
- Memento构造方法传入Originator引用:Memento会绑定单个Originator对象,无法复用。
需要一种既能遵循封装原则,又支持Memento对象复用的解决方案。
解决方案:接口隔离+包级访问控制
这种方案通过接口隐藏Memento的内部细节,同时利用包级访问权限让Originator合法访问状态,既保证封装性,又支持Memento跨Originator复用。
代码实现
// 对外暴露的空接口,Caretaker仅能持有该类型,无法访问内部状态 interface Memento { } class Originator { private String state; Originator(String state) { this.state = state; } public void setState(String newState) { this.state = newState; } public Memento createMemento() { return new ConcreteMemento(state); } public void restoreState(Memento memento) { // 向下转型为具体实现类,同包下可访问其包级方法 this.state = ((ConcreteMemento) memento).getState(); } } // 包级访问的具体备忘录类,仅同包内的类可访问getState方法 class ConcreteMemento implements Memento { private String state; ConcreteMemento(String state) { this.state = state; } // 包级权限方法,仅同包类可调用 String getState() { return state; } } class CareTaker { public static void main(String[] args) { Originator originator = new Originator("state"); Memento memento = originator.createMemento(); originator.setState("new state"); originator.restoreState(memento); } }
方案说明
- 接口隔离:对外仅暴露
Memento空接口,Caretaker只能持有该接口实例,无法直接访问ConcreteMemento的内部状态或方法,保证了封装性。 - 包级访问控制:
ConcreteMemento及其getState方法均为包级权限,同包内的所有Originator类(包括不同的Originator实现)都可以调用该方法恢复状态,实现了Memento的复用。 - 灵活性:如果需要跨包复用,可将
ConcreteMemento的访问权限调整为protected,并让跨包的Originator继承自同一个基类,或者利用Java 9+的模块系统指定友元模块开放权限,包级访问是最简洁的通用实现方式。
内容的提问来源于stack exchange,提问作者Nishant Chauhan
相关产品推荐
相关产品推荐

