PerlとMooで「ランダムダンジョンジェネレーター」を作る連載の第5回です。
前回はBridgeパターンを導入して、テーマとアルゴリズムを分離しました。
今回は新しいテーマ「水中神殿」を追加して、Bridgeパターンの拡張性を体験します。

Open/Closed原則とは
SOLID原則の1つである「Open/Closed原則(OCP)」は、以下のように定義されます。
- Open(開いている): 拡張に対して開いている
- Closed(閉じている): 修正に対して閉じている
つまり、既存のコードを修正せずに、新しい機能を追加できる設計が理想的です。
Bridgeパターンは、この原則を実現するのに最適な設計です。
水中神殿テーマを追加する
新しいテーマ「水中神殿」を追加してみましょう。
既存のコードを一切変更せずに、1クラス追加するだけで済みます。
1
2
3
4
5
6
7
8
9
10
11
| # UnderwaterTempleTheme.pm - 水中神殿テーマ
package UnderwaterTempleTheme;
use v5.36;
use Moo;
extends 'DungeonTheme';
sub wall_char ($self) { '≈' } # 海藻に覆われた壁
sub floor_char ($self) { '~' } # 水底
1;
|
たったこれだけです。
動作確認
水中神殿テーマを既存のアルゴリズムと組み合わせてみましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| #!/usr/bin/env perl
# underwater_demo.pl - 水中神殿テーマのデモ
use v5.36;
use lib '.';
use UnderwaterTempleTheme;
use MazeAlgorithm;
my $dungeon = UnderwaterTempleTheme->new(
algorithm => MazeAlgorithm->new,
width => 41,
height => 11,
);
$dungeon->generate;
print $dungeon->render;
|
実行結果は以下の通りです。
1
2
3
4
5
6
7
8
9
10
11
| ≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈
≈~≈~~~~~≈~~~~~≈~~~~~~~≈~≈~~~~~≈~~~~~~~~~≈
≈~≈~≈≈≈~≈~≈≈≈~≈~≈≈≈≈≈~≈~≈~≈≈≈~≈~≈≈≈≈≈≈≈~≈
≈~≈~~~≈~≈~~~≈~≈~≈~~~≈~≈~≈~≈~~~≈~~~~~~~≈~≈
≈~≈≈≈~≈~≈≈≈~≈~≈~≈~≈~≈~≈~≈~≈~≈≈≈≈≈≈≈≈≈~≈~≈
≈~~~~~≈~~~~~≈~~~≈~≈~~~≈~~~≈~~~~~~~~~~~≈~≈
≈≈≈≈≈~≈≈≈≈≈~≈≈≈≈≈~≈~≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈~≈≈≈~≈
≈~~~≈~~~~~≈~~~~~≈~≈~≈~~~~~~~~~~~~~≈~~~≈~≈
≈~≈~≈≈≈≈≈~≈≈≈≈≈~≈~≈~≈~≈≈≈≈≈≈≈≈≈≈≈~≈≈≈~≈~≈
≈~≈~~~~~~~~~~~~~≈~~~≈~~~~~~~~~~~~~~~≈~~~≈
≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈
|
水中神殿らしい、波と海藻で表現されたダンジョンが生成されました。
変更が必要なかったファイル
以下のファイルは一切変更していません。
DungeonTheme.pm(基底クラス)GenerationAlgorithm.pm(アルゴリズムRole)RandomAlgorithm.pm(既存アルゴリズム)MazeAlgorithm.pm(既存アルゴリズム)CaveTheme.pm(既存テーマ)CastleTheme.pm(既存テーマ)RuinsTheme.pm(既存テーマ)
これがOpen/Closed原則です。
既存のコードは「修正に対して閉じて」いて、新しいテーマは「拡張に対して開いて」います。
Bridgeパターン導入前との比較
もしBridgeパターンを導入していなかったら、水中神殿テーマを追加するために以下のクラスが必要でした。
UnderwaterTempleRandomDungeon.pmUnderwaterTempleMazeDungeon.pm- (将来的に)
UnderwaterTempleBSPDungeon.pm
アルゴリズムが増えるたびに、新しいテーマのクラスも増えてしまいます。
Bridgeパターンでは、テーマは1クラスだけで十分です。
どのアルゴリズムとも組み合わせられます。
全テーマ × 全アルゴリズムのテスト
4つのテーマ × 2つのアルゴリズム = 8通りの組み合わせをテストしてみましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| #!/usr/bin/env perl
# all_combinations.pl - 全組み合わせテスト
use v5.36;
use lib '.';
use CaveTheme;
use CastleTheme;
use RuinsTheme;
use UnderwaterTempleTheme;
use RandomAlgorithm;
use MazeAlgorithm;
my @themes = (
[ 'CaveTheme', CaveTheme->new( algorithm => MazeAlgorithm->new ) ],
[ 'CastleTheme', CastleTheme->new( algorithm => MazeAlgorithm->new ) ],
[ 'RuinsTheme', RuinsTheme->new( algorithm => MazeAlgorithm->new ) ],
[ 'UnderwaterTempleTheme', UnderwaterTempleTheme->new( algorithm => MazeAlgorithm->new ) ],
);
for my $entry (@themes) {
my ( $name, $dungeon ) = $entry->@*;
say "=== $name ===";
$dungeon->generate;
print $dungeon->render;
say "";
}
|
すべての組み合わせが正しく動作することを確認できます。
完成コード
今回追加したUnderwaterTempleTheme.pmの完成コードです。
1
2
3
4
5
6
7
8
9
10
11
| # UnderwaterTempleTheme.pm - 水中神殿テーマ(完成版)
package UnderwaterTempleTheme;
use v5.36;
use Moo;
extends 'DungeonTheme';
sub wall_char ($self) { '≈' } # 海藻に覆われた壁
sub floor_char ($self) { '~' } # 水底
1;
|
今回のまとめ
第5回では、新しいテーマを追加してOpen/Closed原則を体感しました。
- 水中神殿テーマを1クラスで追加
- 既存のコードは一切変更なし
- どのアルゴリズムとも組み合わせ可能
- Open/Closed原則の実践
次回は、アルゴリズム側の拡張として「部屋区分型(BSP)」を追加します。
テーマと同様に、1クラス追加するだけで済むことを確認しましょう。