ページ 11

Javaのmementoパターンについて

Posted: 2016年12月25日(日) 02:17
by gweikty
javaの課題でmementoパターンを使ってBlogEditorクラスにredo機能を実装しなければならないのですが、
redoの実装方法が全くわかりません。ネットで調べてもredo機能について解説しているサイトがありませんでした。
redoの実装方法について教えてください。
よろしくお願いします
環境:
OS : Windows
IDE : Eclipse

以下がBlogEditorクラスのソースコードです。

コード:

package lesson;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Stack;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.WindowConstants;

import lesson.UndoableEditorPane.Memento;

/**
 * Mementoパターンを使ってUndoを実装したエディターです。
 * 
 * [課題] このエディタにRedo機能を実装してください。(Redo: Undoで戻した動作を再度行う)
 * Redoの実行のさせ方はRedoボタンをつけてもキーボードショートカットなどに割り当ててもどのような方法でも大丈夫です。
 */
public class BlogEditor extends JFrame {

	private JTextField titleField;
	private JTextArea bodyArea;
	private UndoableEditorPane editorPane;

	/**
	 * Undoを保持するスタック
	 */
	private Stack<Memento> undoStack;

	/**
	 * Undo(やり直し)情報を保存するを実行する
	 */
	private void saveUndo() {
		undoStack.push(editorPane.createMemento());
	}

	/**
	 * Undo(やり直し)を実行する
	 */
	private void performUndo() {
		if (undoStack.isEmpty())
			return;

		Memento mem = undoStack.pop();
		editorPane.restoreMemento(mem);
	}

	public BlogEditor() {

		initComponent();
		undoStack = new Stack<Memento>();
	}

	private void initComponent() {

		JPanel leftPanel = new JPanel();

		titleField = new JTextField("HTML Editor");
		bodyArea = new JTextArea();
		bodyArea.setText("<b>Hello</b> <i>HTML!</i>");

		JButton saveButton = new JButton("Update");
		saveButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				saveUndo(); // undo情報を保存
				editorPane.set(titleField.getText(), bodyArea.getText()); // テキストをセット
				editorPane.updateView(); // HTMLビューを更新
			}
		});

		JButton undoButton = new JButton("Undo");
		undoButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				performUndo(); // Undoを実行
				titleField.setText(editorPane.getTitle()); // titleのテキストを取得
				bodyArea.setText(editorPane.getBody()); // bodyのテキストを取得
				editorPane.updateView(); // HTMLビューを更新
			}
		});

		leftPanel.setLayout(new BorderLayout());
		leftPanel.add(titleField, BorderLayout.NORTH);
		leftPanel.add(new JScrollPane(bodyArea), BorderLayout.CENTER);

		// ボタンを追加
		JPanel buttons = new JPanel();
		buttons.add(undoButton);
		buttons.add(saveButton);

		leftPanel.add(buttons, BorderLayout.SOUTH);

		editorPane = new UndoableEditorPane();
		editorPane.set(titleField.getText(), bodyArea.getText());

		JSplitPane jsp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		jsp.setResizeWeight(0.5);
		jsp.setLeftComponent(leftPanel);
		jsp.setRightComponent(new JScrollPane(editorPane));

		add(jsp);
		editorPane.set(titleField.getText(), bodyArea.getText()); // テキストをセット
		editorPane.updateView(); // HTMLビューを更新

	}

	public static void main(String[] args) {
		BlogEditor be = new BlogEditor();
		be.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		be.setBounds(100, 100, 960, 480);
		be.setVisible(true);
	}

	/**
	 * SerialVersionUID
	 */
	private static final long serialVersionUID = 1508601814645429086L;

}
以下はUndoableEditorPaneクラスです。

コード:

package lesson;

import javax.swing.JEditorPane;

/**
 * このクラスは変更する必要ありません
 */
@SuppressWarnings("serial")
class UndoableEditorPane extends JEditorPane {

	/**
	 * 自身の状態を保存するためのメメント(思い出)クラス
	 */
	static class Memento { // クラスのアクセス制御はパッケージアクセス
		private String title;
		private String body;

		public Memento(String title, String body) {
			this.title = title;
			this.body = body;
		}

		public String getTitle() {
			return title;
		}

		public String getBody() {
			return body;
		}
	}

	private String title;
	private String body;

	public UndoableEditorPane() {
		super("text/html", "");
		setEditable(false);
		setText("");
	}

	public Memento createMemento() {
		Memento mem = new Memento(title, body);
		return mem;
	}

	public void restoreMemento(Memento mem) {
		this.title = mem.title;
		this.body = mem.body;
	}

	public void set(String title, String body) {
		this.title = title;
		this.body = body;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getTitle() {
		return title;
	}

	public void setBody(String body) {
		this.body = body;
	}

	public String getBody() {
		return body;
	}

	public void updateView() {
		StringBuilder buf = new StringBuilder();

		if (!title.isEmpty()) {
			buf.append("<h1>").append(title).append("</h1>");
		}

		buf.append("<div>");
		buf.append(body);
		buf.append("</div>");

		setText(buf.toString());
	}

}

Re: Javaのmementoパターンについて

Posted: 2016年12月25日(日) 09:02
by みけCAT
現状のシステムだと、履歴がスタックに積まれていき

コード:

[1][2][3][4][5]
             ↑
Undoをすると履歴を捨てるようですね。

コード:

[1][2][3][4]
          ↑
Redoできるようにするには、まず戻した所の履歴を捨てないようにしないといけません。
一口にRedoと言ってもソフトによって様々な仕様がありそうですが、
例えば新たな操作をした時にUndoした履歴を捨ててその操作を記録するようにするといいでしょう。

コード:

[1][2][3][4][5]
          ↑
[1][2][3][4][5]
       ↑
[1][2][3][6]
          ↑
Stackを用いる場合、この図のように1個の配列で管理するのではなく、
Undoした履歴を別のスタックに入れておくといいでしょう。

コード:

操作履歴:[1][2][3][4][5]
Undo履歴:

操作履歴:[1][2][3][4]
Undo履歴:[5]

操作履歴:[1][2][3]
Undo履歴:[5][4]

操作履歴:[1][2][3][6]
Undo履歴:

Re: Javaのmementoパターンについて

Posted: 2017年1月15日(日) 14:32
by gweikty
返信が遅れてすいません。
解答ありがとうございます。