Programming Fundamentals
Design Patterns: Singleton, Factory, Observer
Design patterns are a shared language between developers. When someone says 'this is an Observer', everyone immediately understands the architecture. React hooks, Vue reactivity, Node.js EventEmitter, Django signals - all Observer.
- Node.js EventEmitter - Observer: http.request.on('data', ...), fs.watch()
- DOM events: document.addEventListener('click', ...) - pure Observer
- Django ORM: post_save, pre_delete signals = Observer
- React: useState - Observable state with automatic re-render
Предварительные знания
- OOP basics: classes, instances, methods, inheritance, encapsulation
- Polymorphism and interfaces (or Protocol / Abstract Base Class in Python)
- First-class functions: passing functions as arguments, returning them from other functions
From Christopher Alexander to the Gang of Four, and back to functions
The pattern idea came to software from building architecture. Christopher Alexander published A Pattern Language in 1977: a catalog of 253 solutions for towns, buildings, and rooms. Kent Beck and Ward Cunningham read it and in 1987 presented Using Pattern Languages for Object-Oriented Programs at OOPSLA, the first paper that ported the catalog-of-recurring-solutions idea into Smalltalk code. Seven years later, in 1994, Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (the Gang of Four, GoF) shipped Design Patterns: Elements of Reusable Object-Oriented Software. The 23 patterns split into three groups: creational (Singleton, Factory, Builder), structural (Adapter, Decorator, Facade), and behavioral (Observer, Strategy, Command). The book became the OOP canon for Java, C++, and C# for the next 20 years. In 1996 Peter Norvig gave the talk Design Patterns in Dynamic Languages and showed that in Lisp and Python 16 of the 23 GoF patterns either disappear as separate constructs or collapse into a single function. Singleton becomes a module, Strategy becomes a function, Command becomes a closure. Functional languages developed their own patterns: railroad-oriented programming (Scott Wlaschin, F#) and monad transformers in Haskell. Wlaschin's 2018 book Domain Modeling Made Functional offered an FP-side catalog. Today GoF still gets taught, but more as vocabulary for talking about code than as a required recipe set.
Singleton: one instance only
**Singleton** ensures a class has only one instance and provides a global access point to it. Classic examples: logger, config, database connection pool.
**When NOT to use Singleton:** If state needs to be reset between tests, Singleton becomes a problem - test A pollutes the state for test B. Prefer passing the dependency explicitly (Dependency Injection).
What is the main problem with Singleton in testable code?
Factory Method: creating without coupling to a class
**Factory Method** - a pattern where the object creation logic is moved into a separate method or class. This lets subclasses decide which class to instantiate.
Which SOLID principle does Factory Method implement?
Observer: subscribing to events
**Observer** - a Subject maintains a list of subscribers and notifies them when its state changes. The foundation of event-driven architecture, DOM events, and reactive frameworks.
GoF patterns are universal recipes. The more Singleton, Factory, Observer in the code, the more professional the project.
Patterns are vocabulary for describing solutions, not a design goal. In Python most GoF patterns collapse: Singleton becomes a module, Factory a function, Observer a built-in asyncio.Event. A pattern is worth applying only when the code becomes less clear without it.
The GoF book was written for C++/Smalltalk in 1994, before first-class functions, closures and metaclasses. In modern dynamic languages half of the patterns are workarounds for constraints that no longer exist. Pattern-driven design without understanding why produces over-engineered code that is harder than the problem it solves.
What is the key advantage of Observer over direct method calls?
Key ideas
- Singleton: one instance, global access. Beware in tests!
- Factory Method: create objects without coupling to specific classes → OCP
- Observer: Publisher/Subscriber - loose coupling through events
- Patterns are tools, not dogma: choose based on the problem
- Anti-pattern: applying a pattern where simple code would do
What's next
Patterns help with refactoring and testing:
- Refactoring — Patterns are often the end result of many refactorings
- Testing — Factory and DI make code testable
Вопросы для размышления
- When does Singleton become an anti-pattern? Think of a real example from a project.
- How does Abstract Factory differ from Factory Method? When do you need a 'factory of factories'?
- What is the architectural difference between Observer and Promise/async?
Связанные уроки
- prog-12-objects — Patterns build on OOP: encapsulation and polymorphism
- prog-14-refactoring — Patterns are a tool for refactoring and improving structure
- plt-01-paradigms — Patterns depend on paradigm: OOP vs FP give different solutions
- sd-10-microservices — GoF patterns live inside every microservice
- ds-01-arrays