はじめに
前々からデザインパターンについてまとめたいなぁと思ってたので下記のデザインパターンについてまとめようと思います。(正直あんまよくわかってないです。。。)
今回はオブジェクトの生成に関するパターンについて。
- オブジェクトの生成に関するパターン
- Singleton パターン
- Abstract Factory パターン
- Builder パターン
- Prototype パターン
- Factory Method パターン
- オブジェクトの構造に関するパターン
- Adapter パターン
- Bridge パターン
- Composite パターン
- Decorator パターン
- Facade パターン
- Flyweight パターン
- Proxy パターン
- オブジェクトの振る舞いに関するパターン
- Iterator パターン
- Command パターン
- Chain of Responsibility パターン
- Memento パターン
- Observer パターン
- Mediator パターン
- Interpreter パターン
- State パターン
- Strategy パターン
- Template Method パターン
- Visitor パターン
基本的には下記2つを参考に書いてます。
Singleton パターン
アプリ全体で1つのインスタンスを共有する仕組み。
最初に呼ばれた一回のみインスタンスを生成し、それ以降は同じインスタンスを参照する。
1 2 3 4 5 6 |
// 継承を禁止 final class Singleton { static let shared = Singleton() // 外部からのインスタンス生成を禁止 private init() {} } |
インスタンスが1つのみであることを保証する仕組みなので外部から init
が呼べてはいけないし、サブクラスを作れてもいけません。
どこから呼んでも同じものが得られるという点では setter を持ったプロパティを持ってはいけないし、イニシャライザで引数を持ってもいけないらしい。
iOS だと下記のやつとかが Singleton っぽい
1 2 3 4 5 |
UIApplication.shared URLSession.shared FileManager.default NotificationCenter.default UserDefaults.standard |
shared
, default
, standard
とか名前にばらつきがあるがきっと Singleton だと思う。
FileManager
に関しては singleton instance と書いてある。
1 2 3 4 |
open class FileManager : NSObject { /* Returns the default singleton instance. */ open class var `default`: FileManager { get } |
グローバル変数っぽい使い方しか見たことないので具体的な使い道はわからない。。。
Abstract Factory パターン
名前の通り抽象的な工場なんだたぶん。あるクラスが何かほしいとき下記のような処理になる。
あるクラスが何かほしい -> 工場に依頼 -> 工場で生成
このときあるクラスは具体的な工場を知る(依存する)必要はなく抽象的な工場を知っていればいい。(抽象に依存せよってやつだと思う)
こんな感じかな?(ちょっとよくわかってない。。。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
protocol AbstractFactory { func makeAPIClient() -> APIClient } struct DefaultAPIClientFactory: AbstractFactory { func makeAPIClient() -> APIClient { return DefaultAPIClient() } } struct DummyAPIClientFactory: AbstractFactory { func makeAPIClient() -> APIClient { return DummyAPIClient() } } protocol APIClient { } struct DefaultAPIClient: APIClient { } struct DummyAPIClient: APIClient { } class Piyo { let apiClient: APIClient init(factory: AbstractFactory) { self.apiClient = factory.makeAPIClient() } } class Hoge { func hogehoge() { let piyo = Piyo(factory: DefaultAPIClientFactory()) } } |
Builder パターン
全体の処理手順は Director に、個別の構築処理は Builder に分担し、この2つを組み合わせてオブジェクトを生成する仕組みらしい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
protocol Animal { var name: String { get } var age: Int { get } var voice: String { get } } struct Dog: Animal { let name: String let age: Int let voice: String } protocol AnimalBuilder { var name: String? { get set } var age: Int? { get set } var voice: String? { get set } func name(_ name: String) -> Self func age(_ age: Int) -> Self func voice(_ voice: String) -> Self func build() -> Animal } class DogBuilder: AnimalBuilder { var name: String? var age: Int? var voice: String? func name(_ name: String) -> Self { self.name = name return self } func age(_ age: Int) -> Self { self.age = age return self } func voice(_ voice: String) -> Self { self.voice = voice return self } func build() -> Animal { return Dog(name: name ?? "", age: age ?? 0, voice: voice ?? "") } } class Director { private let builder: AnimalBuilder init(builder: AnimalBuilder) { self.builder = builder } func makeAnimal() -> Animal { return builder.name("いぬ") .age(10) .voice("わん") .build() } } class Hoge { func hogehoge() { let director = Director(builder: DogBuilder()) let dog = director.makeAnimal() } } |
うーん。例が悪いかも。。。(CatBuilder を作ったとしても Director を通すと"わん"と鳴く。。。)
参考サイトの下記コメントにある通り複雑なオブジェクトじゃないと効力は発揮しないかも。
犬小屋作るのに重機を持ってくる必要はないんだから
Prototype パターン
既存のインスタンスをコピーして新しいインスタンスをつくる仕組みらしい。(JS の protptype
とは完全に別物らしい)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class Cat { var name: String var age: Int var voice: String init(name: String, age: Int, voice: String) { self.name = name self.age = age self.voice = voice } func clone() -> Cat { return Cat(name: name, age: age, voice: voice) } } class Hoge { func hogehoge() { let cat = Cat(name: "ねこ", age: 3, voice: "にゃ〜") let copyCat = cat.clone() } } |
こういうことかな? Struct なら勝手にコピーしてくれるし今までこういうの実装した記憶はない。。。
Factory Method パターン
オブジェクト毎に Factory を用意してオブジェクト生成を外部に任せる仕組み。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
struct Animal { let name: String var voice: String! func cry() { print(voice!) } } struct AnimalFactory { let name: String let voice: String func makeAnimal() -> Animal { return .init(name: name, voice: voice) } } class Hoge { func hogehoge() { let factory = AnimalFactory(name: "いぬ", voice: "わん") let animal = factory.makeAnimal() animal.cry() } func piyopiyo() { // これはクラッシュ! let animal = Animal(name: "いぬ") animal.cry() } } |
これはつくりがそもそも悪い気がするけど AnimalFactory
を介さずに Animal
を生成すると cry()
でクラッシュする可能性がある。
Abstract Factory との違いは下記らしい(うーんわからん。。。)
作り方より先に使い方に着目する Abstract Factory とは違い、こっちは、終始作り方に着目するのがポイントですよ。この差がめちゃくちゃ重要。
さいごに
デザインパターンはとりあえず盛り込めばいいものではなく、コード書いてるときにこれあのパターンが使えるんじゃね?とかコード読んでるときにこれはあのパターンじゃね?ってなるのが理想なんだと思う。なのでとりあえずふわっとでもいいから知識として知っとくべきかも??
デザインパターン盛り込んで書いてておれのコードめっちゃクールやん?みたいなデザインパターンを導入するのが目的になるとヤバいかもしれない。
github でこんなんみつけた
ochococo/Design-Patterns-In-Swift
参考
- オブジェクト指向言語を学ぶ人のための ザックリ理解するデザインパターン
- マンガでわかる Singleton
- マンガでわかる Abstract Factory
- マンガでわかる Builder
- マンガでわかる Prototype
- マンガでわかる Factory Method
コメント
[…] その1の続き。今回はオブジェクトの構造に関するパターンについてです。 […]