dasukoの技術ブログ

現役エンジニアのブログです。

Prototypeパターン

Prototypeパターンとは

プログラミングのデザインパターンの一つです。

Prototypeは原型、典型という意味で、生成されるオブジェクトがプロトタイプなインスタンスである時に使用され、このプロトタイプを複製して新しいオブジェクトを生成します。

このパターンは主に以下のような場面で使われます。

  • Factory Methodパターンのようにオブジェクトの生成をサブクラスが行いたくない
  • newなどで新しいオブジェクトを作ることによるコストが高い

FactoryMethodパターンの場合は新しいオブジェクトのクラスが追加されるたびにサブクラスを定義する必要があります。

そのため、その追加作業の効率が悪い場合にPrototypeパターンを使ったりします。

クライアントはnew演算子で特定のクラス名でインスタンス化をする代わりに、プロトタイプのclose()メソッドを呼び出します。

オブジェクトを使用するクラス内で直接オブジェクトを生成する場合は、クラス名を明示的に宣言する必要があります。 そのため、柔軟性がありません。

Prototypeオブジェクトをコピーして新しいオブジェクトを生成することにより、様々な型のPrototypeオブジェクトの生成ができます。

Class図

f:id:dasuko:20200506053026p:plain
Class Diagram

使用例

以下の例では、ゲームでたくさん使用しそうな障害物(Obstacle)をPrototypeとして実装した例です。

C#での実装例

/// <summary>
/// Prototype
/// </summary>
public abstract class Prototype
{
    public abstract Prototype Clone();
}

/// <summary>
/// 障害物
/// </summary>
public class Obstacle : Prototype
{
    public override Prototype Clone()
    {
        return this.MemberwiseClone() as Prototype;
    }
}

Javaでの実装例

// Prototype
public class Prototype implements Cloneable {
    public Prototype clone() throws CloneNotSupportedException {
        return (Prototype)super.clone();
    }
}

// 障害物
public class Obstacle extends Prototype {
    @Override
    public Prototype clone() throws CloneNotSupportedException {
        return (Obstacle)super.clone();
    }
}

まとめ

基本的にこのPrototypeパターンを用いる場合は、DeepCopyよりもShallowCopyをしたい場合だと思いますが、 どちらでコピーするかは注意が必要です。 実装時だけでなく、使用するときもそのPrototypeの実装がDeepCopyなのかShallowCopyなのかは注意した方がいいと思います。

またDeepCopyが不要な場合にはできるだけShallowCopyを使用するようにした方がパフォーマンス的にいいので意識したいです。

  

ちなみに、ShallowCopyはコピー元のオブジェクトの参照をコピーします。

つまり、コピー先のオブジェクトでインスタンス変数に変更を加えると、コピー元オブジェクトのインスタンス変数の値も変更されます。

DeepCopyはオブジェクトとメモリ上のデータの両方をコピーします。

そのため、インスタンス変数も全く別のものになります。

  

C#では以下のようにDeepCopyとShallowCopyを実装します。

(Scoreクラスはコピーコンストラクタを実装しているとします)

// User Class
public class User 
{
    public int age;
    public string name;
    public Score score;

    // ShallowCopy
    public User ShallowCopy()
    {
       return (User) this.MemberwiseClone();
    }
    
    // DeepCopy
    public User DeepCopy()
    {
       User other = (User) this.MemberwiseClone();
       other.score = new Score(score);
       other.name = String.Copy(name);
       return other;
    }
}

参考

Prototype pattern - Wikipedia

Prototype パターン - Wikipedia