Daily Dose of Swift

Abstract Factory Pattern

Photo by Monica Sauro on Unsplash

The Abstract Factory is a creational pattern that makes it possible to abstract the logic of creating a family of related objects from the client classes. The pattern has the object scope achieving its goals through composition at run time.

Use case

A production company has in its creation catalog products like films and books that vary by genre. The catalog must be constantly updated with the new genres of products available in the catalog. This is a conducive use case for implementing the Abstract Factory standard.

Implementation

//1 - Abstract product
protocol Book {
    func read()
}

protocol Movie {
    func watch()
}

//2 - Abstract Factory
protocol CatalogFactory {
    func makeBook() -> Book
    func makeMovie() -> Movie
}

//3 - Concrete product
final class ScienceFictionBook: Book {
    func read() {
        print("Reading a science fiction book")
    }
}

final class ScienceFictionMovie: Movie {
    func watch() {
        print("Watching a science fiction movie")
    }
}

final class SuspenseBook: Book {
    func read() {
        print("Reading a suspense book")
    }
}

final class SuspenseMovie: Movie {
    func watch() {
        print("Watching a suspense movie")
    }
}

//4 - Concrete Factory
struct ScienceFictionCatalogFactory: CatalogFactory {
    func makeBook() -> Book {
        return ScienceFictionBook()
    }
    
    func makeMovie() ->  Movie {
        return ScienceFictionMovie()
    }
}

struct SuspenseCatalogFactory: CatalogFactory {
    func makeBook() -> Book {
        return SuspenseBook()
    }
    
    func makeMovie() ->  Movie {
        return SuspenseMovie()
    }
}

//5 - Client
final class Producer {
    private let catalogFactory: CatalogFactory
    private var bookCatalog = [Book]()
    private var movieCatalog = [Movie]()

    init(catalogFactory: CatalogFactory) {
        self.catalogFactory = catalogFactory
    }
    
    func addBook() {
        bookCatalog.append(catalogFactory.makeBook())
    }
    
    func addMovie() {
        movieCatalog.append(catalogFactory.makeMovie())
    }
}

let producer = Producer(catalogFactory: ScienceFictionCatalogFactory())

producer.addBook()
producer.addMovie()

Algorithm

  • Step 1: Define interface for the types of products.
  • Step 2: Define an abstract factory for the catalog.
  • Step 3: Create a concrete product for each variant.
  • Step 4: Create a concrete factory for each variant that the products may have.
  • Step 5: Create the client class and define its dependency as an abstract type.

Advantages

  • Decreased coupling between client class and concrete class
  • Improves code flexibility
  • Allows the creation code to be encapsulated in the factory

Conclusion

Using the Abstract Factory, we created a code with less change propagation and with flexibility to receive new variants of the product family.

Thanks for reading!