Sobes.tech
Middle

Tell me about the Factory pattern.

sobes.tech AI

Answer from AI

Factory is a creational design pattern that provides a common interface for creating objects in a superclass, while subclasses can alter the type of objects that will be created.

  • Simple Factory: Not a pattern in its pure form, more of an idiom. A class with a method that creates objects based on input parameters.

    package main
    
    import "fmt"
    
    type Product interface {
    	Use()
    }
    
    type ConcreteProductA struct{}
    
    func (p *ConcreteProductA) Use() {
    	fmt.Println("Using Concrete Product A")
    }
    
    type ConcreteProductB struct{}
    
    func (p *ConcreteProductB) Use() {
    	fmt.Println("Using Concrete Product B")
    }
    
    type SimpleFactory struct{}
    
    func (f *SimpleFactory) CreateProduct(t string) Product {
    	switch t {
    	case "A":
    		return &ConcreteProductA{}
    	case "B":
    		return &ConcreteProductB{}
    	default:
    		return nil
    	}
    }
    
    func main() {
    	factory := SimpleFactory{}
    	productA := factory.CreateProduct("A")
    	productA.Use()
    
    	productB := factory.CreateProduct("B")
    	productB.Use()
    }
    
  • Factory Method: Defines an interface for creating an object, but allows subclasses to decide which class to instantiate.

    package main
    
    import "fmt"
    
    // Product interface
    type Product interface {
    	Operation() string
    }
    
    // Concrete Product A
    type ConcreteProduct1 struct{}
    
    func (p *ConcreteProduct1) Operation() string {
    	return "Result of the ConcreteProduct1"
    }
    
    // Concrete Product B
    type ConcreteProduct2 struct{}
    
    func (p *ConcreteProduct2) Operation() string {
    	return "Result of the ConcreteProduct2"
    }
    
    // Creator interface
    type Creator interface {
    	FactoryMethod() Product
    	SomeOperation() string
    }
    
    // Base Creator
    type BaseCreator struct{}
    
    func (bc *BaseCreator) SomeOperation() string {
    	// Call the factory method to get a product
    	product := bc.FactoryMethod()
    	// Use the product
    	result := "Creator: The same creator's code has just worked with " + product.Operation()
    	return result
    }
    
    // Concrete Creator 1
    type ConcreteCreator1 struct {
    	BaseCreator
    }
    
    func (cc *ConcreteCreator1) FactoryMethod() Product {
    	return &ConcreteProduct1{} // Creates Concrete Product A
    }
    
    // Concrete Creator 2
    type ConcreteCreator2 struct {
    	BaseCreator
    }
    
    func (cc *ConcreteCreator2) FactoryMethod() Product {
    	return &ConcreteProduct2{} // Creates Concrete Product B
    }
    
    func main() {
    	// Usage example
    	creator1 := &ConcreteCreator1{}
    	fmt.Println(creator1.SomeOperation())
    
    	creator2 := &ConcreteCreator2{}
    	fmt.Println(creator2.SomeOperation())
    }
    
  • Abstract Factory: Provides an interface for creating families of related or dependent objects without specifying their concrete classes.

    package main
    
    import "fmt"
    
    // Product A interface
    type AbstractProductA interface {
    	UseProductA() string
    }
    
    // Concrete Product A1
    type ConcreteProductA1 struct{}
    
    func (*ConcreteProductA1) UseProductA() string {
    	return "The result of the product A1."
    }
    
    // Concrete Product A2
    type ConcreteProductA2 struct{}
    
    func (*ConcreteProductA2) UseProductA() string {
    	return "The result of the product A2."
    }
    
    // Product B interface
    type AbstractProductB interface {
    	UseProductB() string
    	AnotherFunctionB(collaborator AbstractProductA) string // Interaction with product A
    }
    
    // Concrete Product B1
    type ConcreteProductB1 struct{}
    
    func (*ConcreteProductB1) UseProductB() string {
    	return "The result of the product B1."
    }
    
    // B1 interaction with any AbstractProductA
    func (*ConcreteProductB1) AnotherFunctionB(collaborator AbstractProductA) string {
    	result := collaborator.UseProductA()
    	return fmt.Sprintf("The result of the B1 collaborating with the (%s)", result)
    }
    
    // Concrete Product B2
    type ConcreteProductB2 struct{}
    
    func (*ConcreteProductB2) UseProductB() string {
    	return "The result of the product B2."
    }
    
    // B2 interaction with any AbstractProductA
    func (*ConcreteProductB2) AnotherFunctionB(collaborator AbstractProductA) string {
    	result := collaborator.UseProductA()
    	return fmt.Sprintf("The result of the B2 collaborating with the (%s)", result)
    }
    
    // Abstract Factory interface
    type AbstractFactory interface {
    	CreateProductA() AbstractProductA
    	CreateProductB() AbstractProductB
    }
    
    // Concrete Factory 1
    type ConcreteFactory1 struct{}
    
    func (*ConcreteFactory1) CreateProductA() AbstractProductA {
    	return &ConcreteProductA1{}
    }
    
    func (*ConcreteFactory1) CreateProductB() AbstractProductB {
    	return &ConcreteProductB1{}
    }
    
    // Concrete Factory 2
    type ConcreteFactory2 struct{}
    
    func (*ConcreteFactory2) CreateProductA() AbstractProductA {
    	return &ConcreteProductA2{}
    }
    
    func (*ConcreteFactory2) CreateProductB() AbstractProductB {
    	return &ConcreteProductB2{}
    }
    
    // Client code working with factory
    func ClientCode(factory AbstractFactory) {
    	productA := factory.CreateProductA()
    	productB := factory.CreateProductB()
    
    	fmt.Println(productB.UseProductB())
    	fmt.Println(productB.AnotherFunctionB(productA))
    }
    
    func main() {
    	fmt.Println("Client: Testing client code with the first factory type:")
    	ClientCode(&ConcreteFactory1{})
    
    	fmt.Println("Client: Testing the same client code with the second factory type:")
    	ClientCode(&ConcreteFactory2{})
    }
    

The main difference between Factory Method and Abstract Factory: Factory Method creates individual products, while Abstract Factory creates families of products.

Pattern Name Description When to use
Simple Factory Method in a single class for creating different types of objects. For simple object creation without the need for a factory hierarchy.
Factory Method Interface for creating an object, allowing subclasses to decide which class to instantiate. When the class cannot predict the type of objects it should create.
Abstract Factory Interface for creating families of related objects. When the system should be independent of how its products are created and represented.

The Factory pattern promotes loose coupling of code and makes it easier to add new product types without changing client code.