Prototype – Design Patterns in Swift

Introduction

The prototype pattern is available in Swift out of the box with a Clonable interface. The prototype can be easily recognized by a clone or copy methods, etc.

This pattern enables you to create new instances by copying existing instances to avoid the overhead involved in creating objects that can consume more resources.

In another word, developers use this pattern to:

  • avoid subclasses of an object creator in the client application, as the factory method pattern does.
  • avoid the inherent cost of creating a new object in the standard way when it is prohibitively expensive for a given application.
Prototype – UML

Use Prototype Pattern in Swift

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.

import XCTest

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()
        }
        prototype.boolValue = boolValue
        print("Values defined in SubClass have been cloned!")
        return prototype
    }
}

The client code. Note that Equatable protocol is a type that can be compared for value equality. See implementation of Equatable protocol for more details in the above code.

class Client {

    static func someClientCode() {
        let original = SubClass(intValue: 2, stringValue: "Value2")

        guard let copy = original.copy() as? SubClass else {
            XCTAssert(false)
            return
        }

        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()
    }
}

Conclusion

All prototype classes should have a common interface that makes it possible to copy objects even if their concrete classes are unknown. In addition, prototype objects can produce full copies since objects of the same class can access each other’s private fields.

Leave a Reply

Your email address will not be published. Required fields are marked *