
Swift 原型模式讲解和代码示例
原型是一种创建型设计模式, 使你能够复制对象, 甚至是复杂对象, 而又无需使代码依赖它们所属的类。
所有的原型类都必须有一个通用的接口, 使得即使在对象所属的具体类未知的情况下也能复制对象。 原型对象可以生成自身的完整副本, 因为相同类的对象可以相互访问对方的私有成员变量。
复杂度:
流行度:
使用示例: Swift 的 NSCopying
(克隆) 接口就是立即可用的原型模式。
识别方法: 原型可以简单地通过 clone
或 copy
等方法来识别。
概念示例
本例说明了原型设计模式的结构并重点回答了下面的问题:
- 它由哪些类组成?
- 这些类扮演了哪些角色?
- 模式中的各个元素会以何种方式相互关联?
了解该模式的结构后, 你可以更容易地理解下面基于真实世界的 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