Design Pattern | Prototype Pattern(プロトタイプパターン)

Prototypeパターンとは?

  • Prototypeという英単語は、原型や模範という意味である。
  • Prototypeパターンは、new Xxx()でクラスからインスタンスを生成するのではなく、インスタンスから別のインスタンスを生成する方式である。つまり、コピーしてインスタンスを作る。
  • 複製を作る作業をcloneと言う。
  • GoFデザインパターンでは、生成に関するデザインパターンに分類される。

Prototypeパターンのサンプルプログラム

入力した文字列に下線を引いたり、囲んだりするサンプルプログラムである。

Class Diagram
Prototype Pattern Class Diagram

1. Productインターフェース

複製メソッドを定義するインターフェースである。JavaのCloneableインターフェースを継承する。

Product.java

package com.devkuma.designpattern.creational.prototype.framework;

import java.lang.Cloneable;

public interface Product extends Cloneable {
    void use(String s);

    Product createClone();
}

2. Managerクラス

Productの生成指示や管理を行うクラスである。

Manager.java

package com.devkuma.designpattern.creational.prototype.framework;

import java.util.HashMap;

public class Manager {

    private HashMap showcase = new HashMap();

    public void register(String name, Product prototype) {
        showcase.put(name, prototype);
    }

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

3. UnderlinePenクラス

Productインターフェースを実装するクラスである。このクラスは「文字に下線を引く」クラスになる。

UnderlinePen.java

package com.devkuma.designpattern.creational.prototype.product;

import com.devkuma.designpattern.creational.prototype.framework.Product;

public class UnderlinePen implements Product {

    private char underlineChar;

    public UnderlinePen(char underlineChar) {
        this.underlineChar = underlineChar;
    }

    public void use(String s) {

        int length = s.getBytes().length;

        System.out.println(s);

        for (int i = 0; i < length; i++) {
            System.out.print(underlineChar);
        }

        System.out.println();
    }

    public Product createClone() {
        Product p = null;
        try {
            p = (Product) clone();
        } catch (CloneNotSupportedException e) {
            // オブジェクトのクラスがCloneableインターフェースを実装していない場合にthrowされる例外。
            e.printStackTrace();
        }
        return p;
    }
}

4. MessageBoxクラス

Productインターフェースを実装するクラスである。このクラスは「文字を囲む」クラスになる。

MessageBox.java

package com.devkuma.designpattern.creational.prototype.product;

import com.devkuma.designpattern.creational.prototype.framework.Product;

public class MessageBox implements Product {

    private char decoChar;

    public MessageBox(char decoChar) {
        this.decoChar = decoChar;
    }

    public void use(String s) {

        int length = s.getBytes().length;

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

        System.out.println();
        System.out.println(decoChar + s + decoChar);

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

        System.out.println();
    }

    public Product createClone() {
        Product p = null;
        try {
            p = (Product) clone();
        } catch (CloneNotSupportedException e) {
            // オブジェクトのクラスがCloneableインターフェースを実装していない場合にthrowされる例外。
            e.printStackTrace();
        }
        return p;
    }
}

5. Mainクラス

メイン処理を実行するクラスである。

Main.java

package com.devkuma.designpattern.creational.prototype;

import com.devkuma.designpattern.creational.prototype.framework.Manager;
import com.devkuma.designpattern.creational.prototype.framework.Product;
import com.devkuma.designpattern.creational.prototype.product.MessageBox;
import com.devkuma.designpattern.creational.prototype.product.UnderlinePen;

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

        Manager manager = new Manager();
        UnderlinePen upen = new UnderlinePen('~');
        MessageBox mbox = new MessageBox('*');
        MessageBox pbox = new MessageBox('+');
        manager.register("strong message", upen);
        manager.register("warning box", mbox);
        manager.register("slash box", pbox);

        Product p1 = manager.create("strong message");
        p1.use("Hello world");
        System.out.println();
        Product p2 = manager.create("warning box");
        p2.use("Hello world");
        System.out.println();
        Product p3 = manager.create("slash box");
        p3.use("Hello world");
    }
}

6. 実行結果

Hello world
~~~~~~~~~~~

*************
*Hello world*
*************

+++++++++++++
+Hello world+
+++++++++++++

Prototypeパターンのメリット

サンプルプログラムでは比較的単純な場合だったが、インスタンスを生成するうえで複雑な処理が必要だったり、時間がかかる処理が必要だったりすることもある。インスタンスを生成するたびにnew Xxx()を使うのは非常に非効率である。
Prototypeを作ってコピーすれば、簡単にインスタンスを生成できる。