1はじめに
誰もが間違いを犯し、間違いを埋め合わせてやり直せるような「後悔の薬」があることを望んでいますが、現実は残酷です。コンピュータアプリケーションでは、顧客も間違いを犯すことがよくありますが、「後悔薬」を提供できますか?もちろん、それは可能であり、必要です。この機能は「メモモード」で実現しています。
実際、編集中にCtrl + Zキーの組み合わせを押すと、Word、Notepad、Photoshop、Eclipseなどの多くのアプリケーションソフトウェアがこの機能を提供し、現在の操作を元に戻し、ドキュメントを以前の状態に復元できます。 IEの場合データベースの戻るボタン、データベーストランザクション管理のロールバック操作、ゲームプレイ時の中間結果のアーカイブ機能、データベースとオペレーティングシステムのバックアップ操作、チェスゲームの後悔機能などがこれに属します。カテゴリー。
メモモードでは、オブジェクトの内部状態を記録することができ、ユーザーが後悔した場合は、現在の操作を元に戻し、データを元の状態に戻すことができます。
2.モデルの定義と特徴
Mementoモードの定義:カプセル化を破壊しないことを前提として、オブジェクトの内部状態をキャプチャし、この状態をオブジェクトの外部に保存して、必要なときにオブジェクトを元の状態に復元できるようにします。このモードはスナップショットモードとも呼ばれます。
メモモードはオブジェクト動作モードであり、その主な利点は次のとおりです。
- 状態を復元するメカニズムを提供します。ユーザーが必要なときに、データを特定の履歴状態に簡単に復元できます。
- 内部状態のカプセル化が実現されます。それを作成したイニシエーターを除いて、他のオブジェクトはこの状態情報にアクセスできません。
- 単純化された開始人間。イニシエーターは、内部状態の個々のバックアップを管理および保存する必要はありません。すべての状態情報はメモに保存され、マネージャーによって管理されます。これは、単一責任の原則に準拠しています。
主な欠点は、リソースの消費量が多いことです。保存する内部状態情報が多すぎるか非常に頻繁である場合、比較的大きなメモリリソースを消費します。
3.パターンの構造と実現
メモパターンの中核は、メモクラスとメモの管理に使用するマネージャクラスを設計することです。それでは、その構造と実装について学びましょう。
1.モデルの構造
メモモードの主な役割は次のとおりです。
- 発信者の役割:現在の内部状態情報を記録し、メモの作成やメモデータの復元などの機能を提供し、その他のビジネス機能を実現し、メモ内のすべての情報にアクセスできます。
- Mementoの役割:イニシエーターの内部状態を保存し、必要に応じてこれらの内部状態をイニシエーターに提供する責任があります。
- マネージャー(Caretaker)の役割:メモを管理し、メモを保存および取得する機能を提供しますが、メモの内容にアクセスして変更することはできません。
2.パターンの実装
package net.biancheng.c.memento;
public class MementoPattern {
public static void main(String[] args) {
Originator or = new Originator();
Caretaker cr = new Caretaker();
or.setState("S0");
System.out.println("初始状态:" + or.getState());
cr.setMemento(or.createMemento()); //保存状态
or.setState("S1");
System.out.println("新的状态:" + or.getState());
or.restoreMemento(cr.getMemento()); //恢复状态
System.out.println("恢复状态:" + or.getState());
}
}
//备忘录
class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
//发起人
class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
public Memento createMemento() {
return new Memento(state);
}
public void restoreMemento(Memento m) {
this.setState(m.getState());
}
}
//管理者
class Caretaker {
private Memento memento;
public void setMemento(Memento m) {
memento = m;
}
public Memento getMemento() {
return memento;
}
}
初期状態:S0
新しい状態:S1
回復状態:S0
4.パターンの適用例
【例1】メモモードでブラインドデートゲームをデザインします。
分析:西施、王昭君、貂蝉、楊貴妃とのブラインドデートがある場合は、そのうちの1つを恋人として選択できます。もちろん、前の選択に満足できない場合は、もう一度選択できます。しかし、気を散らさないでください。このゲームは後悔機能を備えており、「メモモード」デザインを使用する方が適切です。
まず、美容情報を取得して保存する機能を提供するメモロールである女の子カテゴリを設計し、次に、現在の瞬間の内部状態を記録するイニシエータロールであるブラインドデート(あなた)カテゴリを設計します。情報(仮の妻の名前)、メモの作成とメモデータの復元の機能を提供します。最後に、ブラインドデートを保存するために使用される、メモの管理を担当するマネージャーの役割であるガールスタック(GirlStack)クラスを定義します。 (あなた)以前に選択した美容情報ですが、最大4つしか保存できず、後悔機能を備えています。
カスタマークラスは、GirlStackオブジェクトとブラインドデート(You)オブジェクトを含むウィンドウプログラムとして設計されており、ActionListenerインターフェイスのイベント処理メソッドactionPerformed(ActionEvent e)を実装し、4つのビューティーイメージとブラインドデートを組み合わせています。 (あなた))選択した美容画像がウィンドウに表示されます。図2にその構造を示します。
package net.biancheng.c.memento;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class DatingGame {
public static void main(String[] args) {
new DatingGameWin();
}
}
//客户窗体类
class DatingGameWin extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
JPanel CenterJP, EastJP;
JRadioButton girl1, girl2, girl3, girl4;
JButton button1, button2;
String FileName;
JLabel g;
You you;
GirlStack girls;
DatingGameWin() {
super("利用备忘录模式设计相亲游戏");
you = new You();
girls = new GirlStack();
this.setBounds(0, 0, 900, 380);
this.setResizable(false);
FileName = "src/memento/Photo/四大美女.jpg";
g = new JLabel(new ImageIcon(FileName), JLabel.CENTER);
CenterJP = new JPanel();
CenterJP.setLayout(new GridLayout(1, 4));
CenterJP.setBorder(BorderFactory.createTitledBorder("四大美女如下:"));
CenterJP.add(g);
this.add("Center", CenterJP);
EastJP = new JPanel();
EastJP.setLayout(new GridLayout(1, 1));
EastJP.setBorder(BorderFactory.createTitledBorder("您选择的爱人是:"));
this.add("East", EastJP);
JPanel SouthJP = new JPanel();
JLabel info = new JLabel("四大美女有“沉鱼落雁之容、闭月羞花之貌”,您选择谁?");
girl1 = new JRadioButton("西施", true);
girl2 = new JRadioButton("貂蝉");
girl3 = new JRadioButton("王昭君");
girl4 = new JRadioButton("杨玉环");
button1 = new JButton("确定");
button2 = new JButton("返回");
ButtonGroup group = new ButtonGroup();
group.add(girl1);
group.add(girl2);
group.add(girl3);
group.add(girl4);
SouthJP.add(info);
SouthJP.add(girl1);
SouthJP.add(girl2);
SouthJP.add(girl3);
SouthJP.add(girl4);
SouthJP.add(button1);
SouthJP.add(button2);
button1.addActionListener(this);
button2.addActionListener(this);
this.add("South", SouthJP);
showPicture("空白");
you.setWife("空白");
girls.push(you.createMemento()); //保存状态
}
//显示图片
void showPicture(String name) {
EastJP.removeAll(); //清除面板内容
EastJP.repaint(); //刷新屏幕
you.setWife(name);
FileName = "src/memento/Photo/" + name + ".jpg";
g = new JLabel(new ImageIcon(FileName), JLabel.CENTER);
EastJP.add(g);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
@Override
public void actionPerformed(ActionEvent e) {
boolean ok = false;
if (e.getSource() == button1) {
ok = girls.push(you.createMemento()); //保存状态
if (ok && girl1.isSelected()) {
showPicture("西施");
} else if (ok && girl2.isSelected()) {
showPicture("貂蝉");
} else if (ok && girl3.isSelected()) {
showPicture("王昭君");
} else if (ok && girl4.isSelected()) {
showPicture("杨玉环");
}
} else if (e.getSource() == button2) {
you.restoreMemento(girls.pop()); //恢复状态
showPicture(you.getWife());
}
}
}
//备忘录:美女
class Girl {
private String name;
public Girl(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
//发起人:您
class You {
private String wifeName; //妻子
public void setWife(String name) {
wifeName = name;
}
public String getWife() {
return wifeName;
}
public Girl createMemento() {
return new Girl(wifeName);
}
public void restoreMemento(Girl p) {
setWife(p.getName());
}
}
//管理者:美女栈
class GirlStack {
private Girl girl[];
private int top;
GirlStack() {
girl = new Girl[5];
top = -1;
}
public boolean push(Girl p) {
if (top >= 4) {
System.out.println("你太花心了,变来变去的!");
return false;
} else {
girl[++top] = p;
return true;
}
}
public Girl pop() {
if (top <= 0) {
System.out.println("美女栈空了!");
return girl[0];
} else return girl[top--];
}
}
5.モードのアプリケーションシナリオ
メモモードの定義、特徴、構造、実装を学びました。それでは、このモードの以下のアプリケーションシナリオを見てみましょう。
- ゲームプレイ時の中間結果のアーカイブ機能など、データの保存・復元が必要なシーン。
- Word、メモ帳、Photoshop、Eclipse、その他のソフトウェアなど、ロールバック可能なシーンを提供する必要があります。編集時にCtrl + Zキーの組み合わせを押し、データベースでのトランザクション操作を行います。
6.パターンの拡大
先に紹介したメモモードには、シングルステートバックアップとマルチステートバックアップの例があります。以下では、メモモードとプロトタイプモードを組み合わせる方法について説明します。メモモードでは、「イニシエーター」情報は「メモ」を定義することでバックアップされ、プロトタイプモードのclone()メソッドには自己バックアップ機能があります。したがって、イニシエーターがクローン可能インターフェースの実装を許可されている場合、それ自体をバックアップする機能があります。メモカテゴリを削除できます。その構造を図4に示します。
package net.biancheng.c.memento;
public class PrototypeMemento {
public static void main(String[] args) {
OriginatorPrototype or = new OriginatorPrototype();
PrototypeCaretaker cr = new PrototypeCaretaker();
or.setState("S0");
System.out.println("初始状态:" + or.getState());
cr.setMemento(or.createMemento()); //保存状态
or.setState("S1");
System.out.println("新的状态:" + or.getState());
or.restoreMemento(cr.getMemento()); //恢复状态
System.out.println("恢复状态:" + or.getState());
}
}
//发起人原型
class OriginatorPrototype implements Cloneable {
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
public OriginatorPrototype createMemento() {
return this.clone();
}
public void restoreMemento(OriginatorPrototype opt) {
this.setState(opt.getState());
}
public OriginatorPrototype clone() {
try {
return (OriginatorPrototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
//原型管理者
class PrototypeCaretaker {
private OriginatorPrototype opt;
public void setMemento(OriginatorPrototype opt) {
this.opt = opt;
}
public OriginatorPrototype getMemento() {
return opt;
}
}
初期状態:S0
新しい状態:S1
回復状態:S0