/ 设计模式 / 原型模式 / Swift Swift 原型模式讲解和代码示例 原型是一种创建型设计模式, 使你能够复制对象, 甚至是复杂对象, 而又无需使代码依赖它们所属的类。 所有的原型类都必须有一个通用的接口, 使得即使在对象所属的具体类未知的情况下也能复制对象。 原型对象可以生成自身的完整副本, 因为相同类的对象可以相互访问对方的私有成员变量。 进一步了解原型模式 导航 简介 概念示例 Example Output 真实世界示例 Example Output 复杂度: 流行度: 使用示例: Swift 的 NSCopying (克隆) 接口就是立即可用的原型模式。 识别方法: 原型可以简单地通过 clone或 copy等方法来识别。 以下示例可在 Swift Playgrounds 上使用。 感谢 Alejandro Mohamad 创建了Playground版本。 概念示例 真实世界示例 概念示例 本例说明了原型设计模式的结构并重点回答了下面的问题: 它由哪些类组成? 这些类扮演了哪些角色? 模式中的各个元素会以何种方式相互关联? 了解该模式的结构后, 你可以更容易地理解下面基于真实世界的 Swift 应用案例。 Example.swift: 概念示例 import XCTest /// Swift has built-in cloning support. To add cloning support to your class, /// you need to implement the NSCopying protocol in that class and provide the /// implementation for the `copy` method. class BaseClass: NSCopying, Equatable { private var intValue = 1 private var stringValue = "Value" required init(intValue: Int = 1, stringValue: String = "Value") { self.intValue = intValue self.stringValue = stringValue } /// MARK: - NSCopying func copy(with zone: NSZone? = nil) -> Any { let prototype = type(of: self).init() prototype.intValue = intValue prototype.stringValue = stringValue print("Values defined in BaseClass have been cloned!") return prototype } /// MARK: - Equatable static func == (lhs: BaseClass, rhs: BaseClass) -> Bool { return lhs.intValue == rhs.intValue && lhs.stringValue == rhs.stringValue } } /// Subclasses can override the base `copy` method to copy their own data into /// the resulting object. But you should always call the base method first. class SubClass: BaseClass { private var boolValue = true func copy() -> Any { return copy(with: nil) } override func copy(with zone: NSZone?) -> Any { guard let prototype = super.copy(with: zone) as? SubClass else { return SubClass() // oops } prototype.boolValue = boolValue print("Values defined in SubClass have been cloned!") return prototype } } /// The client code. class Client { // ... static func someClientCode() { let original = SubClass(intValue: 2, stringValue: "Value2") guard let copy = original.copy() as? SubClass else { XCTAssert(false) return } /// See implementation of `Equatable` protocol for more details. XCTAssert(copy == original) print("The original object is equal to the copied object!") } // ... } /// Let's see how it all works together. class PrototypeConceptual: XCTestCase { func testPrototype_NSCopying() { Client.someClientCode() } } Output.txt: 执行结果 Values defined in BaseClass have been cloned! Values defined in SubClass have been cloned! The original object is equal to the copied object! 真实世界示例 Example.swift: 真实世界示例 import XCTest class PrototypeRealWorld: XCTestCase { func testPrototypeRealWorld() { let author = Author(id: 10, username: "Ivan_83") let page = Page(title: "My First Page", contents: "Hello world!", author: author) page.add(comment: Comment(message: "Keep it up!")) /// Since NSCopying returns Any, the copied object should be unwrapped. guard let anotherPage = page.copy() as? Page else { XCTFail("Page was not copied") return } /// Comments should be empty as it is a new page. XCTAssert(anotherPage.comments.isEmpty) /// Note that the author is now referencing two objects. XCTAssert(author.pagesCount == 2) print("Original title: " + page.title) print("Copied title: " + anotherPage.title) print("Count of pages: " + String(author.pagesCount)) } } private class Author { private var id: Int private var username: String private var pages = [Page]() init(id: Int, username: String) { self.id = id self.username = username } func add(page: Page) { pages.append(page) } var pagesCount: Int { return pages.count } } private class Page: NSCopying { private(set) var title: String private(set) var contents: String private weak var author: Author? private(set) var comments = [Comment]() init(title: String, contents: String, author: Author?) { self.title = title self.contents = contents self.author = author author?.add(page: self) } func add(comment: Comment) { comments.append(comment) } /// MARK: - NSCopying func copy(with zone: NSZone? = nil) -> Any { return Page(title: "Copy of '" + title + "'", contents: contents, author: author) } } private struct Comment { let date = Date() let message: String } Output.txt: 执行结果 Original title: My First Page Copied title: Copy of 'My First Page' Count of pages: 2 概念示例 真实世界示例