Abstract Factory Design Pattern in Go

1. Definition

The Abstract Factory Design Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.

2. Problem Statement

Consider the challenge of developing a UI library that must be consistent across multiple operating systems. If you have a factory for each UI element like buttons, text boxes, etc., introducing a new OS-specific style would mean modifying each factory.

3. Solution

The Abstract Factory pattern addresses this by introducing multiple Factory Methods, one for each type of object to be created. This way, for each OS or style, you have a distinct factory with all the creation methods required.

4. Real-World Use Cases

1. Cross-platform UI libraries where each OS might have different visual elements.

2. A suite of products or tools that need to maintain consistency among various products.

5. Implementation Steps

1. Define separate interfaces for each type of product.

2. Define concrete classes for these product interfaces.

3. Create an abstract factory interface containing methods for creating all related products.

4. Implement concrete factories for each family of products.

6. Implementation in Go

// Importing necessary packages
package main
import (
	"fmt"
)
// Step 1: Define the Abstract Product interfaces
type Button interface {
	Click()
}
type Window interface {
	Open()
}
// Step 2: Concrete Products for Family 1 (Windows OS)
type WinButton struct{}
func (b *WinButton) Click() {
	fmt.Println("Windows Button Clicked!")
}
type WinWindow struct{}
func (w *WinWindow) Open() {
	fmt.Println("Windows Window Opened!")
}
// Concrete Products for Family 2 (Mac OS)
type MacButton struct{}
func (b *MacButton) Click() {
	fmt.Println("Mac Button Clicked!")
}
type MacWindow struct{}
func (w *MacWindow) Open() {
	fmt.Println("Mac Window Opened!")
}
// Step 3: Abstract Factory Interface
type GUIFactory interface {
	CreateButton() Button
	CreateWindow() Window
}
// Step 4: Concrete Factories
type WinFactory struct{}
func (wf *WinFactory) CreateButton() Button {
	return &WinButton{}
}
func (wf *WinFactory) CreateWindow() Window {
	return &WinWindow{}
}
type MacFactory struct{}
func (mf *MacFactory) CreateButton() Button {
	return &MacButton{}
}
func (mf *MacFactory) CreateWindow() Window {
	return &MacWindow{}
}
// Client code
func main() {
	winFactory := &WinFactory{}
	macFactory := &MacFactory{}
	winButton := winFactory.CreateButton()
	winButton.Click()
	winWindow := winFactory.CreateWindow()
	winWindow.Open()
	macButton := macFactory.CreateButton()
	macButton.Click()
	macWindow := macFactory.CreateWindow()
	macWindow.Open()
}

Output:

Windows Button Clicked!
Windows Window Opened!
Mac Button Clicked!
Mac Window Opened!

Explanation:

1. Abstract product interfaces, Button and Window, represent the abstracted UI elements.

2. Concrete implementations for two OS families (Windows & Mac) are provided.

3. The GUIFactory abstract factory interface defines methods to create each abstract product.

4. WinFactory and MacFactory are concrete implementations of the abstract factory, producing OS-specific UI elements.

5. In the client code (main), instances of Windows and Mac factories are used to create and interact with the respective UI elements.

7. When to use?

Use the Abstract Factory when:

1. The system needs to remain independent of how its objects are created, composed, and represented.

2. The system needs to be configured with multiple families of objects.

3. You want to provide a library of objects and reveal only their interfaces, not their implementations.

Remember that the Abstract Factory emphasizes families of objects and the relationships between them.


Comments