はじめに
こんにちは。miyagawaです。
最近、毎日の通勤時間を利用して読書するようにしています。読んだ本の中でO’REILLYのHead Firstオブジェクト指向分析設計 ―頭とからだで覚えるオブジェクト指向の基本がわかりやすく実践しやすいと感じたので、重要そうな部分を抜粋してまとめます。
カプセル化
カプセル化とは外部からの直接の変更を防ぎ、想定外の処理を行わないようにすることを指します。
例としてカプセル化されていないdogクラスを作成しました。barkメソッドが呼ばれるとログで鳴き声が出力されます。
1 2 3 4 5 6 7 8 | class Dog() { String animalSound = "わんわん"; public void bark() { System.out.println("犬が鳴いた"); System.out.println(animalSound); } } |
barkメソッドを呼び出すことによって犬が「わんわん」と吠えることを想定しています。このカプセル化されていないDogクラスをsampleクラスから鳴き声をいじってみます。
1 2 3 4 5 6 7 8 9 10 | // カプセル化なし class Sample() { public static void main(String args[]){ Dog dog = new Dog(); dog.animalSound = "ニャーニャー"; dog.bark(); System.out.println(dog.animalSound) } } |
結果…
1 2 | 犬が鳴いた ニャーニャーニャーニャー |
想定外の動作になってしまいました。カプセル化されていないことによって、外部クラスからanimalSoundを変更できる状態であったためです。これを防ぐためにanimalSoundをカプセル化し、Dogクラスを下記のように変更します。
1 2 3 4 5 6 7 8 9 10 | // カプセル化あり class Dog() { // アクセス修飾子をprivateに変更 private String animalSound = "わんわん"; public void bark() { System.out.println("犬が鳴いた"); System.out.println(animalSound); } } |
1 2 3 4 5 6 7 8 | class Sample() { public static void main(String args[]){ Dog dog = new Dog(); // 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 | class Animal() { // Animalを継承するクラスはeatメソッドを使用することができる public void eat(String food) { System.out.println(food + "を食べた"); } } class Dog() extends Animal { private String animalSound = "わんわん"; public void bark() { System.out.println("犬が鳴いた"); System.out.println(animalSound); } } class PetCare() { public void giveSnack(Animal pet, String food) { pet.eat(food); } } class Sample() { public static void main(String args[]){ Dog dog = new Dog(); // 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 | // 抽象クラス class Animal() { // 抽象メソッド public abstract void bark(); } // 具像Dogクラス class Dog() extends Animal { String animalSound = "わんわん"; public void bark() { System.out.println("犬が鳴いた"); System.out.println(animalSound); } } // 具像Catクラス class Cat() extends Animal { String animalSound = "ニャーニャー"; public void bark() { System.out.println("ネコが鳴いた"); System.out.println(animalSound); } } // Petクラス class Pet() { public Animal[] getPets() { return {new Dog(),new Cat()} } } class Sample() { public static void main(String args[]){ Pet pet = new Pet(); Animal[] pets = pet.getPets(); for (Animal animal: pets) { animal.bark(); } } } |
結果…
1 2 3 4 | 犬が鳴いた わんわん ネコが鳴いた ニャーニャー |
Sampleクラスのmainメソッドで一度Dog、CatクラスがAnimalクラスにキャストされ、その後animal.bark()で各クラスのbark()メソッドが呼ばれています。
親クラスを継承・メソッドの呼び出しを行う際にインスタンスを意識することなく操作でき、独立性が高まります。それによって、よりテスタブルなコードにすることができます。
さいごに
HeadFirstシリーズは記憶に残りやすいように書かれています。今まで見たことのない形式のため、人によっては好き嫌いが出てくるそうです。自分は好きな書かれ方だと感じたので、他のHeadFirstシリーズも読んでみたいと思います。