Featured image of post 第3回-管制塔を作ろう - PerlとMooで航空管制シミュレーターを作ろう

第3回-管制塔を作ろう - PerlとMooで航空管制シミュレーターを作ろう

管制塔(ControlTower)クラスを導入し、航空機同士が直接通信せずに安全に着陸できる設計に変更します。

管制塔が中央で調整

前回の振り返り

前回は、航空機同士が直接参照するとN×(N-1)個の参照が必要になり、管理が爆発的に複雑になることを確認しました。

今回は、実際の空港にヒントを得て、この問題を解決します。

実際の空港はどうしている?

実際の空港では、航空機同士が直接やり取りすることはありません。すべての通信は管制塔を経由して行われます。

	graph TD
    T[管制塔]
    A[JAL123] <--> T
    B[ANA456] <--> T
    C[SKY789] <--> T

航空機は他の航空機の存在を知る必要がありません。管制塔だけを知っていれば良いのです。

  • N機の場合: N個の参照だけで済む(各航空機が管制塔を参照)
  • 新しい航空機を追加しても、既存の設定を変更する必要がない

管制塔クラスを作る

管制塔(ControlTower)クラスを作りましょう。

 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
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;
        say "管制塔: " . $aircraft->flight_number . "を登録しました";
    }

    sub request_landing($self, $aircraft) {
        if ($self->runway_in_use) {
            say "管制塔: " . $aircraft->flight_number . 
                "、滑走路使用中です。待機してください";
            return 0;
        }
        $self->runway_in_use(1);
        say "管制塔: " . $aircraft->flight_number . "、着陸を許可します";
        return 1;
    }

    sub notify_landed($self, $aircraft) {
        $self->runway_in_use(0);
        say "管制塔: " . $aircraft->flight_number . 
            "の着陸を確認。滑走路クリア";
    }
}

管制塔は以下の役割を持ちます:

  • register: 航空機を登録する
  • request_landing: 着陸許可を判断する
  • notify_landed: 着陸完了の通知を受け取る

Aircraftクラスを修正

航空機は管制塔を参照するように変更します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package Aircraft {
    use Moo;

    has flight_number => (is => 'ro', required => 1);
    has tower => (is => 'rw');

    sub request_landing($self) {
        say $self->flight_number . ": 着陸許可をリクエストします";
        if ($self->tower->request_landing($self)) {
            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
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
#!/usr/bin/env perl
use v5.36;

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 . 
                "、滑走路使用中です。待機してください";
            return 0;
        }
        $self->runway_in_use(1);
        say "管制塔: " . $aircraft->flight_number . "、着陸を許可します";
        return 1;
    }

    sub notify_landed($self, $aircraft) {
        $self->runway_in_use(0);
        say "管制塔: " . $aircraft->flight_number . 
            "の着陸を確認。滑走路クリア";
    }
}

package Aircraft {
    use Moo;

    has flight_number => (is => 'ro', required => 1);
    has tower => (is => 'rw');

    sub request_landing($self) {
        say $self->flight_number . ": 着陸許可をリクエストします";
        if ($self->tower->request_landing($self)) {
            say $self->flight_number . ": 着陸します";
            $self->tower->notify_landed($self);
        } else {
            say $self->flight_number . ": 待機します";
        }
    }
}

# 管制塔を作成
my $tower = ControlTower->new;

# 航空機を作成して登録
my $flight1 = Aircraft->new(flight_number => 'JAL123');
my $flight2 = Aircraft->new(flight_number => 'ANA456');
my $flight3 = Aircraft->new(flight_number => 'SKY789');

$tower->register($flight1);
$tower->register($flight2);
$tower->register($flight3);

say "---";

# 着陸を要求
$flight1->request_landing;
say "---";
$flight2->request_landing;
say "---";
$flight3->request_landing;

実行結果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
管制塔: JAL123を登録しました
管制塔: ANA456を登録しました
管制塔: SKY789を登録しました
---
JAL123: 着陸許可をリクエストします
管制塔: JAL123、着陸を許可します
JAL123: 着陸します
管制塔: JAL123の着陸を確認。滑走路クリア
---
ANA456: 着陸許可をリクエストします
管制塔: ANA456、着陸を許可します
ANA456: 着陸します
管制塔: ANA456の着陸を確認。滑走路クリア
---
SKY789: 着陸許可をリクエストします
管制塔: SKY789、着陸を許可します
SKY789: 着陸します
管制塔: SKY789の着陸を確認。滑走路クリア

すべての航空機が管制塔を通じて安全に着陸できました。

新しい設計の利点

	classDiagram
    class ControlTower {
        -aircrafts: Aircraft[]
        -runway_in_use: bool
        +register(aircraft)
        +request_landing(aircraft) bool
        +notify_landed(aircraft)
    }
    class Aircraft {
        -flight_number: string
        -tower: ControlTower
        +request_landing()
    }
    ControlTower --> Aircraft : manages
    Aircraft --> ControlTower : requests
  • 航空機同士は互いを知らない(疎結合)
  • 新しい航空機を追加しても、既存のコードを変更する必要がない
  • 着陸の順序やルールは管制塔が一元管理

今回のまとめ

今回は、管制塔クラスを導入して相互依存の問題を解決しました。

  • 管制塔がすべての通信を仲介する
  • 航空機は管制塔だけを知っていれば良い
  • N機あっても参照はN個だけ

次回は、AircraftクラスにMoo::Roleを使って共通のインターフェースを定義します。

comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。