🕌

Java デザインパターン 僕なりの4つを説明①

2022/01/01に公開

初めまして、mikeです。
軽く私の自己紹介をします。25歳で Web系の受託開発メインのベンチャーに勤めています。
得意な言語としては、JavaとPHP、Javascriptです。
現在は、FlutterやRuby, Goなどを学習しています。

早速ですが、本題に入ります。
Javaデザインパターン徹底攻略に沿ってオブジェクト指向については知っているという前提のもと話を進めていきます。
かなり古めの本ですが、Javaデザインパターンの本を読む機会がありましたので僕なりに使えそうなパターン4種をアウトプットするという形でまとめていきます。
(こちらN番煎じのため似ている表現があるかもしれませんが最後まで見ていってください。)
長くなりそうなので二回に分けて投稿します。

※あくまで例文ですので、実際の開発現場でこういう風に使われるといった記事ではありませんのでお気をつけください。

目次

  1. Factory パターン
  2. Builder パターン
    次回↓
  3. Singleton パターン
  4. Facade パターン

1. Factoryパターン

このパターンは「正しいペアとなるオブジェクトを生成するためのメソッド」
こちらは継承されることを前提に話していきます。
サブクラスが数種類ある場合、条件分岐によってインスタンスをnew(生成)しているとサブクラスが増えるたびに条件分岐をさせるのでコードの記述量が増え可読性が下がります。
今回は商品に消費税を設定する例の中でFactoryパターンを入れて話していきます。

// 消費税に関するクラス
abstract class Percent {
    abstract int getTaxIncludes();
}

// 消費税を継承した具象クラス
class ItemEightPercent extends Percent {

    int getTaxIncludes() {
        return (int) (100 * 1.08);
    }

}

// 消費税を継承した具象クラス
class ItemTenPercent extends Percent {

    int getTaxIncludes() {
        return (int) (100 * 1.10);
    }

}
// 商品に関するクラス
abstract class Item {
    abstract String getItemName();

    abstract int getPrice();

    abstract Percent createPercent();
}

// 消費税10%の商品
class Onigiri extends Item {

    String getItemName() {
        return "おにぎり";
    }

    int getPrice() {
        return 100;
    }

    Percent createPercent() {
        return new ItemTenPercent();
    }

}

// 消費税8%の商品
class Water extends Item {

    String getItemName() {
        return "お水";
    }

    int getPrice() {
        return 100;
    }

    Percent createPercent() {
        return new ItemEightPercent();
    }

}
public class FactoryMain {
    public static void main(String[] args) {
        // 消費税10%
        Item onigiri = new Onigiri();
        Percent taxPrice1 = onigiri.createPercent();
        System.out.println("商品の名前は" + onigiri.getItemName());
        System.out.println("商品の価格は" + onigiri.getPrice());
        System.out.println("商品の税込は" + taxPrice1.getTaxIncludes());
        // 消費税8%
        Item water = new Water();
        Percent taxPrice2 = water.createPercent();
        System.out.println("商品の名前は" + onigiri.getItemName());
        System.out.println("商品の価格は" + onigiri.getPrice());
        System.out.println("商品の税込は" + taxPrice2.getTaxIncludes());
    }
}

サブクラスがファクトリメソッドを持つことで誤って違うペアのインスタンスを生成することがなくなります。またクライアントはメソッド名さえ知っていればペアとなるインスタンスを生成することができます。
そして、消費税が11%、12%と増えていっても同じように拡張していけばいいのでFactoryパターンは「拡張性が豊かである」ということが言えます。

2. Builderパターン

このパターンは「複雑な手順でオブジェクトの生成」です。
通常のコンストラクタでオブジェクトを生成する場合、一回きりのコンストラクタ呼び出しでオブジェクトの生成を済まさなければならない。そのため、コンストラクタの選択が重荷になってしまいます。
そこで、今回の例ではミドルネームを持つユーザとミドルネームを持たないユーザの生成から表示までを例で話したいと思います。

class MyNameClass {
    private String name;

    MyNameClass(String name) {
        this.name = name;
    }

    void dispName() {
        System.out.println("名前は:" + name);
    }
}

/*
 * 通常のコンストラクタだとミドルネームがある場合とない場合で処理を変えないといけない。
 * その時に↓ビルダクラスを作成する。
 */
class MyNameBuilder {
    String name = "";

    void addFirstName(String firstName) {
        this.name += firstName;
    }

    void addMiddleName(String middleName) {
        this.name += middleName;
    }

    void addLastName(String lastName) {
        this.name += lastName;
    }

    MyNameClass getMyNameClass() {
        MyNameClass myNameClass = new MyNameClass(name);
        name = "";
        return myNameClass;
    }
}
// ビルダクラスの呼び出す順番、ルールを定義したクラスを「ディレクタクラス」と呼ぶ
class MyNameDirectorA {
    MyNameClass createMyNameClass(MyNameBuilder builder) {
        builder.addFirstName("Itiro");
        builder.addLastName("Suzuki");
        return builder.getMyNameClass();
    }
}

class MyNameDirectorB {
    MyNameClass createMyNameClass(MyNameBuilder builder) {
        builder.addFirstName("Itiro");
        builder.addMiddleName("Steve");
        builder.addLastName("Suzuki");
        return builder.getMyNameClass();
    }
}
public class BuilderMain {
    public static void main(String[] args) {
        MyNameBuilder builder = new MyNameBuilder();

        // フルネームを持つMyNameClassオブジェクトの生成
        MyNameDirectorA dirA = new MyNameDirectorA();
        MyNameClass class2 = dirA.createMyNameClass(builder);
        class2.dispName();

        // フルネームを持たないMyNameClassオブジェクトを生成
        MyNameDirectorB dirB = new MyNameDirectorB();
        MyNameClass class1 = dirB.createMyNameClass(builder);
        class1.dispName();
    }
}

この例では、ビルダクラスが1種類しかありませんが、将来ビルダクラスの方も種類を増やすことを考えて、ディレクトリクラスとビルダクラスのペアも自由に変えられるようにしております。
ディレクトリクラスを作ることでビルダクラスのメソッドを不正な順番で呼び出してしまうことを防げます。
クライアントプログラム側では、自分が作りたい種類のオブジェクトを生成してくれるディレクトリクラスを選択して希望のオブジェクトを生成してもらえるということになります。

あくまで参考程度に見てもらえると助かります。
また、現在ではこういう使い方をしている。やこの使い方は今はしていないなどのご指摘がありましたらコメントまでお願いいたします。

次回、SingletonパターンとFacadeパターンについての記事を上げたいと思います。
こちらの記事を見てくださった方ありがとうございます。

Discussion