はじめに
こんにちは。miyagawaです。
最近、毎日の通勤時間を利用して読書するようにしています。読んだ本の中でO’REILLYのHead Firstオブジェクト指向分析設計 ―頭とからだで覚えるオブジェクト指向の基本がわかりやすく実践しやすいと感じたので、重要そうな部分を抜粋してまとめます。
カプセル化
カプセル化とは外部からの直接の変更を防ぎ、想定外の処理を行わないようにすることを指します。
例としてカプセル化されていないdogクラスを作成しました。barkメソッドが呼ばれるとログで鳴き声が出力されます。
1 2 3 4 5 6 7 8 | classDog(){ StringanimalSound="わんわん"; publicvoidbark(){ System.out.println("犬が鳴いた"); System.out.println(animalSound); } } |
barkメソッドを呼び出すことによって犬が「わんわん」と吠えることを想定しています。このカプセル化されていないDogクラスをsampleクラスから鳴き声をいじってみます。
1 2 3 4 5 6 7 8 9 10 | // カプセル化なし classSample(){ publicstaticvoidmain(Stringargs[]){ Dog dog=newDog(); dog.animalSound="ニャーニャー"; dog.bark(); System.out.println(dog.animalSound) } } |
結果…
1 2 | 犬が鳴いた ニャーニャーニャーニャー |
想定外の動作になってしまいました。カプセル化されていないことによって、外部クラスからanimalSoundを変更できる状態であったためです。これを防ぐためにanimalSoundをカプセル化し、Dogクラスを下記のように変更します。
1 2 3 4 5 6 7 8 9 10 | // カプセル化あり classDog(){ // アクセス修飾子をprivateに変更 privateStringanimalSound="わんわん"; publicvoidbark(){ System.out.println("犬が鳴いた"); System.out.println(animalSound); } } |
1 2 3 4 5 6 7 8 | classSample(){ publicstaticvoidmain(Stringargs[]){ Dog dog=newDog(); // dog.animalSound = "ニャーニャー"; コンパイルエラー dog.bark(); // System.out.println(dog.animalSound) コンパイルエラー } } |
結果…
1 2 | 犬が鳴いた わんわん |
animalSoundをカプセル化するだけで
・鳴き声の変更
・animalSound単体の出力
が不可能となり、想定通りの処理になりました。カプセル化することによって「想定していない処理」を防ぐことができます。
「全ての変数を闇雲にprivate化すればいい」というわけではなく、値を変更せずに出し入れするだけなのであればpublicでも良いかもしれません。時と場合によって使い分けることが重要です。
継承
継承は親クラスのメソッド・変数など機能を引き継ぐことを指します。例として犬がおやつを食べるプログラムを組みました。
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 | classAnimal(){ // Animalを継承するクラスはeatメソッドを使用することができる publicvoideat(Stringfood){ System.out.println(food+"を食べた"); } } classDog()extendsAnimal{ privateStringanimalSound="わんわん"; publicvoidbark(){ System.out.println("犬が鳴いた"); System.out.println(animalSound); } } classPetCare(){ publicvoidgiveSnack(Animal pet,Stringfood){ pet.eat(food); } } classSample(){ publicstaticvoidmain(Stringargs[]){ Dog dog=newDog(); // Animal animal = new Dog();でも可 PetCare.giveSnack(dog,"ミルク風味ガム"); } } |
結果…
1 | ミルク風味ガムを食べた |
PetCareクラスのgiveSnackメソッド > Dogが継承しているAnimalクラス > eatメソッドの順に呼ばれました。この形式にすることで新しくCatクラスを作成した時Animalを継承することによってコードの重複を防ぐことができます。
ポリモーフィズム
ポリモーフィズムは抽象クラス・メソッドに対して継承しているクラスがプログラムを実装することを指します。例として、Dogクラス・Catクラスにそれぞれbark()メソッドを実装します。
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 | // 抽象クラス classAnimal(){ // 抽象メソッド publicabstractvoidbark(); } // 具像Dogクラス classDog()extendsAnimal{ StringanimalSound="わんわん"; publicvoidbark(){ System.out.println("犬が鳴いた"); System.out.println(animalSound); } } // 具像Catクラス classCat()extendsAnimal{ StringanimalSound="ニャーニャー"; publicvoidbark(){ System.out.println("ネコが鳴いた"); System.out.println(animalSound); } } // Petクラス classPet(){ publicAnimal[]getPets(){ return{newDog(),newCat()} } } classSample(){ publicstaticvoidmain(Stringargs[]){ Pet pet=newPet(); Animal[]pets=pet.getPets(); for(Animal animal:pets){ animal.bark(); } } } |
結果…
1 2 3 4 | 犬が鳴いた わんわん ネコが鳴いた ニャーニャー |
Sampleクラスのmainメソッドで一度Dog、CatクラスがAnimalクラスにキャストされ、その後animal.bark()で各クラスのbark()メソッドが呼ばれています。
親クラスを継承・メソッドの呼び出しを行う際にインスタンスを意識することなく操作でき、独立性が高まります。それによって、よりテスタブルなコードにすることができます。
さいごに
HeadFirstシリーズは記憶に残りやすいように書かれています。今まで見たことのない形式のため、人によっては好き嫌いが出てくるそうです。自分は好きな書かれ方だと感じたので、他のHeadFirstシリーズも読んでみたいと思います。