Help us understand the problem. What is going on with this article?

デザインパターン「Prototype」

More than 3 years have passed since last update.

はじめに

本投稿はJava言語で学ぶデザインパターン入門のデザインパターンをまとめた記事です。今回はPrototypeパターンになります。
まとめ一覧はこちら

Prototypeパターン

スクリーンショット 2016-02-14 15.31.29.png

Prototypeとは

Prototypeとはインスタンスを生成する時のデザインパターンで、クラスからインスタンスを生成するのではなく、インスタンスから別のインスタンスを(コピーして)生成するパターンのことです。

Prototypeと言う英単語の意味

Weblio辞書参照

原型、模範、原形

Prototypeの意味を見てわかるように単にインスタンスを生成するのではなく、原型や元となるインスタンスを元に新しいインスタンスを生成します。

Prototypeパターンのクラス図

スクリーンショット 2016-02-14 15.53.14.png

  • Prototype: インスタンスを複製して、新しいインスタンスを生成するメソッドを定める
  • ConcretePrototype: 実際にインスタンスを複製して、新しいインスタンスを生成するメソッドを実装
  • Client: 実際にインスタンスを複製して新しいインスタンスを生成する役割を担う

具体例

本書に記載されている例を参考に、与えた文字に対して下線を引いて表示するクラスと枠線で囲って表示するクラスを用いて紹介していきます

Mainクラス

use()メソッドに渡した文字をクラスの生成時に渡した記号で、装飾しています

Main.java
public static void main(String[] args) {

        Manager manager = new Manager();

        UnderLinepen upen = new UnderLinePen('-');
        MessageBox mbox = new MessageBox('@');
        MessageBox sbox = new MessageBox('♪');

        manager.register("stong message", upen);
        manager.register("warning box", mbox);
        manager.register("slash box", sbox);

        Product p1 = manager.create("stong message");
        p1.use("Git Hub");
        Product p2 = manager.create("warning box");
        p2.use("Qiita");
        Product p3 = manager.create("slash box");
        p3.use("Java");
}

Productインターフェイス

複製のためのメソッドを定めるインタフェース

public interface Product extends Cloneable {

    public abstract void use(String s);
    public abstract Product createClone();
}
  • createClone() 実際にインスタンスを複製する
  • use()に文字列を渡すとその文字列を装飾して表示する

java.lang.Cloneableを継承していることで、Clone()メソッドでインスタンスを複製できます

Interface Cloneable

Managerクラス

Productインタフェースを利用してインスタンスの複製を行う

Manager.java
public class Manager {
    private HashMap showcase = new HashMap();
    public void register(String name, Product prototype) {
        showcase.put(name, prototype);
    }

    public Product create(String protoname) {
        //※
        Product p = (Product)showcase.get(protoname);
        return p.createClone();
    }
}

  • 複製元のインスタンスを保持しとくためのshowcaseを定義。

※Productのインスタンスをクローンすることで、クラス名に依存することなくProductインターフェイスを実装したクラスを利用することができる。

MessageBoxクラス

  • 文字列の周りを枠線で囲む
  • Productインターフェースを実装
MessageBox.java
public class MessageBox implements Product {
    private char decoChar;
    public MessageBox(char decoChar) {
        this.decoChar = decoChar;
    }

    @Override
    public void use(String string) {
        int length = string.getBytes().length;
        for (int i = 0; i < length + 4; i++) {
            System.out.print(decoChar);
        }
        System.out.println("");
        System.out.println(decoChar + " " + string + " " + decoChar);

        for (int i = 0; i < length + 4; i++) {
            System.out.print(decoChar);
        }
        System.out.println("");
    }

    //Productインターフェイスを実装してなければCloneNotSupportedExceptionが投げられる
    @Override
    public Product createClone() {
        Product p = null;
        try {
            p = (Product)clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }
}

UnderLinePenクラス

  • 文字列に下線を表示
  • Productインターフェースを実装
UnderLinePen.java
public class UnderLinePen implements Product {
    private char ulChar;

    public UnderLinePen(char ulChar) {
        this.ulChar = ulChar;
    }

    @Override
    public void use(String string) {
        int length = string.getBytes().length;
        System.out.println(string);
        System.out.print("");
        for (int i = 0; i < length; i++) {
            System.out.print(ulChar);
        }
        System.out.println("");

    }

    @Override
    public Product createClone() {
        Product p = null;
        try {
            p = (Product)clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }

}

実行結果

Git Hub
-------
@@@@@@@@@
@ Qiita @
@@@@@@@@@
♪♪♪♪♪♪♪♪
 Java 
♪♪♪♪♪♪♪♪

追記

本書では以下のような場合にPrototypeパターンを利用すべきだと記述してあります

  • 種類が多すぎてクラスにまとめられない場合
  • クラスからのインスタンス生成が難しい場合
  • フレームワークと生成するインスタンスを分けたい場合

例えば、図形を描くエディターのようなアプリケーションでは、図形をコピーして描画する機能があると思います。図形などをコピーする際は元の図形を「プロトタイプ」として登録し、コピーしたほうが簡単にできます。

  • 状態を持つインスタンスをコピーするする場合のみ有用

無名クラスだとそもそも状態を持たないので複製する必要がない。無名クラスの場合はファクトリメソッドを使うべき

サンプルコードについて

以下のレポジトリにソースコードをアップしてあります。

shoheiyokoyama/design-pattern_java

デザインパターン

参考文献

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away