OOP
Classes, inheritance, MRO, descriptors, SOLID, and design patterns.
50 questions
JuniorTheoryVery commonWhat is duck typing?
What is duck typing?
A style where an object's suitability is judged by the methods and attributes it actually has, not by its class or inheritance — if it provides the needed methods, it works, enabling polymorphism.
Common mistakes
- ✗Confusing duck typing with static type checking
- ✗Believing it requires inheriting a shared interface or ABC
- ✗Thinking it is about the object's class rather than its methods
Follow-up questions
- →How does an ABC with
__subclasshook__relate to duck typing? - →What runtime error appears when a duck-typed object lacks a method?
JuniorTheoryVery commonWhat is encapsulation, and is it the same as data hiding?
What is encapsulation, and is it the same as data hiding?
Encapsulation bundles data with the methods that operate on it into one unit, keeping related knowledge together and hiding implementation details. Data hiding (restricting access) is one goal of it, but encapsulation is broader than just hiding.
Common mistakes
- ✗Treating encapsulation and data hiding as exact synonyms
- ✗Thinking encapsulation requires language-enforced private fields
- ✗Forgetting that bundling methods with data is part of it
Follow-up questions
- →How does Python signal a 'private' attribute without enforcing it?
- →What does a leading double underscore do to an attribute name?
JuniorTheoryVery commonWhat are the four pillars of OOP?
What are the four pillars of OOP?
Encapsulation bundles data with the methods on it and hides internals; inheritance derives one class from another; polymorphism gives one interface many implementations; abstraction exposes only the essential features of a thing.
Common mistakes
- ✗Listing only three pillars and forgetting abstraction or encapsulation
- ✗Confusing encapsulation (bundling and hiding) with inheritance (deriving classes)
- ✗Treating polymorphism as merely operator or method overloading
Follow-up questions
- →Which pillar does Python's duck typing most directly support?
- →How does abstraction differ from encapsulation in practice?
JuniorTheoryVery commonWhat is polymorphism, and what forms does it take?
What is polymorphism, and what forms does it take?
Polymorphism lets one interface work with different types. Forms: ad-hoc (overloading), parametric (generics), and subtype — a derived type used via its base with overridden methods, the most commonly meant. Python leans on duck typing.
Common mistakes
- ✗Equating polymorphism only with overloading
- ✗Believing Python needs a common base class for polymorphic use
- ✗Confusing parametric (generics) with subtype polymorphism
Follow-up questions
- →How does duck typing provide polymorphism without inheritance?
- →Why does Python not support method overloading by signature?
JuniorTheoryVery commonHow do you call a parent class's method with super()?
How do you call a parent class's method with super()?
Call super() with no arguments in Python 3; it returns a proxy dispatching to the next class in the MRO, so super().__init__() runs the parent's method. It cooperates with multiple inheritance.
Common mistakes
- ✗Thinking
super()always means the literal first base class - ✗Believing the zero-argument form is invalid in Python 3
- ✗Forgetting that
super()follows the MRO, not the static hierarchy
Follow-up questions
- →Why can
super()reach a sibling class not in the direct parent chain? - →What happens if you skip
super().__init__()in a subclass?
MiddleTheoryVery commonHow does abstraction differ from encapsulation?
How does abstraction differ from encapsulation?
Abstraction is the design act of exposing only essential features and ignoring irrelevant detail. Encapsulation is the mechanism that bundles data with methods and hides internals. Abstraction = what to expose; encapsulation = how to hide it.
Common mistakes
- ✗Treating abstraction and encapsulation as the same thing
- ✗Believing abstraction requires an explicit
ABCto exist - ✗Reducing abstraction to access modifiers on fields
Follow-up questions
- →Can you have encapsulation without much abstraction?
- →How do
abc.ABCandProtocoleach express abstraction?
MiddleTheoryVery commonWhy prefer composition over inheritance?
Why prefer composition over inheritance?
Inheritance tightly couples a subclass to its base and exposes its internals, making hierarchies rigid. Composition (an object holding collaborators and delegating) is more flexible and swappable at runtime. Use inheritance only for true is-a cases.
Common mistakes
- ✗Reaching for inheritance to reuse code rather than for is-a
- ✗Building deep fragile hierarchies that are hard to change
- ✗Assuming composition cannot deliver polymorphism
Follow-up questions
- →How does delegation let you swap a collaborator at runtime?
- →When is inheritance still the right tool over composition?
MiddleTheoryVery commonWhat is a context manager, and how do you write one?
What is a context manager, and how do you write one?
An object usable with with defining __enter__ (its return binds to as) and __exit__ (runs on exit even on exception, receiving exc type, value, traceback). You can also wrap a generator with @contextlib.contextmanager.
Common mistakes
- ✗Thinking
withjust callsclose()rather than the enter/exit protocol - ✗Believing
__exit__is skipped when an exception is raised - ✗Assuming you cannot build one from a generator via
contextlib
Follow-up questions
- →What does returning
Truefrom__exit__do to a raised exception? - →How does
contextlib.contextmanagermapyieldto enter and exit?
MiddleTheoryVery commonWhy is object() == object() False, and how do is and == differ?
Why is object() == object() False, and how do is and == differ?
By default == falls back to identity — the default __eq__ compares id() — and two fresh objects differ, so it is False. is always tests identity; == tests equality, customizable via __eq__.
Common mistakes
- ✗Thinking
==compares field values by default - ✗Treating
isand==as interchangeable - ✗Forgetting the default
__eq__falls back to identity comparison
Follow-up questions
- →Why should overriding
__eq__usually come with__hash__? - →Why can small integers compare
Truewithiseven when freshly made?
SeniorTheoryVery commonHow do __new__ and __init__ differ, and in what order do they run?
How do __new__ and __init__ differ, and in what order do they run?
__new__(cls, ...) creates and returns the new instance and runs first; __init__(self, ...) initializes that instance and returns None, running second. __init__ runs only if __new__ returned an instance of cls.
Common mistakes
- ✗Thinking
__init__creates the object rather than initializing it - ✗Believing
__init__runs before__new__ - ✗Assuming
__new__must returnNone
Follow-up questions
- →What happens to
__init__if__new__returns a different class's instance? - →How does overriding
__new__enable a singleton pattern?
JuniorTheoryCommonHow do you define an abstract base class with abc.ABC, and what does @abstractmethod enforce?
How do you define an abstract base class with abc.ABC, and what does @abstractmethod enforce?
Subclass abc.ABC (or set metaclass=ABCMeta) and decorate the required methods with @abstractmethod. Python then refuses to instantiate the base, or any subclass that leaves an abstract method unimplemented, raising TypeError — enforcing a contract concrete subclasses must satisfy.
Common mistakes
- ✗Thinking
abc.ABCalone makes methods abstract without@abstractmethod - ✗Believing a plain
raise NotImplementedErrorblocks instantiation - ✗Expecting the abstract-method check to fire at import rather than on instantiation
Follow-up questions
- →How does an
abc.ABCinterface compare with a structuralProtocol? - →Can you combine
@abstractmethodwith@propertyor@classmethod?
JuniorTheoryCommonWhat do DRY, KISS, and YAGNI mean?
What do DRY, KISS, and YAGNI mean?
DRY — Don't Repeat Yourself: factor duplication into one reusable place. KISS — Keep It Simple: avoid needless cleverness. YAGNI — You Aren't Gonna Need It: don't build speculative features before they're required.
Common mistakes
- ✗Reading
YAGNIas a license to skip needed design entirely - ✗Applying
DRYso zealously it creates the wrong abstraction - ✗Confusing
KISS(simplicity) with premature optimization
Follow-up questions
- →When can over-applying
DRYactually hurt a design? - →How does
YAGNIinteract with the open/closed principle?
JuniorTheoryCommonWhat are magic (dunder) methods in Python?
What are magic (dunder) methods in Python?
Methods with double-underscore names like __init__, __len__, __add__ that the interpreter invokes implicitly through built-ins, operators, or syntax — len(x) calls x.__len__(), a + b calls a.__add__(b).
Common mistakes
- ✗Calling them private methods rather than implicitly-invoked hooks
- ✗Thinking you must invoke
__len__or__add__explicitly for built-ins to work - ✗Assuming the double underscore hides them from operators and built-ins
Follow-up questions
- →What does
__repr__return and how does it differ from__str__? - →Which dunder method makes an object usable in a
forloop?
JuniorTheoryCommonHow are an object's instance attributes stored?
How are an object's instance attributes stored?
By default in a per-instance __dict__ mapping name to value, so attributes can be added or removed dynamically at runtime. obj.__dict__ or vars(obj) shows that dict directly.
Common mistakes
- ✗Thinking attributes are fixed at class definition time
- ✗Believing instance attributes live on the class, not the instance
- ✗Forgetting
obj.__dict__exposes the per-instance mapping
Follow-up questions
- →How does
__slots__change where instance attributes are stored? - →Why does
dir(obj)show more names thanobj.__dict__?
JuniorTheoryCommonDoes Python support multiple inheritance?
Does Python support multiple inheritance?
Yes — a class may list several bases, as in class A(B, C). Method and attribute lookup follows the MRO computed by C3, which enables mixins but can introduce ambiguity the MRO order resolves.
Common mistakes
- ✗Claiming Python forbids multiple inheritance like Java
- ✗Thinking the first base always wins regardless of the MRO
- ✗Assuming clashing methods from two bases are merged and both run
Follow-up questions
- →How do mixins differ from regular multiple inheritance?
- →What error appears when bases form an inconsistent hierarchy?
MiddleTheoryCommonWhat problem does the structural pattern adapter solve?
What problem does the structural pattern adapter solve?
It wraps a class whose interface you can't change so it matches the interface the client expects, translating calls between the two. It lets otherwise-incompatible code work together — fitting a third-party API onto your own interface — without touching either side's source.
Common mistakes
- ✗Confusing adapter (reconcile interfaces) with decorator (add behavior)
- ✗Mixing it up with facade, which simplifies a whole subsystem
- ✗Editing the adapted class's source instead of wrapping it
Follow-up questions
- →How does an object adapter (composition) differ from a class adapter (inheritance)?
- →Why is duck typing often an adapter substitute in Python?
MiddleCodeCommonWhat does this class-attribute shadowing print?
What does this class-attribute shadowing print?
1 1, then 1 2, then 3 2. Initially Child.x is inherited from Parent. Assigning Child.x = 2 creates a separate attribute on Child that shadows the parent's, so later changing Parent.x to 3 no longer affects Child, which keeps its own 2.
Common mistakes
- ✗Thinking parent and child always share one attribute
- ✗Believing an attribute write propagates up the MRO to the parent
- ✗Expecting the child's shadow to clear when the parent is reassigned
Follow-up questions
- →Where does attribute lookup search, and in what order, along the MRO?
- →How does this differ for a mutable class attribute like a list?
MiddleCodeCommonWrite a context manager for resource cleanup
Write a context manager for resource cleanup
Use @contextlib.contextmanager: acquire before yield value (bound to as), and release in a finally so cleanup runs even on exception. It is equivalent to a class with __enter__ / __exit__. The with statement guarantees the code after yield (the finally) executes.
Common mistakes
- ✗Omitting
try/finally, so cleanup is skipped when the body raises - ✗Using
returninstead ofyieldin a@contextmanager - ✗Releasing before the
yield, so the body runs after cleanup
Follow-up questions
- →How does
@contextmanagermap the code before/afteryieldto__enter__/__exit__? - →What can
__exit__do that a@contextmanagerfinallyblock cannot easily do?
MiddleTheoryCommonWhat does the structural pattern decorator (GoF) do?
What does the structural pattern decorator (GoF) do?
It wraps an object in another of the same interface to add behavior at runtime, stacking layers without subclassing. Each wrapper forwards to the inner object and adds its own piece, so features compose dynamically. It is distinct from Python's @decorator function syntax.
Common mistakes
- ✗Equating the GoF decorator pattern with Python's
@decoratorsyntax - ✗Confusing it with adapter, which changes the interface rather than preserving it
- ✗Adding features by subclass explosion instead of runtime wrapping
Follow-up questions
- →How does the decorator pattern avoid the subclass explosion of feature combinations?
- →How can Python's
@decorators implement the decorator pattern?
MiddleTheoryCommonWhat problem does the creational pattern factory method solve?
What problem does the creational pattern factory method solve?
It moves object creation into a method (often overridden by subclasses) so callers request a product by intent, not by concrete class. Calling code depends only on the abstract product, so a new variant is a new subclass — no edits to existing call sites.
Common mistakes
- ✗Confusing it with singleton — factory method is about which class, not how many instances
- ✗Hard-coding the concrete product type instead of returning the abstract one
- ✗Thinking it eliminates subclasses rather than relying on them
Follow-up questions
- →How does factory method differ from the abstract factory pattern?
- →Why is factory method often unnecessary in Python's duck-typed world?
MiddleTheoryCommonWhat is a mixin in Python?
What is a mixin in Python?
A small helper class added to an inheritance chain to contribute specific behavior or methods, not meant to stand alone or be instantiated. It is technically an ordinary class; convention just names it ...Mixin.
Common mistakes
- ✗Thinking
mixinis a language keyword or special construct - ✗Believing a mixin requires metaclasses to work
- ✗Treating a mixin as a standalone class meant to be instantiated
Follow-up questions
- →Where should a mixin sit in the base list relative to the main class?
- →How does a mixin rely on methods it does not itself define?
MiddleTheoryCommonWhat problem does the behavioral pattern observer solve?
What problem does the behavioral pattern observer solve?
It sets up a one-to-many link: when a subject's state changes, every registered observer is notified automatically, usually by a notify loop calling each observer's update. It decouples the subject from its listeners — the subject knows only the observer interface, not concrete types.
Common mistakes
- ✗Thinking observers poll instead of being sent push notifications
- ✗Confusing it with strategy, which swaps an algorithm
- ✗Coupling the subject to concrete observer types instead of an interface
Follow-up questions
- →How can the observer pattern cause memory leaks if observers aren't deregistered?
- →How does observer relate to Python callbacks or the pub/sub model?
MiddleTheoryCommonHow do you implement a singleton in Python?
How do you implement a singleton in Python?
Ensure one shared instance. Idiomatic options: a module (imported once, so a natural singleton), a metaclass overriding __call__, a decorator caching the instance, or overriding __new__. Metaclass and module approaches cover subclasses well.
Common mistakes
- ✗Forgetting modules are themselves natural singletons
- ✗Using
__init__to block a second instance instead of__new__ - ✗Ignoring thread safety when lazily creating the instance
Follow-up questions
- →Why is the singleton pattern often criticized as a hidden global?
- →How does a metaclass
__call__enforce one instance per class?
MiddleTheoryCommonWhat problem does the behavioral pattern strategy solve?
What problem does the behavioral pattern strategy solve?
It captures a family of interchangeable algorithms behind a shared interface and lets the client pick or swap one at runtime, instead of hard-coding if/elif branches. The context holds a strategy object and delegates to it, so a new algorithm drops in without touching the context.
Common mistakes
- ✗Confusing strategy (swap algorithm) with observer (notify listeners)
- ✗Baking algorithm choice into subclasses instead of an injectable object
- ✗Thinking the strategy can't change after the context is built
Follow-up questions
- →How is strategy often just a function or
callablein Python? - →How does strategy differ from the state pattern, which looks similar?
JuniorTheoryOccasionalWhat categories of design patterns are there?
What categories of design patterns are there?
The GoF groups them into three: Creational (object creation — factory, singleton, builder), Structural (composing objects — adapter, facade, decorator), and Behavioral (object interaction and responsibility — observer, strategy, iterator).
Common mistakes
- ✗Mixing up which pattern belongs to which category
- ✗Thinking patterns are language features rather than design ideas
- ✗Assuming every pattern relies on inheritance
Follow-up questions
- →Which category does the strategy pattern belong to?
- →Why are some GoF patterns redundant in Python?
MiddleTheoryOccasionalWhat does the creational pattern abstract factory provide?
What does the creational pattern abstract factory provide?
An interface for creating whole families of related objects without naming concrete classes. One factory produces a matched set — say, all widgets of one UI theme — so swapping the factory swaps the entire family at once and stops products of different families from mixing.
Common mistakes
- ✗Conflating it with factory method, which makes one product, not a family
- ✗Confusing it with builder's step-by-step assembly of a single object
- ✗Letting products from different concrete factories mix
Follow-up questions
- →When is the extra abstraction of an abstract factory not worth it?
- →How do factory method and abstract factory relate to each other?
MiddleTheoryOccasionalWhat problem does the structural pattern bridge solve?
What problem does the structural pattern bridge solve?
It splits an abstraction from its implementation so the two vary independently, holding the implementation via composition instead of inheritance. This avoids a class explosion when two orthogonal dimensions — say shapes × rendering backends — would otherwise multiply into NxM subclasses.
Common mistakes
- ✗Confusing bridge (decouple two dimensions) with adapter (reconcile interfaces)
- ✗Using inheritance where bridge calls for composition
- ✗Applying it when there's only one axis of variation
Follow-up questions
- →How does bridge differ from strategy, which also composes behavior?
- →When does an NxM subclass explosion justify introducing a bridge?
MiddleTheoryOccasionalWhat problem does the creational pattern builder solve?
What problem does the creational pattern builder solve?
It constructs a complex object step by step through a sequence of method calls, separating how it is assembled from its final representation. You add only the parts you need (no telescoping constructors), then call build() to get the finished product.
Common mistakes
- ✗Confusing builder (stepwise assembly) with factory (pick a class)
- ✗Returning or using the product before
build()finalizes it - ✗Using it for simple objects that don't need staged construction
Follow-up questions
- →How does builder eliminate the telescoping-constructor problem?
- →How is a fluent method-chaining API related to the builder pattern?
MiddleTheoryOccasionalWhat is the difference between cohesion and coupling?
What is the difference between cohesion and coupling?
Cohesion measures how focused one module is — high cohesion (it does one well-defined thing) is good. Coupling measures interdependence between modules — low coupling (modules depend minimally) is good. Aim for high cohesion, low coupling.
Common mistakes
- ✗Swapping the definitions of cohesion and coupling
- ✗Forgetting the goal is high cohesion plus low coupling
- ✗Treating the two terms as interchangeable
Follow-up questions
- →How does low coupling make unit testing easier?
- →Which
SOLIDprinciple most directly raises cohesion?
MiddleTheoryOccasionalWhat problem does the structural pattern facade solve?
What problem does the structural pattern facade solve?
It puts one simplified, high-level interface in front of a complex subsystem of many classes, so clients use the facade instead of wiring the parts themselves. It cuts coupling to internals and gives a clean entry point, while still allowing direct subsystem access when needed.
Common mistakes
- ✗Thinking facade forbids direct subsystem access rather than just offering a simpler path
- ✗Confusing it with adapter, which translates one interface into another
- ✗Assuming a facade must wrap every subsystem class exhaustively
Follow-up questions
- →How does facade differ from adapter in intent?
- →Can a facade itself become a god object, and how do you avoid that?
MiddleTheoryOccasionalWhat is the MRO, and what determines it?
What is the MRO, and what determines it?
Method Resolution Order — the deterministic linear order Python searches base classes for an attribute or method. It is computed by C3 linearization, exposed as Cls.__mro__, and is the order super() walks.
Common mistakes
- ✗Describing the MRO as plain depth-first left-to-right search
- ✗Thinking the MRO is recomputed on every attribute access
- ✗Forgetting
super()dispatches along the same MRO
Follow-up questions
- →What does the C3 algorithm guarantee about local precedence?
- →When does Python refuse to build an MRO and raise an error?
MiddleTheoryOccasionalWhat's the difference between _x and __x attribute names?
What's the difference between _x and __x attribute names?
A single leading underscore (_x) is a convention for internal use; it stays fully accessible. A double leading underscore (__x) triggers name mangling — rewritten to _ClassName__x so subclasses won't override it.
Common mistakes
- ✗Believing
__xis truly private and inaccessible from outside - ✗Thinking
_xraises an error on external access - ✗Assuming
_xalso undergoes name mangling like__x
Follow-up questions
- →How can you still reach a mangled
__xfrom outside the class? - →Why does a trailing dunder like
__x__skip name mangling?
MiddleTheoryOccasionalWhat problem does the structural pattern proxy solve?
What problem does the structural pattern proxy solve?
It puts a stand-in object with the same interface in front of a real object to control access — adding lazy loading, caching, access checks, or remote calls without changing the client. The client can't tell the proxy from the real subject, since both share one interface.
Common mistakes
- ✗Confusing proxy (control access, same interface) with adapter (change interface)
- ✗Mixing it up with decorator's feature-stacking intent
- ✗Exposing a different interface than the real subject
Follow-up questions
- →How does a virtual proxy implement lazy initialization?
- →How does proxy differ from decorator when both wrap an object?
MiddleTheoryOccasionalWhat does the Single Level of Abstraction Principle (SLAP) require?
What does the Single Level of Abstraction Principle (SLAP) require?
That all statements inside a single function sit at the same level of abstraction — don't mix high-level orchestration with low-level detail in one body. You extract the low-level steps into named helper functions, so each function reads as a coherent narrative at one altitude.
Common mistakes
- ✗Reading 'single level' as a literal indentation or nesting limit
- ✗Confusing it with limiting inheritance depth
- ✗Mixing orchestration and detail in one function instead of extracting helpers
Follow-up questions
- →How does SLAP relate to keeping functions short and well-named?
- →How do SLAP and the DRY principle reinforce each other?
MiddleTheoryOccasionalWhat do the SOLID principles stand for?
What do the SOLID principles stand for?
SRP (single responsibility — one reason to change), OCP (open for extension, closed for modification), LSP (subtypes replace their base), ISP (small client-specific interfaces), DIP (depend on abstractions). Martin's five OOD rules.
Common mistakes
- ✗Misremembering one of the five letters' meaning
- ✗Confusing
ISP(interface splitting) withSRP - ✗Thinking
LSPforbids subclassing rather than constraining it
Follow-up questions
- →Give a concrete example of an
LSPviolation. - →How does
DIPrelate to dependency injection?
SeniorTheoryOccasionalHow does the MRO algorithm C3 linearization resolve the diamond problem?
How does the MRO algorithm C3 linearization resolve the diamond problem?
C3 merges each base's MRO plus the base list, keeping local precedence and monotonicity. For a diamond A(B, C) both deriving D, it yields [A, B, C, D, object] — D appears once, after all its subclasses.
Common mistakes
- ✗Thinking C3 is depth-first so
Dlands right afterB - ✗Believing the shared base is duplicated in the result
- ✗Assuming the leftmost base's full chain is taken first to
object
Follow-up questions
- →What property does monotonicity guarantee across a subclass's MRO?
- →Give a base ordering where C3 fails and raises
TypeError.
SeniorTheoryOccasionalWhat does __slots__ do, and what are its trade-offs?
What does __slots__ do, and what are its trade-offs?
__slots__ declares a fixed attribute set and removes the per-instance __dict__, saving memory and slightly speeding access — valuable for millions of instances. The cost: you can't add attributes outside the list.
Common mistakes
- ✗Thinking
__slots__disables__getattr__/__setattr__hooks - ✗Believing
__slots__makes attributes private - ✗Claiming
__slots__brings no memory benefit
Follow-up questions
- →What happens to
__slots__when a subclass omits its own declaration? - →How do you keep
__weakref__support on a slotted class?
SeniorTheoryOccasionalWhat does the Dependency Inversion Principle require?
What does the Dependency Inversion Principle require?
High-level modules shouldn't depend on low-level ones — both depend on abstractions; and abstractions shouldn't depend on details, details depend on abstractions. In practice inject an interface, not a concrete class, to decouple policy from detail.
Common mistakes
- ✗Confusing dependency inversion with inverting inheritance
- ✗Letting the abstraction be shaped by one concrete implementation
- ✗Thinking
DIPis satisfied just by importing an interface
Follow-up questions
- →Who should own the abstraction — the high-level or low-level module?
- →How do
Protocoltypes supportDIPin Python?
MiddleTheoryRareWhat does the behavioral pattern chain of responsibility solve?
What does the behavioral pattern chain of responsibility solve?
It passes a request along a chain of handlers until one handles it, so the sender doesn't know which will. Each handler either processes the request or forwards it to the next, decoupling sender from receiver and letting you reorder or add handlers freely.
Common mistakes
- ✗Thinking every handler runs rather than stopping at the first that handles it
- ✗Confusing it with observer's simultaneous broadcast
- ✗Coupling the sender to a specific handler instead of the chain
Follow-up questions
- →What happens if no handler in the chain handles the request?
- →How does WSGI/Django middleware resemble chain of responsibility?
MiddleTheoryRareWhat problem does the behavioral pattern command solve?
What problem does the behavioral pattern command solve?
It packages a request as an object carrying the action plus its arguments, so calls can be queued, logged, passed around, or undone. The invoker triggers execute without knowing the concrete operation, decoupling who requests an action from who actually performs it.
Common mistakes
- ✗Thinking command is just a direct method call with no request object
- ✗Confusing it with observer's broadcast of events
- ✗Missing that reifying the request is what enables undo and queuing
Follow-up questions
- →How does the command pattern enable undo/redo functionality?
- →How is a Python
callableorfunctools.partiala lightweight command?
MiddleTheoryRareWhat problem does the structural pattern composite solve?
What problem does the structural pattern composite solve?
It arranges objects into tree structures and lets clients treat individual leaves and whole branches uniformly through one shared interface. A folder and a file both expose size(); the folder just recurses into its children. It models part-whole hierarchies cleanly.
Common mistakes
- ✗Giving leaves and composites different interfaces so clients must type-check
- ✗Confusing it with decorator's linear wrapping
- ✗Forgetting the recursion that makes branch operations work
Follow-up questions
- →How does a composite handle an operation that is meaningless on a leaf?
- →How does composite relate to walking a filesystem tree?
MiddleTheoryRareWhat problem does the structural pattern flyweight solve?
What problem does the structural pattern flyweight solve?
It minimizes memory by sharing one immutable instance of common intrinsic state across many objects, keeping per-object extrinsic state outside. Thousands of tree sprites share one texture object; only positions differ. Python's string and small-int interning is a built-in example.
Common mistakes
- ✗Sharing mutable state instead of immutable intrinsic state
- ✗Confusing it with singleton — one object total vs many sharing state
- ✗Forgetting to keep extrinsic state outside the flyweight
Follow-up questions
- →Why must a flyweight's shared intrinsic state be immutable?
- →How is CPython's caching of small integers a flyweight?
MiddleTheoryRareWhat problem does the behavioral pattern interpreter solve?
What problem does the behavioral pattern interpreter solve?
It defines a class hierarchy representing the grammar of a small language plus an interpret method to evaluate sentences in it. Each grammar rule becomes a node type; an expression tree is built and recursively evaluated. It fits simple DSLs like filters or arithmetic.
Common mistakes
- ✗Confusing the interpreter pattern with a full lexer/parser/compiler
- ✗Forgetting it pairs with composite to build the expression tree
- ✗Using it for complex grammars where a real parser belongs
Follow-up questions
- →Why is the interpreter pattern impractical for complex grammars?
- →How does the interpreter pattern relate to the composite pattern?
MiddleTheoryRareWhat problem does the behavioral pattern mediator solve?
What problem does the behavioral pattern mediator solve?
It centralizes communication between many objects in one mediator so they no longer reference each other directly. Components talk to the mediator, which coordinates them, turning a tangled many-to-many web of dependencies into a hub-and-spoke. Think a chat room routing messages.
Common mistakes
- ✗Confusing mediator (coordinates peers) with observer (one-way notify)
- ✗Mixing it up with facade's external simplification role
- ✗Leaving direct component-to-component references in place
Follow-up questions
- →How can a mediator itself grow into a god object?
- →How does mediator differ from observer when both reduce coupling?
MiddleTheoryRareWhat problem does the behavioral pattern memento solve?
What problem does the behavioral pattern memento solve?
It captures an object's internal state into a snapshot (the memento) that can be stored and later restored, without exposing the object's internals. The originator creates and restores from mementos; a caretaker just holds them. It is the basis of undo/redo.
Common mistakes
- ✗Confusing memento (snapshot state) with command (reify an operation)
- ✗Exposing the originator's internals instead of an opaque memento
- ✗Putting restore logic in the caretaker rather than the originator
Follow-up questions
- →How does memento underpin undo/redo, and how does it relate to command?
- →What are the memory costs of keeping many mementos around?
MiddleTheoryRareWhat problem does the creational pattern prototype solve?
What problem does the creational pattern prototype solve?
It creates new objects by cloning an existing instance (the prototype) instead of instantiating a class from scratch. Useful when construction is expensive or the concrete type is decided at runtime — you copy a ready object, often via copy.deepcopy, and tweak it.
Common mistakes
- ✗Confusing prototype (clone an instance) with factory (instantiate a class)
- ✗Forgetting deep vs shallow copy when nested mutables are involved
- ✗Thinking it returns a shared instance like singleton
Follow-up questions
- →When does prototype need
deepcopyrather than a shallowcopy? - →How does Python's
__copy__/__deepcopy__protocol support prototype?
MiddleTheoryRareWhat problem does the behavioral pattern state solve?
What problem does the behavioral pattern state solve?
It lets an object change its behavior when its internal state changes, by delegating to a separate state object the context swaps at runtime. It replaces sprawling if/elif on a status flag with one class per state, each defining the transitions out of itself.
Common mistakes
- ✗Confusing state (context self-transitions) with strategy (client injects behavior)
- ✗Keeping a giant
if/elifon a status flag instead of state objects - ✗Forgetting each state defines its own outgoing transitions
Follow-up questions
- →How does state differ from strategy when both delegate to a swappable object?
- →Where does transition logic live — in the context or the state objects?
MiddleTheoryRareWhat problem does the behavioral pattern template method solve?
What problem does the behavioral pattern template method solve?
It defines the skeleton of an algorithm in a base-class method, deferring specific steps to subclass overrides. The overall sequence is fixed; subclasses fill in the variable parts without changing the structure. It is the 'Hollywood principle' — the base calls you.
Common mistakes
- ✗Confusing template method (inheritance, fixed skeleton) with strategy (composition)
- ✗Letting subclasses change the step order rather than just fill steps
- ✗Putting the fixed sequence in subclasses instead of the base method
Follow-up questions
- →How does template method differ from strategy in how it varies behavior?
- →What is a 'hook' method in a template method, and why make it optional?
MiddleTheoryRareWhat problem does the GoF behavioral pattern visitor solve?
What problem does the GoF behavioral pattern visitor solve?
It lets you add new operations to a fixed set of element classes without modifying them, by moving the operation into a visitor object. Each element calls visitor.visit(self) (double dispatch); a new operation is a new visitor, not edits across every element class.
Common mistakes
- ✗Getting the trade-off backwards — visitor eases new operations, not new element types
- ✗Confusing the double-dispatch visit with simple iteration
- ✗Editing every element class instead of writing one new visitor
Follow-up questions
- →Why does adding a new element type force edits to every existing visitor?
- →How does double dispatch make visitor work without
isinstancechecks?