O Prototype é um padrão de projeto criacional que permite a clonagem de objetos, mesmo complexos, sem acoplamento à suas classes específicas.
Todas as classes de prototypes(protótipos) devem ter uma interface comum que permita copiar objetos, mesmo que suas classes concretas sejam desconhecidas. Objetos protótipos podem produzir cópias completas, pois objetos da mesma classe podem acessar os campos privados um do outro.
Exemplo conceitual
Este exemplo ilustra a estrutura do padrão de projeto Prototype . Ele se concentra em responder a estas perguntas:
De quais classes ele consiste?
Quais papéis essas classes desempenham?
De que maneira os elementos do padrão estão relacionados?
Depois de aprender sobre a estrutura do padrão, será mais fácil entender o exemplo a seguir, com base em um caso de uso Swift do mundo real.
Example.swift: Exemplo conceitual
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: Resultados da execução
Values defined in BaseClass have been cloned!
Values defined in SubClass have been cloned!
The original object is equal to the copied object!
Exemplo do mundo real
Example.swift: Exemplo do mundo real
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: Resultados da execução
Original title: My First Page
Copied title: Copy of 'My First Page'
Count of pages: 2