2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Kotlinでデザインパターン Prototype編

Last updated at Posted at 2019-03-22

はじめに

本記事はJava言語で学ぶデザインパターン入門を参考にしながら
JavaではなくKotlinで実装してみようというものです。

Java言語で学ぶデザインパターン入門

調べてみる

目的

  • 予め生成したインスタンスから新たなインスタンスを作成するパターンがPrototypeです。
    (簡単に言えばインスタンスをコピー(クローン)するためのパターンです。)

  • このパターンを適応するとインスタンスからコピーを作成しますので、
    GUIアプリケーションなどで図形を複製する場合などに使えます。

  • もちろんクラスからインスタンスを生成することも可能ではありますが、
    インスタンスをそのままコピーするほうが色々手間が省けるよねってときにPrototypeを利用します。

構成

クラス名 説明
Client Protoypeを使ってインスタンスをコピーするクラス
Prototype どのようなメソッドでインスタンスをコピーするか決めるインタフェース
ConcreatePrototype Prototypeインタフェースのメソッドに合わせて、実際にどうやってコピーするか実装するクラス

Prototype.png

実装してみる

Javaだとどうだたったか?

JavaですとClonableというマーカインタフェースを継承し、
継承したクラスでCloneを正常に呼び出せるようになりShallow Copyでコピーできるようになります。

Clonableを継承しないクラスでもCloneを呼び出すことはできますが、
”CloneNotSupportedException”が発生しますので正常にコピーできません。

"Java言語で学ぶデザインパターン入門"にならってJavaで実装すると次のような感じです。

Product

Cloneableを継承したProductクラスを用意します。
あとは外部クラスからCloneを実行するためにcreateCloneを用意しておきます。

public interface Product extends Cloneable {
    public abstract Product createClone();
}
ProductDefault

Productクラスを実装したProductDefaultクラスを作成します。
createCloneではcloneを呼び出してインスタンスをShallow Copyします。

先程説明したとおりCloneableを継承してないと
"CloneNotSupportedException"が発生するので例外をCatchしておきます。

public class ProductDefault implements Product {

    public ProductDefault() { }

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

ProductDefaultクラスの動作を確認してみますと、
違うインスタンスが作成できていますので正常に動作しているようです。

public class Main {
    public static void main(String[] args) {
        ProductDefault p = new ProductDefault();
        Product c = p.createClone();
        System.out.println("-- Clone Test --");
        System.out.println("pとpは" + ((p == p) ? ("同じ") : ("別の"))+ "インスタンス");
        System.out.println("pとcは" + ((p == c) ? ("同じ") : ("別の"))+ "インスタンス");
    }
}

-- Clone Test --
pとpは同じインスタンス
pとcは別のインスタンス
p:1625635731 c:1580066828

Kotlinだとどうするか?

KotlinでもJavaと同様にCloneableを継承すればCloneできるようになります。
Javaと同じようにKotlinでも実装していきます。

Product
abstract class Product : Cloneable {
    abstract fun createClone() : Product
}
ProductDefault
class ProductDefault : Product() {
    override fun createClone(): ProductDefault {
        try {
            return clone() as ProductDefault
        } catch (e : CloneNotSupportedException) {
            throw e
        }
    }
}
Main
fun main(args: Array<String>) {
    val p = ProductDefault()
    val c = p.createClone()
    println("-- Class Clone Test --")
    println("pとpは${if (p === p) "同じ" else "別の"}インスタンス")
    println("pとcは${if (p === c) "同じ" else "別の"}インスタンス")
}

-- Class Clone Test --
ppは同じインスタンス
pcは別のインスタンス

KotlinだとData ClassのCopyがあるのでそっちも使える

KotlinですとData Classがサポートされています。
Data Classであればcopyがあるのでこれをインスタンスのコピーに使えます。
ですからKotlinですとProrotypeを使べきシチュエーションが少なくなっているかもしれません。

data class ProductData(val name : String, val serial : String)

```kotlin
fun main(args: Array<String>) {
    val data = ProductData("iPhone", "0000-1111-2222-3333")
    val copyData = data.copy()
    println("-- Data Class Clone Test --")
    println("dataとdataは${if (data === data) "同じ" else "別の"}インスタンス")
    println("dataとcopyDataは${if (data === copyData) "同じ" else "別の"}インスタンス")
}

-- Data Class Clone Test --
datadataは同じインスタンス
datacopyDataは別のインスタンス

おわりに

  • インスタンスをコピーしたいときはCloneableを継承したクラスを実装する
  • Cloneable継承クラスでしかCloneが呼び出せないので、外部クラスがCloneできるようにメソッドを生やす
  • Cloneで実行されるコピーはShallow Copyなので注意が必要、Deep Copyするならば別途実装が必要になる

余談:ちょっとわからなかったところ

ちなみにKotlinでProductをinterfaceで実装しようとするとNoClassDefFoundErrorが発生します。
おそらくCloneableを継承するのはクラスでなければならないみたいです。
この内容を深掘りすると長そうなのと、筆者の理解がまだ追いついていないので本記事では省略します。

interface Product : Cloneable {
    fun createClone() : Product
}
Exception in thread "main" java.lang.NoClassDefFoundError: java/lang/Cloneable$DefaultImpls
	at main.Product$DefaultImpls.clone(Product.kt)
	at main.ProductDefault.clone(ProductDefault.kt:3)
	at main.ProductDefault.createClone(ProductDefault.kt:6)
	at MainKt.main(main.kt:6)
Caused by: java.lang.ClassNotFoundException: java.lang.Cloneable$DefaultImpls
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 4 more
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?