相信没有哪个程序猿没用过 ctrl + Z 的吧,这是恢复上一次状态的快捷键,之所以能恢复到之前的状态,肯定之前的状态有所保存, Android 同样也可以实现保存之前的状态,最明显的例子就是 Activity 的 onSaveInstanceState() 方法,在看了备忘录模式之后,也就明白了这种保存历史状态的原理了。它也是一种行为模式,用于保存对象当前状态,并且在之后可以再次恢复到此状态。备忘录模式实现的方式需要保证被保存的对象状态不能被对象从外部访问,目的是为了保护好被保存的状态的完整性以及内部实现不向外暴露。
第十三章 编程中的“后悔药”——备忘录模式
1.定义
在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复到之前保存的状态。
2.使用场景
1).需要保存一个对象在某一个时刻的状态或部分状态。
2).如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问,而是通过中间对象间接访问内部状态。
3.简单实现
以一个可以撤销输入的文本框为例。
xml布局如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.qinshou.mementopatterndemo.MainActivity">
<EditText
android:id="@+id/et_note"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="top" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_previous"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@null"
android:text="撤销" />
<Button
android:id="@+id/btn_save"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@null"
android:text="保存" />
</LinearLayout>
</LinearLayout>
创建一个备忘录类,用来存储需要保存的文字和下标:
public class Memento {
public String note;
public int cursorIndex;
public Memento(String note, int cursorIndex) {
this.note = note;
this.cursorIndex = cursorIndex;
}
}
创建一个管理历史状态的“记录者”:
public class NoteRecorder {
private static final int MAX = 30;
List<Memento> mementoList = new ArrayList<>(MAX);
public void saveMemento(Memento memento) {
if (mementoList.size() > MAX) {
//大于最大数量的时候移除第一个状态
mementoList.remove(0);
}
mementoList.add(memento);
}
public Memento getPreviousMemento() {
if (mementoList.size() > 0) {
//返回最新的一个状态并移除,达到多次撤销的目的
Memento mMemento = mementoList.get(mementoList.size() - 1);
mementoList.remove(mementoList.size() - 1);
return mMemento;
}
return null;
}
}
public class MainActivity extends AppCompatActivity {
private NoteRecorder mNoteRecorder;
private EditText etNote;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etNote = (EditText) findViewById(R.id.et_note);
Button btnPrevious = (Button) findViewById(R.id.btn_previous);
Button btnSave = (Button) findViewById(R.id.btn_save);
mNoteRecorder = new NoteRecorder();
btnPrevious.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Memento mMemento = mNoteRecorder.getPreviousMemento();
if (mMemento != null) {
Log.i("daolema", "mMemento:" + mMemento.note);
etNote.setText(mMemento.note);
etNote.setSelection(mMemento.cursorIndex);
}
}
});
btnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mNoteRecorder.saveMemento(new Memento(etNote.getText().toString(), etNote.getSelectionEnd()));
Toast.makeText(MainActivity.this, etNote.getText().toString() + "--->保存成功", Toast.LENGTH_SHORT).show();
}
});
}
}
运行程序,需要在输入后点击“保存”按钮才能保存一次状态:
点击“撤销”按钮,可以看到依次恢复了之前保存的每次的状态:
4.总结
备忘录模式在不破坏封装的条件下通过备忘录对象存储另外一个对象内部状态,并可以在需要的时候把这个对象还原到储存起来的状态。备忘录模式并不是持久性存储,在 app 被杀死后重新打开并不能恢复之前状态,如果在 app 关闭时还需要保存状态则要用到数据持久化存储,如共享参数、文件存储等等。
优点:
1).给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史状态。
2).实现了信息的封装,使用户不用关心状态的保存细节。
缺点:
1).消耗资源,如果类成员变多,则会占用比较大的资源,而且每一次保存也会消耗一定内存。