Book Image

Swift 2 Design Patterns

By : Julien Lange
Book Image

Swift 2 Design Patterns

By: Julien Lange

Overview of this book

Table of Contents (15 chapters)
Swift 2 Design Patterns
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

The abstract factory pattern


We already introduced you to a very popular concept in design patterns: factories. Factories are the classes that handle the instantiation of related objects without subclassing. The factory method pattern that we have already seen hides the class name from where an object is instantiated. The abstract factory pattern is more complete as it creates families of related or dependent objects.

Roles

The abstract factory pattern is designed to build objects grouped in families without having to know the concrete class needed to create the object.

This pattern is generally used in the following domains:

  • A system that uses products needs to stay independent of how these products are grouped and instantiated

  • A system can have several product families that can evolve

Design

The following diagram represents the generic structure of the abstract factory pattern. You will see how products and families are decoupled:

Participants

The abstract factory pattern has a lot of participants:

  • Abstract Factory: This abstract class defines the signature of the different methods that create our products.

  • ConcreteFactory1 and ConcreteFactory2: These are our concrete classes that implement our methods for each products' families. By knowing the family and product, the factory is able to create an instance of the product for that family.

  • IProductA and IProductB: These are our interfaces that define our products that are independent of their family. A family is introduced in their concrete subclasses.

  • ProductA and ProductB: These are the concrete classes that implement IProductA and IProductB, respectively.

Collaborations

The Client class uses one instance of one of the concrete factories to create products throughout the interface of the abstract factory.

Illustration

Our company specializes in manufacturing watches. Our watches are built in two parts: a band and dial. Our watches come in two sizes, so we must adapt the manufacturing of the band and dial according to the size of our watch.

In order to simplify how to manage the manufacturing of our watches, the direction team decided to use one manufacturer who specializes in products that are adapted to the 38 mm model of our watch, and another manufacturer whose products are adapted to the 42 mm model of our watch.

Each of these manufacturers will build a dial and band that are adapted to the dimension of the watch.

Implementation

To implement our pattern, we first need to identify our actors. The two manufacturers represent the ConcreteFactory1 and ConcreteFactory2 classes. These two factories implement the AbstractFactory method, which tell us that we can create a band or dial. Of course, the concrete factories will create the dial adapted to the size of the watch produced in that manufacture.

Our ConcreteProductA and ConcreteProductB classes are the band and the dial; each of these products implements their respective IProductA and IProductB interfaces, as shown in the following code:

import UIKit

//Our interfaces
protocol IWatchBand {
  var color: UIColor{get set}
  var size: BandSize{get set}
  var type: BandType{get set}
  init(size: BandSize)
}

protocol IWatchDial {
  var material: MaterialType{get set}
  var size: WatchSize{get set}
  init(size: WatchSize)
}

//Enums
enum MaterialType: String {
  case Aluminium = "Aluminium",
  StainlessSteel = "Stainless Steel",
  Gold = "Gold"
}

enum BandType: String {
  case Milanese = "Milanese",
  Classic = "Classic",
  Leather = "Leather",
  Modern = "Modern",
  LinkBracelet = "LinkBracelet",
  SportBand = "SportBand"
}

enum WatchSize: String {
  case _38mm = "38mm", _42mm = "42mm"
}

enum BandSize: String {
  case SM = "SM", ML = "ML"
}

//prepare our Bands components
class MilaneseBand: IWatchBand {
  var color = UIColor.yellowColor()
  var size: BandSize
  var type = BandType.Milanese
  required init(size _size: BandSize) {
    size = _size
  }
 }

class Classic: IWatchBand {
  var color = UIColor.yellowColor()
  var size: BandSize
  var type = BandType.Classic
  required init(size _size: BandSize) {
    size = _size
  }
}
class Leather:IWatchBand{
  var color = UIColor.yellowColor()
  var size:BandSize
  var type = BandType.Leather
  required init(size _size: BandSize) {
    size = _size
  }
}
class Modern: IWatchBand {
  var color = UIColor.yellowColor()
  var size: BandSize
  var type = BandType.Modern
  required init(size _size: BandSize) {
    size = _size
  }
}

class LinkBracelet: IWatchBand {
  var color = UIColor.yellowColor()
  var size: BandSize
  var type = BandType.LinkBracelet
  required init(size _size: BandSize) {
    size = _size
  }
}
class SportBand: IWatchBand {
  var color = UIColor.yellowColor()
  var size: BandSize
  var type = BandType.SportBand
  required init(size _size: BandSize) {
    size = _size
  }
}


//Dials
class AluminiumDial: IWatchDial {
  var material: MaterialType = MaterialType.Aluminium
  var size: WatchSize
  required init(size _size:WatchSize){
    size = _size
  }
}

class StainlessSteelDial: IWatchDial {
  var material: MaterialType = MaterialType.StainlessSteel
  var size: WatchSize
  required init(size _size:WatchSize){
    size = _size
  }
}

class GoldDial: IWatchDial {
  var material: MaterialType = MaterialType.Gold
  var size: WatchSize
  required init(size _size:WatchSize){
    size = _size
  }
}


//Our AbstractFactory
class WatchFactory {
  
  func createBand(bandType: BandType) -> IWatchBand {
    fatalError("not implemented")
  }
  func createDial(materialtype: MaterialType) -> IWatchDial{
    fatalError("not implemented")
  }
  
  //our static method that return the appropriated factory.
  final class func getFactory(size: WatchSize) -> WatchFactory{
    var factory: WatchFactory?
    switch(size){
    case ._38mm:
      factory = Watch38mmFactory()
    case ._42mm:
      factory = Watch42mmFactory()
    }
    return factory!
  }

}


// Concrete Factory 1 for 42 mm
class Watch42mmFactory: WatchFactory {
  override func createBand(bandType: BandType) -> IWatchBand {
    switch bandType {
    case .Milanese:
      return MilaneseBand(size: .ML)
    case .Classic:
      return Classic(size: .ML)
    case .Leather:
      return Leather(size: .ML)
    case .LinkBracelet:
      return LinkBracelet(size: .ML)
    case .Modern:
      return Modern(size: .ML)
    case .SportBand:
      return SportBand(size: .ML)
    default:
      return SportBand(size: .ML)
    }
  }
  
  override func createDial(materialtype: MaterialType) -> IWatchDial {
    switch materialtype{
    case MaterialType.Gold:
      return GoldDial(size: ._42mm)
    case MaterialType.StainlessSteel:
      return StainlessSteelDial(size: ._42mm)
    case MaterialType.Aluminium:
      return AluminiumDial(size: ._42mm)
    }
  }
}

//Concrete Factory 2 for 38mm
class Watch38mmFactory: WatchFactory{
  override func createBand(bandType:BandType) -> IWatchBand {
    switch bandType {
    case .Milanese:
      return MilaneseBand(size: .SM)
    case .Classic:
      return Classic(size: .SM)
    case .Leather:
      return Leather(size: .SM)
    case .LinkBracelet:
      return LinkBracelet(size: .SM)
    case .Modern:
      return Modern(size: .SM)
    case .SportBand:
      return SportBand(size: .SM)
    default:
      return SportBand(size: .SM)
    }
  }
  
  override func createDial(materialtype: MaterialType) -> IWatchDial {
    switch materialtype{
    case MaterialType.Gold:
      return GoldDial(size: ._38mm)
    case MaterialType.Gold:
      return StainlessSteelDial(size: ._38mm)
    case MaterialType.Gold:
      return AluminiumDial(size: ._38mm)
    default:
      return AluminiumDial(size: ._38mm)
      
    }
  }
}

Usage

To simulate our client, we will use the following code:

//Here we deliver products from the Manufacture 1 specialized in
//products for the 38 mm Watch
let manufacture1 = WatchFactory.getFactory(WatchSize._38mm)
let productA = manufacture1.createBand(BandType.Milanese)
productA.color
productA.size.rawValue
productA.type.rawValue

let productB = manufacture1.createDial(MaterialType.Gold)
productB.material.rawValue
productB.size.rawValue


//Here we delivers products from the Manufacture 2 specialized in
//products for the 42 mm Watch
let manufacture2 = WatchFactory.getFactory(WatchSize._42mm)
let productC = manufacture2.createBand(BandType.LinkBracelet)
productC.color
productC.size.rawValue
productC.type.rawValue

let productD = manufacture2.createDial(MaterialType.Gold)
productD.material.rawValue
productD.size.rawValue

Tip

Downloading the example code

You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

The Playground file will display our product's properties, depending on the factory used. The details of product A (the band) and product B (the dial) from the manufacture1 object are shown in the following screenshot:

The details of product C (the band) and product D (the dial) from the manufacture2 object are shown in the following screenshot:

The sizes of the band and the dial adapt to the manufacturer who delivers the product.

Note

We should use the singleton pattern to ensure that we have only one instance of our abstract factory. This instance can be shared between several clients.