Abstract Factory includes several create methods to create related product types.
Create one product type? → Factory Method
✅ Single Responsibility Principle (SRP)
✅ Open/Closed Principle (OCP): Abstract Factory allows to extend
✅ Liskov Substitution Principle (LSP) / Interface Segregation Principle (ISP): The Abstract Factory has a simple interface (create methods) letting Concrete Factories to implement. ⚠️ But the other extra common logics in abstract objects needs to be careful.
✅ Dependency Inversion Principle (DIP): Factories depend on Abstract Products.
// Abstract Factory
abstract class GUIFactory {
// Create methods
public abstract createButton(): Button;
public abstract createCheckbox(): Checkbox;
// Reusable Shared Logic
protected logFactoryUsage(): void {
console.log(`[${this.constructor.name}] factory used.`);
}
}
// Concrete Factories
// The product can either be encapsulated in the factory or use DI injection.
// - Encapsulation: for fixed type cohesion and simplicity
// - DI: for testability and runtime flexibility
class MacFactory extends GUIFactory {
public createButton(): Button { return new MacButton(); }
public createCheckbox(): Checkbox { return new MacCheckbox(); }
}
class WinFactory extends GUIFactory {
public createButton(): Button { return new WinButton(); }
public createCheckbox(): Checkbox { return new WinCheckbox(); }
}
// Abstract Products
interface Button {
render(): void;
}
interface Checkbox {
check(): void;
}
// Concrete Products
class MacButton implements Button {
render() { console.log("Render Mac Button"); }
}
class WinButton implements Button {
render() { console.log("Render Windows Button"); }
}
class MacCheckbox implements Checkbox {
check() { console.log("Check Mac Checkbox"); }
}
class WinCheckbox implements Checkbox {
check() { console.log("Check Windows Checkbox"); }
}
// Client
function compute(factory: GUIFactory) {
const btn = factory.createButton();
const checkbox = factory.createCheckbox();
btn.render();
checkbox.check();
}
compute(new MacFactory());
compute(new WinFactory());
// Abstract Factory
interface GUIFactory {
// Create methods
createButton(): Button;
createCheckbox(): Checkbox;
// Reusable Shared Logic
logFactoryUsage(): void;
}
// Concrete Factories
// The product can either be encapsulated in the factory or use DI injection.
// - Encapsulation: for fixed type cohesion and simplicity
// - DI: for testability and runtime flexibility
class MacFactory implements GUIFactory {
public createButton(): Button { return new MacButton(); }
public createCheckbox(): Checkbox { return new MacCheckbox(); }
private logFactoryUsage(): void { console.log("MacFactory factory used."); }
}
class WinFactory implements GUIFactory {
public createButton(): Button { return new WinButton(); }
public createCheckbox(): Checkbox { return new WinCheckbox(); }
private logFactoryUsage(): void { console.log("WinFactory factory used."); }
}
// ... same code
Go lacks encapsulation mechanisms (private, protected, public).
// Abstract Factory
type GUIFactory interface {
// Create methods
CreateButton() Button
CreateCheckbox() Checkbox
// Reusable Shared Logic
logFactoryUsage()
}
// Concrete Factories
// The product can either be encapsulated in the factory or use DI injection.
// - Encapsulation: for fixed type cohesion and simplicity
// - DI: for testability and runtime flexibility
type MacFactory struct{}
func (MacFactory) CreateButton() Button { return MacButton{} }
func (MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }
func (MacFactory) logFactoryUsage() { fmt.Println("MacFactory factory used.") }
type WinFactory struct{}
func (WinFactory) CreateButton() Button { return WinButton{} }
func (WinFactory) CreateCheckbox() Checkbox { return WinCheckbox{} }
func (MacFactory) logFactoryUsage() { fmt.Println("WinFactory factory used.") }
// Abstract Products
type Button interface {
Render()
}
type Checkbox interface {
Check()
}
// Concrete Products
type MacButton struct{}
func (MacButton) Render() { fmt.Println("Render Mac Button") }
type WinButton struct{}
func (WinButton) Render() { fmt.Println("Render Windows Button") }
type MacCheckbox struct{}
func (MacCheckbox) Check() { fmt.Println("Check Mac Checkbox") }
type WinCheckbox struct{}
func (WinCheckbox) Check() { fmt.Println("Check Windows Checkbox") }
// Client
func compute(factory GUIFactory) {
btn := factory.createButton();
checkbox := factory.createCheckbox();
btn.render();
checkbox.check();
}
func main() {
compute(MacFactory{})
compute(WinFactory{})
}