Posted at

Swiftでデザインパターン プロトタイプパターン

More than 1 year has passed since last update.


プロトタイプメソッド


  • protocolでcloneメソッドを実装する

  • サブクラスでprotocolを継承し、cloneメソッド内で新しくインスタンスを作成し、returnする

  • 端的にいえば、クラスに自分のコピーを返却するメソッドを実装すること

  • このコピーは別のインスタンスというのが重要(値渡しになる)


メリット


  • クラスは参照渡しになるが、cloneを使えば、値渡しになる

  • クラスからインスタンスを1から作るのが大変な場合、時間などのコストがかかりすぎる場合

  • クラスは通常参照渡しになるが、cloneを使えば、値渡しすることができる

  • 複雑な生成処理を気にしないで良い(例: initに値を渡さなければいけない or 生成の手順などを全く気にしなくて良いので使いやすい


protocol Comment {
func clone() -> Comment
func fetchCommentContent() -> [String]
func pushCommentContent(txt: String)
}

class ArticleComment {
private var contents = [String]()

init(contents: [String]) {
self.contents = contents
}
}

extension ArticleComment: Comment {
func clone() -> Comment {
let vc = ArticleComment(contents: self.contents)
return vc
}

func fetchCommentContent() -> [String] {
return self.contents
}

func pushCommentContent(txt: String) {
self.contents.append(txt)
}
}

let comments = ["Amazon最高", "いや、ヨドバシのほうが最高"]
let oldArticleComment = ArticleComment(contents: comments)

// articleCommentを普通に利用する処理があると仮定

// 他の箇所にArticleCommentを渡したい場合
let newArticleComment = oldArticleComment.clone()
newArticleComment.pushCommentContent(txt: "楽天のほうが良い!")

print(oldArticleComment.fetchCommentContent()) // ["Amazon最高", "いや、ヨドバシのほうが最高"]
print(newArticleComment.fetchCommentContent()) // ["Amazon最高", "いや、ヨドバシのほうが最高", "楽天のほうが良い!"]

// 値渡しなので、newArticleCommentに値を追加しても、oldArticleCommentに影響はない
// 複雑な生成処理を気にしないで良い(例: initに値を渡さなければいけない or 生成の手順などを全く気にしなくて良い
// -> インスタンス化が簡単


総評


  • 理解するのは難しくないパターン

  • クラスのインスタンスの使い回しなどで活躍できそう

  • 他の人がいっていたが、画像描画系などで線のコピーはよくある機能なので、そういったところでも活躍できそう

  • チームでやる場合、他の人も読みやすいし、未来の自分も読みやすくなるというメリットもある

  • classは普通に扱うと参照渡しになるが、参照渡しは、複雑になったり障害の元になるなど、危険なため、値渡しになるので安全性は高まると思われた


補足


  • UIViewControllerの場合は、既にcopyメソッドとmutableCopyメソッドが存在する

  • oveerideして使う。overrideしないでcopyメソッドを利用した場合、例外が発生する


import UIKit

class ViewController: UIViewController {

var txt = ""

func setup(txt: String) {
self.txt = txt
}

// https://developer.apple.com/documentation/objectivec/nsobject/1418807-copy
override func copy() -> Any {
let vc = ViewController()
vc.setup(txt: self.txt)
return vc
}

// https://developer.apple.com/documentation/objectivec/nsobject/1418978-mutablecopy
override func mutableCopy() -> Any {
let vc = ViewController()
vc.setup(txt: self.txt)
return vc
}

func shout() -> String {
return self.txt
}
}

let vc = ViewController()
vc.setup(txt: "test1")

let vc2 = vc.mutableCopy() as! ViewController
//let vc2 = vc.copy() as! ViewController

vc2.setup(txt: "test2")

print(vc.shout())
print(vc2.shout())