Factory Pattern
Part of the Object Oriented Programming section of Coddy's GO journey — lesson 87 of 107.
The Factory pattern delegates object creation to a separate function or method, allowing you to create instances without exposing the creation logic. Unlike Singleton which controls instance count, Factory focuses on flexible object creation based on input parameters.
In Go, a factory is typically a function that returns an interface type, letting you create different concrete types through a unified API:
type Notifier interface {
Send(message string) string
}
type EmailNotifier struct{}
type SMSNotifier struct{}
func (e EmailNotifier) Send(message string) string {
return "Email: " + message
}
func (s SMSNotifier) Send(message string) string {
return "SMS: " + message
}
func NewNotifier(notifierType string) Notifier {
switch notifierType {
case "email":
return EmailNotifier{}
case "sms":
return SMSNotifier{}
default:
return EmailNotifier{}
}
}The factory function NewNotifier encapsulates the decision of which concrete type to create. Client code works only with the Notifier interface, remaining unaware of the specific implementation:
notifier := NewNotifier("sms")
result := notifier.Send("Hello!") // "SMS: Hello!"This pattern shines when you need to create objects based on runtime conditions, want to centralize complex initialization logic, or need to easily add new types without modifying client code. Simply add a new struct implementing the interface and update the factory function.
Challenge
EasyLet's build a document converter system using the Factory pattern! You'll create a factory that produces different document converters based on the target format, allowing client code to work with any converter through a unified interface.
You'll organize your code across two files:
converter.go: Define your converter interface and concrete implementations.Create a
Converterinterface with a methodConvert(content string) stringthat takes document content and returns the converted output.Implement three converter types:
PDFConverter— itsConvertmethod returnsPDF: [content]HTMLConverter— itsConvertmethod returnsHTML: <p>[content]</p>MarkdownConverter— itsConvertmethod returnsMD: # [content]
Create a factory function
NewConverter(format string) Converterthat returns the appropriate converter based on the format string (pdf,html, ormarkdown). For any unrecognized format, return aPDFConverteras the default.main.go: Use your factory to create converters and process documents.Read a format type and document content. Use the factory to create the appropriate converter, then convert the content and print the result.
The following inputs will be provided:
- Line 1: Format type (
pdf,html,markdown, or something else) - Line 2: Document content to convert
For example, given:
html
Welcome to Go ProgrammingYour output should be:
HTML: <p>Welcome to Go Programming</p>And given:
markdown
Chapter OneYour output should be:
MD: # Chapter OneAnd given:
pdf
Annual Report 2024Your output should be:
PDF: Annual Report 2024And given:
docx
Meeting NotesYour output should be:
PDF: Meeting NotesNotice how the main code doesn't need to know about specific converter types—it just asks the factory for a converter and uses the interface. This makes it easy to add new formats later without changing the client code!
Cheat sheet
The Factory pattern delegates object creation to a separate function, allowing you to create instances without exposing the creation logic. It focuses on flexible object creation based on input parameters.
A factory function returns an interface type, enabling creation of different concrete types through a unified API:
type Notifier interface {
Send(message string) string
}
type EmailNotifier struct{}
type SMSNotifier struct{}
func (e EmailNotifier) Send(message string) string {
return "Email: " + message
}
func (s SMSNotifier) Send(message string) string {
return "SMS: " + message
}
func NewNotifier(notifierType string) Notifier {
switch notifierType {
case "email":
return EmailNotifier{}
case "sms":
return SMSNotifier{}
default:
return EmailNotifier{}
}
}Client code works only with the interface, remaining unaware of specific implementations:
notifier := NewNotifier("sms")
result := notifier.Send("Hello!") // "SMS: Hello!"This pattern is useful when you need to create objects based on runtime conditions, want to centralize complex initialization logic, or need to add new types without modifying client code.
Try it yourself
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Read format type
scanner.Scan()
format := scanner.Text()
// Read document content
scanner.Scan()
content := scanner.Text()
// TODO: Use the factory to create the appropriate converter
// TODO: Convert the content and print the result
fmt.Println(result)
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of Go OOP
External FilesGo Workspace & ModulesPackages & ImportsExported vs Unexported NamesIntroduction to OOP in GoStructs as ClassesDefining Methods on StructsPointer vs Value ReceiversStruct InitializationConstructor FunctionsRecap - Simple Calculator4Interfaces
Introduction to InterfacesImplicit ImplementationInterface as ContractEmpty Interface (any)Type AssertionType SwitchInterface CompositionStringer & Error InterfacesRecap - Shape Calculator7Encapsulation
Exported vs Unexported FieldsPackage-Level EncapsulationGetter & Setter MethodsInformation Hiding in GoRecap - Student Records10Generics (Go 1.18+)
Introduction to GenericsType ParametersType ConstraintsGeneric StructsGeneric Methods WorkaroundRecap - Generic Collection13Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternAbstract Factory PatternObserver PatternStrategy Pattern2Types & Structs Deep Dive
Basic & Composite TypesCustom Type DefinitionsStruct TagsAnonymous StructsNested StructsZero Values & DefaultsRecap - Contact Book5Composition Over Inheritance
Why Go Has No InheritanceStruct Embedding BasicsMethod PromotionEmbedding Multiple StructsEmbedding vs AggregationShadowing Embedded MethodsRecap - Employee Hierarchy8Error Handling & OOP
The error InterfaceCustom Error TypesError Wrapping (fmt.Errorf)Sentinel Errorserrors.Is() and errors.As()Panic, Defer, and RecoverRecap - File Parser3Pointers & Memory
Pointer Basics in GoPointers to StructsPass by Value vs ReferenceThe new() FunctionGarbage Collection in GoRecap - Linked List Builder6Polymorphism in Go
Polymorphism via InterfacesDuck Typing in GoInterface Satisfaction RulesPolymorphic CollectionsDependency InjectionRecap - Payment Processor9Concurrency & OOP
Goroutines BasicsChannels & CommunicationBuffered vs Unbuffered ChanSelect Statementsync.Mutex & sync.RWMutexsync.WaitGroupThread-Safe Struct DesignRecap - Worker Pool