本文實(shí)例代碼:https://github.com/JamesZBL/java_design_patterns
備忘錄模式(Memento),別名為快照模式(Snapshot)蟀苛,是的行為型模式的一種召边。它的主要特點(diǎn)是創(chuàng)建一個(gè)特殊對(duì)象用于保存其他若干個(gè)對(duì)象在某一刻的狀態(tài),以便在需要獲得該狀態(tài)的時(shí)候能夠及時(shí)恢復(fù)汰蜘。
實(shí)例
一粒種子從被播種到發(fā)芽再到開花會(huì)經(jīng)歷很長(zhǎng)的時(shí)間灼狰,雖然我們用肉眼看不出來它在長(zhǎng)大掩幢,但它的確每時(shí)每刻都在生長(zhǎng)或油。植物生長(zhǎng)過程中的每一刻的狀態(tài)都是不完全相同的寞忿,所以我們可以用一個(gè)“快照”來保存植物的若干個(gè)狀態(tài)。
我們暫時(shí)不考慮植物學(xué)上各種微觀的狀態(tài)顶岸,所以簡(jiǎn)單定義一下植物腔彰,它的狀態(tài)由高度和重量組成叫编,同時(shí),植物區(qū)分不同的生長(zhǎng)階段:
Plant.java
public interface Plant {
int getWeight();
int getHeight();
FlowerType getType();
}
定義一個(gè)花朵類 Flower
霹抛,它實(shí)現(xiàn)植物 Plant
接口宵溅,以獲得其生長(zhǎng)過程中某一時(shí)刻的狀態(tài),同時(shí)花朵可以生長(zhǎng)上炎,為了模擬生長(zhǎng)過程,我們自定義一個(gè)生長(zhǎng)速度計(jì)算公式雏搂,并且每次調(diào)用 growing
方法都會(huì)使花朵的階段向下移階段跳轉(zhuǎn)藕施,比如花朵處于種子階段的時(shí)候,生長(zhǎng)一次就處于了 “花苞” 的階段凸郑。
模擬花朵生長(zhǎng)的方法:
public void growing() {
setWeight(getWeight() * 2);
setHeight(getHeight() * 3);
switch (type) {
case SEED: {
setType(FlowerType.BURGEON);
break;
}
case BURGEON: {
setType(FlowerType.BUD);
break;
}
case BUD: {
setType(FlowerType.BLOOM);
break;
}
case BLOOM: {
setType(FlowerType.DEAD);
setHeight(0);
setWeight(0);
break;
}
default:
break;
}
}
在花朵類中定義一個(gè)私有的靜態(tài)內(nèi)部類 FlowerMemento
裳食,這個(gè)類負(fù)責(zé)記錄花朵的生長(zhǎng)狀態(tài):
private static class FlowerMemento implements Plant {
private FlowerType type;
private int height;
private int weight;
private FlowerMemento(FlowerType type, int height, int weight) {
this.type = type;
this.height = height;
this.weight = weight;
}
@Override
public int getWeight() {
return weight;
}
@Override
public int getHeight() {
return height;
}
@Override
public FlowerType getType() {
return type;
}
}
用一個(gè)枚舉類來定義花朵的生長(zhǎng)階段:
FlowerType.java
public enum FlowerType {
SEED("種子"), BURGEON("發(fā)芽"), BUD("花苞"), BLOOM("開放"), DEAD("凋零");
private String name;
FlowerType(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
為了便于理清這幾個(gè)類之間的關(guān)系,現(xiàn)在給出完整的 Flower
類:
Flower.java
public class Flower implements Plant {
private FlowerType type;
private final String name;
private int height;
private int weight;
public void growing() {
// 上文給出了完整的此方法
}
FlowerMemento getMemento() {
return new FlowerMemento(getType(), getHeight(), getWeight());
}
void setMemento(Plant plant) {
FlowerMemento flowerMemento = (FlowerMemento) plant;
setType(flowerMemento.getType());
setHeight(flowerMemento.getHeight());
setWeight(flowerMemento.getWeight());
}
@Override
public String toString() {
return String.format("名稱:%s\t狀態(tài):%s\t質(zhì)量:%d克\t高度:%d厘米", getName(), getType(), getWeight(), getHeight());
}
public Flower(FlowerType type, String name, int height, int weight) {
this.type = type;
this.name = name;
this.height = height;
this.weight = weight;
}
// getter & setter ...
private static class FlowerMemento implements Plant {
private FlowerType type;
private int height;
private int weight;
private FlowerMemento(FlowerType type, int height, int weight) {
this.type = type;
this.height = height;
this.weight = weight;
}
// getter & setter ...
}
}
最后模擬一下花朵的生長(zhǎng)過程:
App.java
public class Application {
private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
Flower flower = new Flower(FlowerType.SEED, "水仙花", 1, 2);
LOGGER.info(flower.toString());
flower.growing();
LOGGER.info(flower.toString());
flower.growing();
LOGGER.info(flower.toString());
flower.growing();
LOGGER.info(flower.toString());
flower.growing();
LOGGER.info(flower.toString());
flower.growing();
LOGGER.info(flower.toString());
}
}
總結(jié)
通過這個(gè)例子可以總結(jié)出芙沥,備忘錄模式的主要特點(diǎn)有:
在不修改被捕獲對(duì)象的原有狀態(tài)前提下诲祸,抓取一個(gè)對(duì)象的內(nèi)部狀態(tài),并在獨(dú)立于該對(duì)象的對(duì)象中保存被捕獲對(duì)象的狀態(tài)而昨。所以應(yīng)該使用靜態(tài)內(nèi)部類作為快照保存的實(shí)現(xiàn)救氯,因?yàn)閷?duì)象的狀態(tài)和對(duì)象沒有直接緊密的聯(lián)系,而是相對(duì)的獨(dú)立聯(lián)系歌憨。
為了保證狀態(tài)持有者的數(shù)據(jù)不允許被除了 “被捕獲對(duì)象” 之外的對(duì)象訪問到着憨,應(yīng)當(dāng)將狀態(tài)持有者的類定義為 “被捕獲對(duì)象類” 的私有類。
在許多的軟件中务嫡,都需要保存當(dāng)前工作進(jìn)度的功能甲抖,所以這些正是備忘錄模式的使用場(chǎng)景:
游戲軟件中的存檔
字處理軟件(比如 MS Office)中 “撤銷上一步” 的操作
瀏覽器中的返回上一頁
數(shù)據(jù)庫(kù)中的事務(wù)回滾
為了避免每次保存狀態(tài)和恢復(fù)狀態(tài)耗費(fèi)較多內(nèi)存資源,可以將備忘錄模式和之前的文章中提到的原型模式結(jié)合使用心铃。
個(gè)人博客同步更新准谚,獲取更多技術(shù)分享請(qǐng)關(guān)注:鄭保樂的博客