オブジェクト指向で大切になるInterfaceの考え方やオブジェクトの再利用性を学ぶために「Java言語で学ぶデザインパターン入門」について学び、Javaとついでにkotlinで書いてみることにしました。
今回はFactoryについて書いてみます。
##Factoryとは
Templateパターンだとスーパークラスで処理の枠組み(似たような共有の処理)を定め、サブクラスで具体的な内容を定めるようなデザインパターンだったのに対し、インスタンス生成のための枠組み(フレームワーク)を定めるデザインパターン。
下記はインスタンス生成の枠組みと、実際に「IDカード(インスタンス)を作る」サブクラスについて実装していきます。
Productクラス
このクラスでは「製品」を定義した抽象クラスで、製品は「使う」事を前提としたuseメソッドが定義されている。
また、抽象化することで製品としての役割として「使用」できることを強制することができる。(という意図を感じました。)
kotlinだとvoidとしてのUnitを指定せずとも、戻り値を指定していない関数の型はUnitになるとの事です。またIDEでも冗長だと指摘されました。
abstract class Product {
public abstract void use();
}
abstract class Product {
abstract fun use()
}
Factoryクラス
ここではインスタンス生成の枠組みを実装していきます。
ポイントは「製品を作る」事と「製品と登録する」事はサブクラスの実装に任せているが、createメソッドでの手順はFactoryクラスでfinalとして「定義が決まっている」意図を読み取ることができます。
Kotlinとjavaのprotectedの違いはJavaだとサブクラスor同じパッケージのクラスからアクセスできるのに対し、Kotlinだとサブクラスからのみアクセスとの事。
ここではサブクラスからのみアクセスできるkotlinのprotectedを使用する。
また、Javaだとpublic finalなcreateメソッドだが、kotlinはdefault
でpublic finalなメソッドのため指定しない。
abstract class Factory {
public final Product create(String owner) {
Product p = createProduct(owner);
registerProduct(p);
return p;
}
protected abstract Product createProduct(String owner);
protected abstract void registerProduct(Product product);
}
abstract class Factory {
fun create(owner: String): Product {
val p = createProduct(owner)
registerProduct(p)
return p
}
protected abstract fun createProduct(owner: String): Product
protected abstract fun registerProduct(product: Product)
}
IDCardクラス
Productクラスを継承した実際の製品としてのサブクラスを定義していきます。
kotlinでコンストラクタの初期化処理をする場合はinit
を定義する。
class IDCard extends Product {
private String owner;
public IDCard(String owner) {
System.out.println(owner + "のカードを作ります。");
this.owner = owner;
}
public void use() {
System.out.println(owner + "のカードを使います。");
}
public String getOwner() {
return owner;
}
}
class IDCard (private val owner: String): Product() {
init { println(owner + "のカードを作ります。") }
override fun use() = println(owner + "のカードを使います。")
fun getOwner() = owner
}
IDCardFactoryクラス
インスタンス生成の枠組みであるFactoryクラスを継承し具体的な処理を実装します。
インスタンスを生成し、製品を実際に作るcreateProductメソッドと、ownersフィールドで登録を実現しているregisterProductメソッドが実装されています。
KotlinだとListはread onlyらしく、追加可能なmutableListを使用します。
参考:KotlinとList
また、Kotlinはインスタンス生成時にnewを使用しない。
また、キャストにおいてinstanceofの代わりにコンパイラがよしなに対応してくれるsmart castを実装しました。
class IDCardFactory extends Factory {
private List<String> owners = new ArrayList<String>();
@Override
protected Product createProduct(String owner) {
return new IDCard(owner);
}
@Override
protected void registerProduct(Product product) {
owners.add(((IDCard)product).getOwner());
}
public List<String> getOwners() {
return owners;
}
}
class IDCardFactory: Factory() {
private var owners: MutableList<String> = mutableListOf()
override fun createProduct(owner: String) = IDCard(owner)
override fun registerProduct(product: Product) {
if(product is IDCard) owners.add(product.getOwner()) //smart cast
}
fun getOwners() = owners
}
Mainクラス
IDカードを作成していきます。
public class FactorySample {
public static void main(String[] args) {
Factory factory = new IDCardFactory();
Product card1 = factory.create("佐藤");
Product card2 = factory.create("鈴木");
Product card3 = factory.create("田中");
card1.use();
card2.use();
card3.use();
((IDCardFactory)factory).getOwners().stream().forEach(System.out::println);
}
}
fun main(args: Array<String>){
val factory = IDCardFactory()
val card1 = factory.create("佐藤")
val card2 = factory.create("鈴木")
val card3 = factory.create("田中")
card1.use()
card2.use()
card3.use()
factory.getOwners().forEach(System.out::println)
}
佐藤のカードを作ります。
鈴木のカードを作ります。
田中のカードを作ります。
佐藤のカードを使います。
鈴木のカードを使います。
田中のカードを使います。
佐藤
鈴木
田中
クラス図
所感
-
Factoryクラスは実際に生成するIDCardクラスについての記述はなく、あくまでProductとインスタンス生成のメソッドを呼び出せばProductが生成されるので、具体的なクラス名による束縛がない事がメリットである事を学んだ。
-
上記であまり触れてなかったのですが、FactoryとProductを同じパッケージにし、IDCardとIDCardFactoryを別パッケージ(例えばframeworkパッケージとidcardパッケージ)で定義した場合、具体的なクラスやパッケージによる束縛がないことを「framworkパッケージはidcardパッケージに依存していない」と表現する事を学んだ。
-
Kotlinについては下記の点を学ぶことができた
- 戻り値を指定しない場合はvoidである
Unit
がデフォルトで指定されているので定義する必要はない -
protected
の仕様がJavaとは異なる - メソッドの定義はデフォルトで
public final
- コンストラクタの初期化処理をする場合は
init
を使用する -
List
はread only、mutableList
は追加可能 - インスタンス生成時にnewを使用しない
- キャストは
smart cast
が便利
##参考
下記を参考にさせて頂き、大変読みやすく、理解しやすかったです。
Kotlinの型を知る ~前編~
kotlinとjavaのアクセス修飾子の関係性
【Kotlin】コンストラクタの書き方
KotlinとList
Kotlin文法 - クラス、継承、プロパティ
JavaプログラマがKotlinでつまづきがちなところ