見出し画像

3分間プログラミング講座 第10回: オブジェクト作りの「工場」?~デザインパターンとファクトリーメソッドパターン~



登場人物:

  • チャット先生(通称:先生): オブジェクト指向マスター。普段は優しく、たまにユーモアも交えながら解説。

  • ボット助手(通称:ボット君): プログラミングを始めたばかりの若手。素朴な疑問を投げかけるのが得意。


ボット君: 先生!前回はインターフェースと抽象クラスで、設計の約束事について学びました!プログラムをきれいに保つためのルール、って感じでしたね!

先生: ハハハ、ボット君!その通り!インターフェースや抽象クラスは、クラス間の関係性を明確にし、変更に強いプログラムを作るための強力な道具だ。今日は、それらの道具をさらに効果的に使うための「デザインパターン」という考え方、特に「オブジェクト作りの『工場』」とも呼ばれるパターンについて学んでいこう。

ボット君: デザインパターン?工場?なんだかすごい名前ですね!

先生: そうだね。デザインパターンは、ソフトウェア開発でよく直面する問題に対して、**「こうすればうまくいく」という、経験に基づいた「再利用可能な解決策」**のことなんだ。建築における「設計の型」のようなものだと思えばいい。


デザインパターンとは?~「良い設計」の知恵袋~

先生: デザインパターンは、1994年に出版された「GoF(Gang of Four)」と呼ばれる4人の著者による「デザインパターン」という本で体系化されたのが始まりなんだ。彼らは、オブジェクト指向の優れた設計には共通の「パターン」があることを発見し、それを23種類に分類して紹介した。

ボット君: 23種類も!そんなにたくさんあるんですか!

先生: そうだね。それぞれが特定の設計上の問題を解決するための「型」を提供しているんだ。デザインパターンを学ぶことで、

  • 「良い設計」の引き出しが増える: ゼロから設計を考えるのではなく、実績のある解決策を適用できる。

  • 開発者間の共通言語になる: 「この部分はファクトリーパターンでいこう」と言えば、皆が同じ設計をイメージできる。

  • 変更に強いプログラムが作れる: パターンは柔軟性や拡張性を高めるように設計されていることが多い。

先生: 今日は、その中でも特にオブジェクトの生成に関わる重要なパターンの一つ、「ファクトリーメソッドパターン(Factory Method Pattern)」について見ていこう。


ファクトリーメソッドパターンとは?~オブジェクト作りの「工場」~

先生: ファクトリーメソッドパターンは、その名の通り「オブジェクトを生成する処理を、サブクラスに任せる」ためのパターンだ。まるで「工場」のように、具体的な製品(オブジェクト)の作り方を、専門の部署(サブクラス)に任せるイメージだね。

ボット君: オブジェクトを作るのを、別のクラスに任せるんですか?なんでそんなことするんですか?

先生: 良い疑問だ!これまでのプログラムでは、オブジェクトを作るときは「new クラス名()」と直接書いていたよね?

Java

// 直接オブジェクトを生成する例
Dog myDog = new Dog();
Cat myCat = new Cat();

先生: この書き方だと、もし「Dogクラスの作り方」や「Catクラスの作り方」が変わったり、新しい種類の動物(例えばLion)を追加したりするたびに、new Dog()やnew Cat()と書いている全ての場所を修正する必要が出てくるかもしれない。これは、プログラムが大きくなると大変なことになる。

先生: ファクトリーメソッドパターンは、この「オブジェクト生成の処理」を、それを利用する側(クライアントコード)から切り離すことで、以下のメリットをもたらすんだ。

  • 柔軟性の向上: クライアントコードは、具体的なクラス名を知らなくてもオブジェクトを生成できるようになる。

  • 拡張性の向上: 新しい種類のオブジェクトを追加する際に、既存のクライアントコードを変更する必要がない。新しい「工場」を追加するだけで済む。

  • 結合度の低下: オブジェクトを利用する側と、オブジェクトを生成する側の依存関係が緩やかになる。


ファクトリーメソッドパターンの例

先生: 動物の例で考えてみよう。動物を生成する「工場」を作るイメージだ。

Java

// ステップ1: 生成するオブジェクトの共通インターフェースを定義
interface Animal {
    void makeSound();
}

// ステップ2: 具象クラス(具体的な製品)を実装
class Dog implements Animal {
    @Override
    public void makeSound() {
        System.out.println("ワン!");
    }
}

class Cat implements Animal {
    @Override
    public void makeSound() {
        System.out.println("ニャー!");
    }
}

// ステップ3: 抽象ファクトリークラスを定義
// ここで「ファクトリーメソッド」を抽象メソッドとして宣言する
abstract class AnimalFactory {
    // これがファクトリーメソッド(抽象メソッド)
    // このメソッドは、Animalインターフェースを実装したオブジェクトを返すことを約束する
    public abstract Animal createAnimal();

    // 共通の処理(テンプレートメソッド的な要素)
    // 例えば、生成した動物に共通の初期設定を行うなど、生成後の共通処理をここに記述できる
    public void prepareAnimal() {
        Animal animal = createAnimal(); // ファクトリーメソッドを呼び出してオブジェクトを生成
        animal.makeSound(); // 生成された動物に何かさせる
        System.out.println("動物が準備できました。");
    }
}

// ステップ4: 具象ファクトリー(具体的な工場)を実装
// 各ファクトリーが、特定の種類のAnimalオブジェクトを生成する責任を持つ
class DogFactory extends AnimalFactory {
    @Override
    public Animal createAnimal() {
        return new Dog(); // Dogオブジェクトを生成
    }
}

class CatFactory extends AnimalFactory {
    @Override
    public Animal createAnimal() {
        return new Cat(); // Catオブジェクトを生成
    }
}

// ステップ5: クライアントコード(オブジェクトを利用する側)
public class FactoryMethodExample {
    public static void main(String[] args) {
        System.out.println("--- Dogの準備 ---");
        // DogFactoryを使ってDogオブジェクトを生成し、準備処理を実行
        // ここではcreateAnimal()を直接呼び出す例
        AnimalFactory dogFactory = new DogFactory();
        Animal dog = dogFactory.createAnimal(); // createAnimal()を使ってDogが作られる
        dog.makeSound();

        System.out.println("\n--- Catの準備 ---");
        // CatFactoryを使ってCatオブジェクトを生成し、準備処理を実行
        AnimalFactory catFactory = new CatFactory();
        Animal cat = catFactory.createAnimal(); // createAnimal()を使ってCatが作られる
        cat.makeSound();

        // 例えば、新しい動物(Lion)を追加したい場合
        // LionクラスとLionFactoryクラスを追加するだけで、既存のクライアントコード(このmainメソッド)
        // を変更する必要はない。(AnimalFactory型の変数を使ってprepareAnimal()を呼ぶ限り)
        System.out.println("\n--- 共通の準備メソッドの利用(実践的な使い方)---");
        // prepareAnimal()メソッドは、内部でcreateAnimal()を呼び出す
        // クライアントはこのメソッドを呼び出すだけで、具体的な生成処理を意識しなくて済む
        dogFactory.prepareAnimal();
        catFactory.prepareAnimal();
    }
}

ボット君: わー!new Dog()とかnew Cat()って直接書かずに、createAnimal()っていうメソッドでオブジェクトを作ってる!しかもAnimalFactoryって抽象クラスがあって、それを継承したDogFactoryやCatFactoryが具体的なオブジェクトを作ってるんですね!これなら、もしAnimalの作り方が変わっても、DogFactoryの中だけ直せばいいし、新しい動物が増えてもAnimalFactoryを実装した新しい工場クラスを作ればいいだけなんですね!

先生: その通り!クライアントコード(FactoryMethodExampleのmainメソッド)は、具体的なDogFactoryやCatFactoryを知っているけれど、生成されたオブジェクトがDogなのかCatなのかは、Animalインターフェースとして扱っている。これにより、クライアントコードは「どんな動物が作られるか」に依存せず、「動物が作られる」という事実にだけ依存するようになるんだ。

先生: これが、ファクトリーメソッドパターンが「オブジェクト作りの工場」と呼ばれる理由であり、プログラムの柔軟性と拡張性を高める強力な仕組みなんだ。


今日のまとめ!

  • デザインパターン:

    • ソフトウェア開発でよく直面する問題に対する「再利用可能な解決策」の集まり。

    • 「良い設計」の知恵袋であり、開発者間の共通言語となる。

  • ファクトリーメソッドパターン:

    • オブジェクトの生成処理を、サブクラスに任せるデザインパターン。

    • 目的: オブジェクト生成の処理をクライアントコードから切り離し、柔軟性と拡張性を高める。

    • メリット:

      • クライアントコードは具体的なクラス名を知らなくてもオブジェクトを生成できる。

      • 新しい種類のオブジェクトを追加する際に、既存のクライアントコードを変更する必要がない。

      • オブジェクトを利用する側と生成する側の結合度が低下する。

    • 仕組み: オブジェクトを生成する抽象メソッド(ファクトリーメソッド)を抽象ファクトリークラスで定義し、それを継承した具象ファクトリークラスが具体的なオブジェクトを生成する。


ボット君: 先生、今日はデザインパターンとファクトリーメソッドパターンについて、すごくよく分かりました!オブジェクトの作り方にもこんなに賢い方法があるなんて、びっくりです!これで、もっと変更に強いプログラムが書けそうな気がします!

先生: その調子だ、ボット君!デザインパターンは、まさに「先人たちの知恵」の結晶だ。ファクトリーメソッドパターンは、その中でも最も基本的でよく使われるものの一つだよ。次回は、オブジェクト指向の設計におけるもう一つの重要な概念、「みんなで使う共通の道具」とも言えるstatic修飾子とユーティリティクラスについて学んでいこう!

ボット君: 共通の道具!なんだか便利そうです!楽しみです!ありがとうございました!


次回:第11回: みんなで使う共通の道具~staticとユーティリティクラス

#オブジェクト指向 #プログラミング初心者 #Java #クラスとメソッド #プログラミング学習 #入門


いいなと思ったら応援しよう!

ナオユキ|物語と知識のコンシェルジュ もしこの記事が少しでも役に立ったり、楽しんでいただけたりしたら、チップをいただけるととても嬉しいです! いただいたチップは、今後のより良いコンテンツ制作のための書籍代📚とnote購入🗒️、他の方へのチップ代💰に使わせていただきます。 いつも応援ありがとうございます!