第26回「思考を変えろ!!さらばコメントアウト 後編」
更新日:
こんにちは。
森川です。
黒瀬もちょっとずつ復旧してきましたが、まだひどいところはひどいです。
私のような立場は比較的自由がきくので、今回ばかりは仕事先にも無理をきいてもらって、本当にわずかばかりですがのお手伝いをさせていただきました。
私は大して力になれてないですが、黒瀬商工会青年部の皆様の復興活動には頭が下がるばかりです。
私生活でもちょっと出ると渋滞渋滞でなかなか大変なのですが、これが正常に戻るのが電車が全面復旧する11月あたり、
すべてが完全に復旧するのがニュースで見ると来年の9月あたりと本当に被害が甚大ではなかったんだなぁとつくづく感じます。
本題
さて、前回はデザインの話だったので今回はシステムの話です。
前前回は、「思考を変えろ!!さらばコメントアウト」というお題で、プログラムの変更・追加についての考え方の概要を書いた。
おさらいをすると、オブジェクト指向(以降OOと略す)の設計方法は、システム運用の視点から考えられており、運用時におけるプログラムの変更、追加へのリスクを下げるための思考方法である。
詳細は前前回ブログを読んでいただきたい。第24回「思考を変えろ!!さらばコメントアウト」
これまでの流れ
- 1. オブジェクト指向について書いてみたい
- 2. オブジェクト指向(OO オーオー)とはなんぞや??(前編)
- 3. オブジェクト指向(OO オーオー)とはなんぞや??(後編)
- 4. 偉大な先人達に学べ。デザインパターンの重要性。
- 5. たかが文法されど文法。
- 6. 思考を変えろ!!数学的から国語的思考へ
- 7. 思考を変えろ!!さらばコメントアウト 前編
今回は、「思考を変えろ!!さらばコメントアウト 後編」ということでもっと具体的に掘り下げて、実例をあげながら「プログラムの変更」についての考え方を書いてみたい。
さらばコメントアウト
プログラムに変更を加えるということは、バグが発生する危険性があり、変更箇所があればあるほどその危険性は増大する。
その危険性をさげるためには、簡単にいうとタイトル「さらばコメントアウト」とあるようになるべくコメントアウトして修正する箇所を減らすことである
変更箇所が1クラスにまとまっているのであればまだ扱いやすいが、変更箇所があっちもこっちもあるというような作り方をしていると本当に後々地獄をみる。
ではOO(オーオー)はよく実際の世界に例えられるので、実際の世界に例えて説明したいと思う。
例えば、「 車 」
■お題
冬になったので路面が凍結する危険性があります。あなたの車を凍結した路面でも対応できるように改修してください。
■修正内容
ノーマルタイヤからスタッドレスタイヤへ履き替える。
つまり、StudlessTireクラスを新規に作成し、NormalTireクラスから変更する。
■解説
Carクラスのcreateメソッドには、タイヤを取り付けるコードがあります。
今回はその部分をNormalTireクラスからStudlessTireクラスへ取り換えることにします。
class Car{
private NormalTire tire;
public void create(){
NormalTire tire = new NormalTire();
tire.initPresure(); //タイヤの空気圧を調整
this.tire = tire; //タイヤをとりつける
};
}
class NormalTire{
public void initPresure(){
System.out.println("空気圧を調整します");
};
}
冬になったのでこのプログラムを修正します。
class Car{
private StudlessTire tire;
//private NormalTire tire;
public void create(){
StudlessTire tire = new StudlessTire();
//NormalTire tire = new NormalTire();
tire.initPresure(); //タイヤの空気圧を調整
this.tire = tire; //タイヤをとりつける
};
}
class NormalTire{
public void initPresure(){
System.out.println("空気圧を調整します");
};
}
//新設
class StudlessTire{
public void initPressure(){
System.out.println("空気圧を調整します");
}
}
行ったプログラム修正は、
1. StudlessTireクラスを新設している。
2. 3か所のNormalTireという文言をStudlessTireに編集している。
です。
このプログラムをより変更に強いプログラムにすることを考えてみたいと思います。
そもそも変更に強いプログラムってどんなプログラムなの?
今回の例でいうと、StudlessTireクラスを新設して、Carクラス側に3か所も修正が必要になっています。
実際の世界に戻って考えてみたいと思います。
車のタイヤを変えました。車本体側にも何か調整が必要ですか??
そんなことないでしょう。
普通は、タイヤを付け替えたらそれで終わりです。
プログラムも同じでそうしたいのです。
Carクラス側にはなるべく修正をいれたくないのです。
StudlessTireクラスに付け替えてそれで終わりにしたいのです。
つまりStudelessTireクラスを新設してCarクラス側を3か所も変更しないといけないのは、変更に弱いのです。
タイヤを付け替える必要があるので、必ず1か所は変更が必要になります。
でも他の2か所の修正はなくすことができます。
どうするかというとそうInterfaceさんの登場。
class Car{
private Tire tire;
public void create(){
Tire tire = new NormalTire();
tire.initPresure(); //タイヤの空気圧を調整
this.tire = tire; //タイヤをとりつける
};
}
class Tire implements Tire{
public void initPressure(){
System.out.println("空気圧を調整します");
}
}
interface Tire{}
冬になったのでこのプログラムを修正します。
class Car{
private Tire tire;
public void create(){
Tire tire = new StudlessTire();
//Tire tire = new NormalTire();
tire.initPresure(); //タイヤの空気圧を調整
this.tire = tire; //タイヤをとりつける
};
}
class NormalTire implements Tire{
public void initPressure(){
System.out.println("空気圧を調整します");
}
}
//新設
class StudlessTire implements Tire{
public void initPressure(){
System.out.println("空気圧を調整します");
}
}
interface Tire{}
これで修正箇所は、StudlessTireクラスを新設することと、Carクラスの1箇所を書き換えるだけになった。
タイヤを付け替えたらそれで終わりという実際の世界と同じになったわけです。
更に変更に強くする
でもまだ問題点がある。
もしStudlessTireを新設する人がinitPresure()メソッドを作るのを忘れたらどうしますか?
前編でも書いたように変更する人が、仕様を知らないと修正できないシステムではダメなんよ。
また実際の世界に戻ってみたいと思う。
車ってどんなタイヤでもつけられたら困りますよね。
トヨタ、マツダなどメーカーによっても違うし、車のサイズによっても違うよね。
大型トラックに軽自動車のタイヤがついたらダメだよね。
プログラムでも同じ、Tireクラスを継承すればどんなクラスでも設置できるのは困るのでルールを作らないといけない。
どうするかというと、そう抽象メソッドよね。
class Car{
private Tire tire;
public void create(){
Tire tire = new StudlessTire();
tire.initPresure(); //タイヤの空気圧を調整
this.tire = tire; //タイヤをとりつける
};
}
class NormalTire implements Tire{
@Override
public void initPressure(){
System.out.println("空気圧を調整します");
}
}
//新設
class StudlessTire implements Tire{
@Override
public void initPressure(){
System.out.println("空気圧を調整します");
}
}
interface Tire{}{
initPressure(); //抽象メソッド
}
これでTireクラスを継承するクラスは、initPressure()メソッドの実装が義務付けられるルールができたわけよ。
どんなタイヤでも設置できなくなった。
ここで使っているinterfaceは車で言えばホイールのような役目を担っている。
Interfaceの「inter」って何を「inter」するのかというと、クラスとクラスの間を「inter」するのよね。
interfaceとはクラスとクラスをつなぐ役割の登場人物というイメージを持っておけばわかりやすいと思う。
上記をあえてプログラム用語で小難しく書いてみる。
前者のコードは、CarクラスとTireクラスの結合が強いため、変化に弱いプログラムである。
後者のコードは、CarクラスとTireクラスの間に疎結合を作ることにより結合を弱め、変化に強いプログラムへ改修した。
オブジェクト指向ででてくる小難しい用語の意味が少しでもわかったでしょうか??
これがオブジェクト指向の3大要素にもあげられるポルモーフィズム(多様性)の使用例です。
ポルモーフィズム(多様性)を使って変化に強いコードにしたのです。
大前提に戻って考え直してみる
ここでもう一度、大前提に戻ってもう一度考えてみたいと思う。
タイヤは変更する可能性が高いから、こういう設計方法にしているということを理解していただきたい。
変更が絶対に起こらないのであれば、こんなめんどくさいことをやる必要はない。
変更する可能性が高いから疎結合を作るのであって、絶対変更しないなら疎結合はいらないということ。
また、実際の世界に戻ってみる。
「車」でも取り換えが発生する可能性が高い部分は、細かいパーツになっているよね。
例えば、タイヤやバンパーなど。
でも取り換えが発生する可能性が低い部分は、細かいパーツになっていない。
例えば、車のボディーの横の部分、天井の部分など1枚の鉄板よね。
だからそのような部分をぶつけたりして、修理が必要になると修理代ぶち高いよね。
痛い目を見たうちのフリード(笑)
プログラムも同じなのです、変更が入ると予想される部分には、事前に対策をしておかないといけないのです。
OO(オーオー)でよくいう変化する部分と変化しない部分を切り分けるということはこういうことです。
これが前回のブログで話した「運用の視点」でプログラムを考えるということ。
構造化プログラミングにこんな考え方はないでしょう。
最後に
今回は簡単な一例をあげましたが、対策方法はいろいろあるのよ。
その代表例がブログでもしきりにあげているGOF(Gang of Four)のデザインパターンよね。
これでGOFの重要性がわかっていただけたのではないでしょうか??
私もこういうことを知って、これこそプロのシステム屋がやらないといけないことだなと実感しました。
プロとして仕事をしていると作るだけなら言語関係なくほぼなんとでもなるのよ。
動くものができるのは当然で、その先、つまり運用について考えたシステムを作るのがプロの仕事だと私は考えています。
でも、これがまた奥が深い(涙)。
これらを習得するには数多くのシステム構築・運用経験が必要であり、それとともにデザインパターンなどの知識的な学習も必要である。
一筋縄ではいかないのは、それらの構築運用経験と知識的学習を平行してやらないといけないということ、運用経験が乏しいのにデザインパターンだけ勉強しても何をしているのか意味が理解できないのよ。
私自身もまだまだ修行中の身である。
間違った部分があってもご了承いただきたい。
何かの参考になれば。ではでは。