Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Swift 2 Design Patterns

You're reading from   Swift 2 Design Patterns Build robust and scalable iOS and Mac OS X game applications

Arrow left icon
Product type Paperback
Published in Oct 2015
Publisher
ISBN-13 9781785887611
Length 224 pages
Edition 1st Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Julien Lange Julien Lange
Author Profile Icon Julien Lange
Julien Lange
Arrow right icon
View More author details
Toc

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:

Design

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:

Usage

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

Usage

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.

You have been reading a chapter from
Swift 2 Design Patterns
Published in: Oct 2015
Publisher:
ISBN-13: 9781785887611
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image