Abstract Factory (aka. Kit) provides an interface for creating families of related or dependent objects without specifying their concrete classes.

Structure

Class diagram (UML):

Abstract Factory Class Diagram (UML)

When Would You Use It?

  • A system should be independent of how its products are created, composed, and represented.
  • A system should be configured with one of multiple families of products.
  • A family of related product objects is designed to be used together, and you need to enforce this constraint.
  • You want to provide a class library of products, and you want to reveal just their interfaces, not their implementations.

Related Patterns

AbstractFactory classes are often implemented with factory methods, but they can also be implemented using Prototype.
A concrete factory is often a singleton.

Discussion

Factory and Product

In Abstract Factory pattern, it has two different aspects: factory and product, which actually forms a “two-dimensional” matrix. In a real design practice, how to determine factory and product?

  • Factory, usually refers to “environment”, which “clent” usually does not need to identufy, such as diffferent OSes (like Windows and Mac), or different languages (like Swift and Object-C).
  • Product, usually refers to the actual “operating target” that the application points to, like UI element, value type, etc.

References

  • Design Patterns: Elements of Reusable Object-Oriented Software, by Book by Erich Gamma, John Vlissides, Ralph Johnson, and Richard Helm (GoF)
  • Pro Objective-C Design Patterns for iOS, by Carlo Chung
  • Pro Design Patterns in Swift, by Adam Freeman

Example #1 – Number ToolKit

Define a tool class for “number”, which is done either through “NSNumber” or by “Swift Int”.

Here is the class diagram:

Abstract Factory Class Diagram (UML) - Number ToolKit

Code

import Foundation
import UIKit

// Abstract product
protocol Number {

    func captureValue(string: String)
}

// Abstract factory
protocol NumberAbstractFactory {

    func createNumber() -> Number
}

// Concrete Product
class SwiftNumber: Number {

    private var swiftNumber: Int = 0

    func captureValue(string: String) {

        swiftNumber = Int((string as NSString).integerValue)

        println("Capture string "(string)" into number(Swift Int): (swiftNumber)")
    }
}

class NextStepNumber: Number {

    private var nextStepNumber: NSNumber?

    func captureValue(string: String) {

        nextStepNumber = NSNumber(longLong: Int64(string.toInt()!))

        println("Capture string "(string)" into number(NSNumber): (nextStepNumber)")
    }

}

// Concrete factories
class SwiftNumberFactory: NumberAbstractFactory {

    // sigleton instance
    static let sharedInstance: SwiftNumberFactory = SwiftNumberFactory()

    func createNumber() -> Number {
        return SwiftNumber()
    }
}

class NextStepNumberFactory: NumberAbstractFactory {

    // sigleton instance
    static let sharedInstance: NextStepNumberFactory = NextStepNumberFactory()

    func createNumber() -> Number {
        return NextStepNumber()
    }
}

// Client 
class Client {

    static func convertStringToNumber(string: String, withNumberFactory numberFactory: NumberAbstractFactory) {

        let number: Number = numberFactory.createNumber()
        number.captureValue(string)

    }
}


// Usage

// environment enum
enum ENV {
    case Swift
    case NextStep
}

// choose right "env"
// usually done by "outer" system
let env: ENV = .Swift
var numberFactory: NumberAbstractFactory = SwiftNumberFactory()
if env == ENV.NextStep {

    numberFactory = NextStepNumberFactory()
}

Client.convertStringToNumber("67.8", withNumberFactory: numberFactory)

Leave a Reply

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

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>