定义
在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样,以后就可以将该对象恢复到原先保存的状态。
使用场景
- 需要保存一个对象在摸一个时刻的状态或部分状态
- 如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过中间对象可以直接访问其内部状态
结构
-
原发器 Originator
负责创建一个备忘录,可以记录、恢复自身的内部状态。同时 Originator 还可以根据需要决定 Memento 存储自身的哪些内部状态。
-
备忘录 Memento
备忘录角色,用于储存 Originator 内部状态,并可以防止 Originator 以外的对象访问 Memento
-
负责人 Caretaker
负责存储备忘录,不能对备忘录的内容进行操作和访问,只能够将备忘录传递给其他对象
简单实现
一个简单的便签功能Demo
/**
* @author jc
* @time 2018/4/30 下午5:59
* @desc 备忘录角色,确定需要保存的内部状态
*/
public class Memoto {
public String text;
public int cursor;
}
/**
* @author jc
* @time 2018/4/30 下午6:07
* @desc NoteEditText充当Originator角色,创建和恢复备忘录
*/
public class NoteEditText extends android.support.v7.widget.AppCompatEditText {
public NoteEditText(Context context) {
this(context, null);
}
public NoteEditText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public NoteEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 创建备忘录对象 存储编辑的信息
*
* @return
*/
public Memoto createMemoto() {
Memoto memoto = new Memoto();
memoto.text = getText().toString();
memoto.cursor = getSelectionStart();
return memoto;
}
/**
* 从备忘录中恢复数据
*
* @param memoto
*/
public void restore(Memoto memoto) {
setText(memoto.text);
setSelection(memoto.cursor);
}
}
/**
* @author jc
* @time 2018/4/30 下午5:56
* @desc 负责管理 Memoto 对象
*/
public class NoteCaretaker {
/**
* 最大存储数量
*/
private static final int MAX = 20;
List<Memoto> mMemotos = new ArrayList<>(MAX);
int mIndex = 0;
/**
* 保存备忘录记录到记录列表中
*
* @param memoto
*/
public void saveMemoto(Memoto memoto) {
if (mMemotos.size() > MAX) {
mMemotos.remove(0);
}
mMemotos.add(memoto);
mIndex = mMemotos.size() - 1;
}
/**
* 获取上一个存档信息
*
* @return
*/
public Memoto getPrevMemoto() {
mIndex = mIndex > 0 ? --mIndex : mIndex;
return mMemotos.get(mIndex);
}
/**
* 获取下一个存档信息,相当于恢复
*
* @return
*/
public Memoto getNextMemoto() {
mIndex = mIndex < mMemotos.size() - 1 ? ++mIndex : mIndex;
return mMemotos.get(mIndex);
}
}
public class MementoActivity extends AppCompatActivity implements View.OnClickListener {
NoteEditText noteEditText;
TextView mSavaTv;
TextView mUndoBtn;
TextView mRedoBtn;
NoteCaretaker noteCaretaker = new NoteCaretaker();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memento);
initViews();
}
private void initViews() {
noteEditText = findViewById(R.id.note_et);
mUndoBtn = findViewById(R.id.undo_btn);
mUndoBtn.setOnClickListener(this);
mRedoBtn = findViewById(R.id.redo_btn);
mRedoBtn.setOnClickListener(this);
mSavaTv = findViewById(R.id.save_tv);
mSavaTv.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.undo_btn:
noteEditText.restore(noteCaretaker.getPrevMemoto());
makeToast("撤销 : ");
break;
case R.id.redo_btn:
noteEditText.restore(noteCaretaker.getNextMemoto());
makeToast("恢复 : ");
break;
case R.id.save_tv:
noteCaretaker.saveMemoto(noteEditText.createMemoto());
makeToast("保存 : ");
break;
default:
break;
}
}
private void makeToast(String s) {
Toast.makeText(this, s + " 光标位置 :"
+ noteEditText.getSelectionStart(), Toast.LENGTH_SHORT).show();
Log.v(getClass().getSimpleName(), s + noteEditText.getText());
}
}
小结
备忘录模式的关键在于设计备忘录类和负责人类,需要防止原发器以外的其它对象访问备忘录
备忘录对象通常封装了原发器的部分或所有状态信息,而且这些信息不能被其他对象访问;也就是说不能在备忘录对象之外保存原发器的状态
原发器可以调用备忘录的所以信息,允许原发器访问先前状态所有数据
负责人只负责备忘录的保存并将备忘录对象传递给其他对象
其他对象只需要从负责人处取出备忘录对象并将原发器对象的状态恢复,而无需关心备忘录的保存细节
思考
- 备忘录模式与命令模式实现撤销时的异同