FizzBuzzを無理やり設計し
てみた
休み明け最初の勉強会なのでネタトークをします
前知識
https://nekogata.hatenablog.com/entry/2018/09/10/163206
1~100までの数値をTerminalに表示せよ。ただし3で割り切れる場合はFizz, 5で割
り切れる場合はBuzz, 3と5で割り切れる場合はFizzBuzzと表示せよ。
FizzBuzz
ヤツが降臨した
仕様が変更されたとする
・3で割り切れた場合はFizzじゃなくてBizzと表示して!
・やっぱり3で割り切れた場合はFizzで、7で割り切れた場合はBizzと表示して!
・1~100までの数値じゃなくて1~200までの数値に対して処理して!
・やっぱり配列の各要素に対して処理して!
・やっぱり木構造で!
・結果を出力するんじゃなくてファイルに出力して!
etc
表示内容を変更する場合にはここを
変更する必要がある
表示条件を変更する場合にはここを
変更する必要がある
処理対象を変更する場合にはここを
変更する必要がある
単一責務の原則に違反している😥
単一責務の原則
単一責務の原則とは「1つのクラスに1つの役割(機能)」というシンプルな原則
クラスの妥当性をチェックするために別の角度から眺める必要が出てきます。こ
の時の言葉が「クラスを変更する理由が2つ以上存在してはならない」というも
の
https://qiita.com/UWControl/items/98671f53120ae47ff93a
https://nekogata.hatenablog.com/entry/2018/09/10/163206
(再掲)
設
計
その前にFizzBuzzの仕様について考えてみた
FizzBuzzにおいて変更されうる仕様は下記の通りと定義した
1. それぞれのルール
例)3で割り切れる場合はFizz → 3で割り切れる場合はBizz
2. ルールの組み合わせと順序
例)3で割り切れる場合はFizz、5で割り切れる場合はBuzz
→ 3で割り切れる場合はFizz、5で割り切れる場合はBuzz、7で割り切れる場合はBizz
例)3で割り切れる場合はFizz、5で割り切れる場合はBuzz、3と5で割り切れる場合はFizzBuzz
→ 3で割り切れる場合はFizz、5で割り切れる場合はBuzz、3と5で割り切れる場合はBuzzFizz
3. 入力
例)1〜100の数値に対して処理を行う → 1〜200の数値に対して処理を行う
例)1〜100の数値に対して処理を行う → 配列の各要素に対して処理を行う
4. 出力
例)画面出力 → ファイル出力
単一責務の原則に違反していないだろうか?🤔
(再掲)FizzBuzzの仕様について考えてみた
FizzBuzzにおいて変更されうる仕様は下記の通りと定義した
1. それぞれのルール
例)3で割り切れる場合はFizz → 3で割り切れる場合はBizz
2. ルールの組み合わせと順序
例)3で割り切れる場合はFizz、5で割り切れる場合はBuzz
→ 3で割り切れる場合はFizz、5で割り切れる場合はBuzz、7で割り切れる場合はBizz
例)3で割り切れる場合はFizz、5で割り切れる場合はBuzz、3と5で割り切れる場合はFizzBuzz
→ 3で割り切れる場合はFizz、5で割り切れる場合はBuzz、3と5で割り切れる場合はBuzzFizz
3. 入力
例)1〜100の数値に対して処理を行う → 1〜200の数値に対して処理を行う
例)1〜100の数値に対して処理を行う → 配列の各要素に対して処理を行う
4. 出力
例)画面出力 → ファイル出力
1. それぞれのルール
それぞれのルールに関する処理は1つのクラスに集約されているため違反していない
2. ルールの組み合わせと順序
ルールが追加、削除された時に既存のルールは影響を受けない
またChain of Responsibility パターンを採用しているためルールの適応順序は自由に定義できる
(補足)Chain of Responsibility パターン
Chain of Respoisibility パターンとは、「責任」を負ったものが、「鎖」状につな
がれた状態をイメージさせるパターンです。
それぞれのクラスには次に適応すべきクラスのインスタンスをフィールド変数と
して持ちます
http://www.techscore.com/tech/DesignPattern/ChainOfResponsibility.html/
(補足)Chain of Responsibility パターン
それぞれのルールはAbstractRuleクラスを継承しており、AbstractRuleクラスはフィールド変数に
AbstractRuleクラスのインスタンスもしくはnullを$nextRuleとして保持します
それぞれのルールは自身の責務を行なった後に$nextRuleがAbstractRuleクラスのインスタンスであれば、
そのルールに責務を移譲します
(補足)Chain of Responsibility パターン
例えば3の倍数はFizz→5の倍数はBuzz→7の倍数はBizzの順番でルールを適応する(105の時に
FizzBuzzBizzと表示させたい)のであれば
1. BizzRule->$nextRule=null
2. BuzzRule->$nextRule=1で定義したインスタンス
3. FizzRule->$nextRule=2で定義したインスタンス
とすればFizz→Buzz→Bizzの順番でルールが適応される
3. 入力
入力処理は1つのクラスに集約されているため違反していない
4. 出力
出力処理は1つのクラスに集約されているため違反していない
単一責務の原則に違反していない😃
他のSOLID原則に違反していないだろうか?🤔
オープン・クローズドの原則
この原則は「クラスは拡張に対して開いていて、修正に対して閉じていなければ
ならない」という表現が用いられます。
これは、クラスに対して「拡張が出来る」「修正する場合はそのクラスだけ修正
すればいい」という2つの条件を要求しているものです。
言わんとしていることはわかりますよね。では、問題は「どうやってそれを実現
するか」と言う点です。
実現するためのテクニックの王道があります。それは
「クラスはオブジェクトではなくインターフェースに依存せよ」
というものです。
https://qiita.com/UWControl/items/98671f53120ae47ff93a
全てのクラスはインターフェースに依存しているので、例えばFizzRuleクラスを改修した時にFizzBuzzクラ
スも改修を行う必要がある、といったことは無い
また何らかの理由があってChain of Responsibility パターンの採用を見送る必要があったとしてもIRuleは変
更されないのでFizzBuzzクラスが影響を受けるといったこともない
オープン・クローズドの原則に違反していない😃
リスコフの置換原則
この原則は「基本クラスを使っている場所で基本クラスの代わりにサブクラスを
使っても問題なく動かなけらばならない」というものです。
例えばsampleクラスのインスタンスを引数にとるhogehoge関数があるとします
int hogehoge(sample Object)
この時hogehoge関数は引数にsampleクラスの代わりにsampleクラスを継承した
newSampleクラスを指定しても問題なく動作しなくてはならない
https://qiita.com/UWControl/items/98671f53120ae47ff93a
https://github.com/SunriseDigital/work-shop/wiki/リスコフの置換原則
そもそもリスコフの置換原則を適応すべき箇所がない
まぁリスコフの置換原則にも違反はしていない😃
インターフェース分離の法則
インターフェース分離の法則は「クライアントに、クライアントが利用しないメ
ソッドへの依存を強制してはならない。」という法則です
例えば、以下のようなコードがあったとします。
interface ultraSuperDeluxeGreateBigSugoiEngine
{
void Departure();
void PlayMusic();
void CallYourGirlFriend();
//その他いろんな機能
}
この時、「このインターフェースを継承したクラスのPlayMusicメソッドを変更
した場合に他のメソッドも修正する必要がある」という問題点があります
https://qiita.com/UWControl/items/98671f53120ae47ff93a
IRule, IPrinterに関しては1つの関数しか宣言していないため分離の必要はない
問題となるのはIteratorである
Iteratorを実現したクラスではrewind(), current(), key(), next(), valid()の定
義を行う必要がある
CountUpIteratorは$positionが$max以下の間、インクリメントを続けるイ
テレーターである。そのためcurrent()の定義は不必要である(正確にい
うとcurrent()とkey()は同じ内容となる)
そもそもIteratorインターフェースとは何か?
PHP 5 は、たとえば foreach 命令などによる反復処理を可能とするよう オブジェク
トを定義する手段を提供します。 デフォルトで、 全ての アクセス権限がある プロ
パティは、反復処理に使用することができます。
1~100の間、$positionが$max以下の間、インクリメントを続けるってイ
テレーターなのか?
http://php.net/manual/ja/language.oop5.iterations.php
もっとシンプルにこれでよくね?
1~100のインクリメントをしたいのであれば、1~100までの数値が格納さ
れた配列を定義してやればいい
ということで、少々修正
インターフェース分離の法則にも違反していない😃
依存関係逆転の原則
上位のモジュールは下位のモジュールに依存してはならない。どちらのモジュー
ルも「抽象」に依存すべきであるという原則
https://qiita.com/UWControl/items/98671f53120ae47ff93a
FizzBuzzクラスに着目する
コンストラクタを見ると、いずれの引数も型にインターフェースを指定している
したがってFizzBuzzクラスは例えばFizzRuleクラスといった下位のクラスに依存しているのではなくインタ
ーフェース(抽象)に依存していることが分かる(コンストラクタインジェクション)
依存関係逆転の原則にも違反していない😃
よってSOLID原則すべてに違反していない😃
感想
楽しいけどUML作ったり資料作ったりするのが疲れる
一方でこの作業は設計を行なった際の暗黙知を形式知とする作業なのかと考える
と、レビューやメンテナンス性の向上に役立つのではないかと思った
コード
https://github.com/SuzukiCecil/FizzBuzz_Normal
https://github.com/SuzukiCecil/FizzBuzz_Solid
https://github.com/SuzukiCecil/FizzBuzz_ObserverP
attern

Fizzbuzz solid