
前回の振り返り
前回は、管制塔クラスを導入して航空機間の相互依存を解消しました。
今回は、航空機クラスに共通のインターフェースを定義してさらに設計を改善します。
なぜインターフェースが必要か
現在のAircraftクラスは1種類ですが、実際の空港には様々な航空機がやってきます。ジャンボジェットから小型のプロペラ機まで、サイズも見た目も様々です。
- 旅客機(PassengerAircraft)— お客さんを乗せて飛ぶ
- 貨物機(CargoAircraft)— 荷物専門の働き者
- 小型機(SmallAircraft)— 身軽で小回りが利く
管制塔は、これらすべての航空機と同じ方法で通信できなければなりません。「あなたは旅客機だからこう話して、貨物機はこう…」なんて面倒ですよね。共通のインターフェースがあれば、どんな航空機でも同じ方法で扱えます。
Aircraft::Roleを定義
Moo::Roleを使って、航空機の共通インターフェースを定義します。Moo::Roleについては、以下のシリーズで詳しく解説しています。
1
2
3
4
5
6
7
8
| package Aircraft::Role {
use Moo::Role;
requires 'request_landing';
requires 'receive_clearance';
has tower => (is => 'rw');
}
|
requiresで必須メソッドを宣言し、hasで共通の属性を定義しています。
旅客機クラス
Aircraft::Roleを使って旅客機クラスを作ります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| package PassengerAircraft {
use Moo;
with 'Aircraft::Role';
has flight_number => (is => 'ro', required => 1);
has passengers => (is => 'ro', default => 0);
sub request_landing($self) {
say $self->flight_number .
"(旅客機): 着陸許可をリクエストします";
$self->tower->request_landing($self);
}
sub receive_clearance($self, $cleared) {
if ($cleared) {
say $self->flight_number . ": 着陸許可を受信。着陸します";
$self->tower->notify_landed($self);
} else {
say $self->flight_number . ": 待機指示を受信。待機します";
}
}
}
|
貨物機クラス
同様に貨物機クラスを作ります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| package CargoAircraft {
use Moo;
with 'Aircraft::Role';
has flight_number => (is => 'ro', required => 1);
has cargo_weight => (is => 'ro', default => 0);
sub request_landing($self) {
say $self->flight_number .
"(貨物機): 着陸許可をリクエストします";
$self->tower->request_landing($self);
}
sub receive_clearance($self, $cleared) {
if ($cleared) {
say $self->flight_number . ": 着陸許可を受信。着陸します";
$self->tower->notify_landed($self);
} else {
say $self->flight_number . ": 待機指示を受信。待機します";
}
}
}
|
管制塔を修正
管制塔からreceive_clearanceを呼び出すように修正します。
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
27
28
29
30
31
| package ControlTower {
use Moo;
has aircrafts => (is => 'ro', default => sub { [] });
has runway_in_use => (is => 'rw', default => 0);
sub register($self, $aircraft) {
push @{$self->aircrafts}, $aircraft;
$aircraft->tower($self);
say "管制塔: " . $aircraft->flight_number . "を登録しました";
}
sub request_landing($self, $aircraft) {
if ($self->runway_in_use) {
say "管制塔: " . $aircraft->flight_number .
"、滑走路使用中です";
$aircraft->receive_clearance(0);
return;
}
$self->runway_in_use(1);
say "管制塔: " . $aircraft->flight_number .
"、着陸を許可します";
$aircraft->receive_clearance(1);
}
sub notify_landed($self, $aircraft) {
$self->runway_in_use(0);
say "管制塔: " . $aircraft->flight_number .
"の着陸を確認。滑走路クリア";
}
}
|
完成コード
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
| #!/usr/bin/env perl
use v5.36;
package Aircraft::Role {
use Moo::Role;
requires 'request_landing';
requires 'receive_clearance';
has tower => (is => 'rw');
}
package ControlTower {
use Moo;
has aircrafts => (is => 'ro', default => sub { [] });
has runway_in_use => (is => 'rw', default => 0);
sub register($self, $aircraft) {
push @{$self->aircrafts}, $aircraft;
$aircraft->tower($self);
say "管制塔: " . $aircraft->flight_number . "を登録しました";
}
sub request_landing($self, $aircraft) {
if ($self->runway_in_use) {
say "管制塔: " . $aircraft->flight_number .
"、滑走路使用中です";
$aircraft->receive_clearance(0);
return;
}
$self->runway_in_use(1);
say "管制塔: " . $aircraft->flight_number .
"、着陸を許可します";
$aircraft->receive_clearance(1);
}
sub notify_landed($self, $aircraft) {
$self->runway_in_use(0);
say "管制塔: " . $aircraft->flight_number .
"の着陸を確認。滑走路クリア";
}
}
package PassengerAircraft {
use Moo;
with 'Aircraft::Role';
has flight_number => (is => 'ro', required => 1);
has passengers => (is => 'ro', default => 0);
sub request_landing($self) {
say $self->flight_number .
"(旅客機): 着陸許可をリクエストします";
$self->tower->request_landing($self);
}
sub receive_clearance($self, $cleared) {
if ($cleared) {
say $self->flight_number . ": 着陸許可を受信。着陸します";
$self->tower->notify_landed($self);
} else {
say $self->flight_number . ": 待機指示を受信。待機します";
}
}
}
package CargoAircraft {
use Moo;
with 'Aircraft::Role';
has flight_number => (is => 'ro', required => 1);
has cargo_weight => (is => 'ro', default => 0);
sub request_landing($self) {
say $self->flight_number .
"(貨物機): 着陸許可をリクエストします";
$self->tower->request_landing($self);
}
sub receive_clearance($self, $cleared) {
if ($cleared) {
say $self->flight_number . ": 着陸許可を受信。着陸します";
$self->tower->notify_landed($self);
} else {
say $self->flight_number . ": 待機指示を受信。待機します";
}
}
}
# 管制塔を作成
my $tower = ControlTower->new;
# 旅客機と貨物機を作成
my $passenger = PassengerAircraft->new(
flight_number => 'JAL123',
passengers => 180
);
my $cargo = CargoAircraft->new(
flight_number => 'FDX456',
cargo_weight => 50000
);
$tower->register($passenger);
$tower->register($cargo);
say "---";
$passenger->request_landing;
say "---";
$cargo->request_landing;
|
実行結果:
1
2
3
4
5
6
7
8
9
10
11
12
| 管制塔: JAL123を登録しました
管制塔: FDX456を登録しました
---
JAL123(旅客機): 着陸許可をリクエストします
管制塔: JAL123、着陸を許可します
JAL123: 着陸許可を受信。着陸します
管制塔: JAL123の着陸を確認。滑走路クリア
---
FDX456(貨物機): 着陸許可をリクエストします
管制塔: FDX456、着陸を許可します
FDX456: 着陸許可を受信。着陸します
管制塔: FDX456の着陸を確認。滑走路クリア
|
異なる種類の航空機が同じインターフェースで管制塔と通信できています。
Roleの効果
classDiagram
class AircraftRole {
<<role>>
+tower
+flight_number()*
+request_landing()*
+receive_clearance()*
}
class PassengerAircraft {
+passengers
}
class CargoAircraft {
+cargo_weight
}
AircraftRole <|.. PassengerAircraft
AircraftRole <|.. CargoAircraft
- 管制塔はどの種類の航空機でも同じ方法で扱える
- 新しい航空機の種類を追加しても、管制塔のコードを変更する必要がない
- インターフェースを守れば、自由に新しい航空機クラスを作れる
今回のまとめ
今回は、Aircraft::Roleで航空機の共通インターフェースを定義しました。
requiresで必須メソッドを宣言- 異なる種類の航空機を統一的に扱える
- 新しい航空機クラスの追加が容易
次回は、滑走路を別のクラスとして分離し、リソース管理の仕組みを実装します。