Lesson 8 · OKSTEM College · Associate of Science in Computer Science
What Are Design Patterns?
Design patterns are reusable solutions to common software design problems. They are not code you copy-paste — they are templates you adapt. Three classic categories: Creational (how objects are made), Structural (how objects are composed), Behavioural (how objects communicate).
Creational: Singleton
A Singleton ensures only one instance of a class ever exists. Useful for configuration, logging, or database connections.
classConfig:
_instance = Nonedef__new__(cls):
ifcls._instance isNone:
cls._instance = super().__new__(cls)
cls._instance.debug = Falsereturncls._instance
a = Config(); b = Config()
print(a is b) # True — same object
a.debug = Trueprint(b.debug) # True — b IS a
Creational: Factory Method
A Factory delegates object creation to a method, decoupling the caller from concrete classes.
The Observer pattern lets objects subscribe to events. When the subject changes, all subscribers are notified automatically.
classEventBus:
def__init__(self):
self._listeners: dict = {}
defsubscribe(self, event, fn):
self._listeners.setdefault(event, []).append(fn)
defpublish(self, event, data=None):
for fn inself._listeners.get(event, []):
fn(data)
bus = EventBus()
bus.subscribe("login", lambda u: print(f"Welcome, {u}!"))
bus.subscribe("login", lambda u: print(f"Logging login for {u}"))
bus.publish("login", "Alice")
# Welcome, Alice!# Logging login for Alice
Practice Problems
1. When would you choose Singleton over a module-level variable?
When you need lazy initialization (create the object only when first needed),
or when you want to encapsulate related state and methods together,
or when you need to control subclassing. Module-level variables are simpler
but can't be subclassed or lazily initialized as cleanly.
2. Add a "logout" event to the EventBus example that prints a farewell.
Correct — subsequent calls to the constructor return the same object.
Singleton doesn't inherently prevent subclassing (though subclassing Singletons is tricky).
Singleton objects can have instance methods.
Recap: Singleton overrides __new__ to return the cached instance if one already exists.
The Factory pattern's main benefit is
Performance is not the motivation.
Correct — the caller asks for a 'notifier' without knowing which concrete class it gets.
Factory doesn't affect inheritance.
Those are separate patterns.
Recap: notifier_factory('sms') returns an SMSNotifier — the caller never imports SMSNotifier directly. Swap implementations without changing call sites.
The Observer pattern is best described as
That's tight coupling — the opposite of Observer.
Correct — publish/subscribe decouples producers from consumers.
That's Factory.
That's Singleton.
Recap: Observer = event bus pattern. Subscribers register; when an event fires, all subscribers are called. Neither side knows the other's details.
Design patterns are
They are templates and concepts, not copy-paste code.
Correct — you adapt the pattern to your language and context.
Patterns apply at any scale; Singleton is common even in small scripts.
Patterns are language-agnostic concepts, not library code.
Recap: patterns are a shared vocabulary for design. Saying 'use an Observer here' communicates an architecture quickly to other engineers.
Which pattern category does 'Factory' belong to?
Correct — Creational patterns deal with how objects are instantiated.
Structural patterns deal with how objects are composed (Adapter, Decorator, etc.).
Behavioural patterns deal with how objects communicate (Observer, Strategy, etc.).