練習課題
概要
本課題では、図形描画処理においてユーザーの操作履歴を保持せず、
「取り消し(Undo)」や「やり直し(Redo)」といった操作ができない構造に対して、
状態の保存と巻き戻しを可能にする設計改善を検討する。
課題コード
以下は、DrawService
がキャンバス上に図形(circle, square など)を描画する処理である。
操作内容や状態の履歴は保持しておらず、ユーザー操作を巻き戻したり再実行することができない。
- TypeScript
- PHP
- Python
class DrawService {
private canvas: string[] = [];
draw(shape: string) {
this.canvas.push(shape);
console.log(`描画: ${shape}`);
}
showCanvas() {
console.log("現在のキャンバス:", this.canvas.join(", "));
}
}
// 利用例
const drawer = new DrawService();
drawer.draw("circle");
drawer.draw("square");
drawer.showCanvas(); // circle, square
<?php
class DrawService {
private array $canvas = [];
public function draw(string $shape): void {
$this->canvas[] = $shape;
echo "描画: {$shape}\n";
}
public function showCanvas(): void {
echo "現在のキャンバス: " . implode(", ", $this->canvas) . "\n";
}
}
// 利用例
$drawer = new DrawService();
$drawer->draw("circle");
$drawer->draw("square");
$drawer->showCanvas(); // circle, square
class DrawService:
def __init__(self):
self.canvas = []
def draw(self, shape: str):
self.canvas.append(shape)
print(f"描画: {shape}")
def show_canvas(self):
print("現在のキャンバス:", ", ".join(self.canvas))
# 利用例
drawer = DrawService()
drawer.draw("circle")
drawer.draw("square")
drawer.show_canvas() # circle, square
設問 1:このコードが抱えている設計上の問題点を挙げよ
以下の観点を参考に、具体的に列挙すること:
- 状態が積み上がる一方で、過去の状態に戻れない
- 操作履歴が記録されておらず、巻き戻しや再実行の仕組みが存在しない
- UI/UX 要件として一般的な「Undo/Redo」機能に対応できない
- ユーザー操作の追跡や検証が困難
設問 2:この処理を履歴管理可能な構造に改善するにはどうすべきか
以下の観点を含めて改善案を整理すること:
- 各操作(描画など)を履歴として保持するにはどのような構造にすべきか
- 状態を記録・復元するためのアプローチ(オブジェクト/スナップショット)をどう設計するか
- 操作を記録・巻き戻しできるパターンの活用方法
例:適用候補となるパターン
パターン名 | 主な目的と効果 |
---|---|
Command | 各操作(命令)をオブジェクトとして記録・実行・取り消し可能にする |
Memento | オブジェクトの状態スナップショットを保存し、元に戻せるようにする |
Prototype | 状態を複製可能にし、履歴のためのクローン生成を容易にする |
State (補助) | 現在の状態をオブジェクト化して、遷移制御や状態の可視化を支援 |
発展課題(任意)
- 操作履歴に加えて、操作の「やり直し」も可能にするにはどのような構造が必要か
- 履歴の最大保持数やユーザーごとの履歴など、実運用で必要な制限・拡張機能に備えるには?
提出フォーマット(レビュー・勉強会用)
- 構造上の問題点(3 点以上)
- 改善方針とパターンの選定理由
- 改善後の構成概要(履歴の保存・復元の流れ、操作の抽象化方針など)