On this page
What you need first
This lesson uses ideas from these earlier lessons. Read them first if any idea is new to you.
01Why interviewers ask this
The interviewer in a machine-coding round may ask: "Why add a class only to create objects?" or "Should this class have only one object?"
A weak answer uses a pattern name because it sounds good. A strong answer says what problem the pattern solves here, and what it costs. At SDE-3 level you should say when not to use it.
02The idea in plain words
Think about a juice shop. You say "mango", and the shop makes a mango juice. That is a factory. A thali is a plate with many small dishes. The cook adds what you choose and checks the plate before it leaves. That is a builder. One shared water tap that every worker can change is the Singleton trap.
A design pattern is a name for a code shape that solves a problem that comes again and again. A smell is a sign in the code that a pattern may help. These patterns create objects. We use the food order of lesson 106.
Factory: one place that chooses the class
A factory is a function that creates the right object for a name. The smell is a chain of if kind == "upi" checks, repeated wherever the name arrives. A dictionary is enough:
class UpiPayment:
def pay(self, amount):
return f"UPI paid Rs {amount}"
# CardPayment and CashOnDelivery have the same shape
PAYMENTS = {"upi": UpiPayment, "card": CardPayment, "cod": CashOnDelivery}
def make_payment(kind):
if kind not in PAYMENTS:
raise ValueError(f"unknown payment kind: {kind}")
return PAYMENTS[kind]()A new kind is a new class and one new line in the dictionary. This is the open-closed rule of lesson 106.
When not to use it. With one class, or when the caller already knows the class, write UpiPayment(). It is clearer.
Builder: build a complex object step by step
An order may have many optional parts, and a call such as Order(items, "98000", None, None, "", False) is hard to read. A builder collects the parts one at a time. Then build() checks them.
class Order:
def __init__(self, items, phone, coupon="", note=""):
self.items, self.phone = items, phone
self.coupon, self.note = coupon, note
def subtotal(self):
return sum(price * qty for price, qty in self.items)
class OrderBuilder:
def __init__(self):
self._items, self._phone = [], ""
self._coupon, self._note = "", ""
def item(self, price, qty):
self._items.append((price, qty))
return self
def coupon(self, code):
self._coupon = code
return self
def note(self, text):
self._note = text
return self
def phone(self, number):
self._phone = number
return self
def build(self):
if not self._items:
raise ValueError("an order needs at least one item")
if not self._phone:
raise ValueError("an order needs a phone number")
return Order(tuple(self._items), self._phone, self._coupon, self._note)Each method returns the builder, so the calls chain. No half-made order exists.
When not to use it. With three or four values, use keyword arguments (arguments with names), such as Order(items=x, phone="98000").
Singleton: one object, global access
A Singleton is a class that allows only one object, and any code can reach it. People use it for a counter or settings. Here is one for order IDs:
class OrderIds:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.last = 0
return cls._instance
def next(self):
self.last += 1
return self.lastOrderIds() always returns the same object. That is the trap:
- Hidden shared state. A test that places an order changes the counter for every later test.
- Hidden dependency. A method that calls
OrderIds()does not show that it needs IDs, and you cannot pass in a fake (lesson 106). - Anyone can change it.
The fix is to separate "we need one" from "the class forces one". Write a normal class, create one object at start-up, and pass it in through the constructor (dependency injection, lesson 106). The program has one, and a test makes its own.
class IdSource:
def __init__(self):
self.last = 0
def next(self):
self.last += 1
return self.last
class OrderService: # a small service: it gets the source from outside
def __init__(self, ids):
self.ids = ids
def place(self):
return self.ids.next()Stuck on something? The AI tutor has read this lesson. Ask it with a free account
03Pause and predict
Pause. Guess first.
Test A places two orders and expects IDs 1 and 2. Test B then places one order and expects ID 1. Both use the
OrderIdsSingleton in the same program. What ID does test B see?
Write your guess before you read on. The answer is in the worked example.
04Worked example
The order is 2 biryani at Rs 250 and 1 lassi at Rs 60. The subtotal is 2 x 250 + 60 = Rs 560 (lesson 106).
Factory. make_payment("upi").pay(560) returns UPI paid Rs 560. For wallets, we write WalletPayment and add one dictionary line. Changed lines in old code: 0.
Builder. OrderBuilder().item(250, 2).item(60, 1).phone("98000").build() gives an order with subtotal 560. With no items, build() raises an order needs at least one item.
Singleton. Three tests run in one program, in this order: A places 2 orders, B places 1, and C places 3. Each expects its first ID to be 1. With OrderIds:
A sees 1 and 2. B sees 3, which answers the predict box, and B fails. C sees 4, 5 and 6, and C fails. With IdSource, each test builds OrderService(IdSource()). A sees 1 and 2, B sees 1, and C sees 1, 2 and 3. All pass, in any order.
05Trade-offs
- A factory solves one place that chooses the class. It costs one more step to read. I would skip it for one kind.
- A builder solves unreadable constructors and half-made objects. It costs an extra class. I would use keyword arguments for few values.
- A Singleton solves a guarantee of one object. It costs hidden shared state and failing tests. I would create one object and pass it in.
06Common misconceptions
"A pattern makes the code better, so I should use all three." Each adds code. Use one when its smell appears.
"A Singleton is how I make sure there is only one." Passing one object to everyone gives you one, with no global access.
07How to say it in the interview
Say it like this
I use a factory when a name chooses the class, and a builder when an object has many optional parts to check. I avoid a Singleton, because it hides shared state, so I create one object and pass it in.
08Check your understanding
- Name one smell for Factory and one for Builder. Why is a Singleton a trap?
- Tests A, B and C run in this order and place 3, 2 and 4 orders. Each expects its first ID to be 1. With the
OrderIdsSingleton, which IDs does each test see, and which tests fail? - A teammate writes a Singleton
Database. The tests change each other's data. What do you do?
09Answers
Show the answers
- Factory: the same
if kind == ...chain in many places. Builder: a constructor with many optional values. A Singleton hides shared state, so tests change each other. - A sees 1, 2 and 3. B sees 4 and 5. C sees 6, 7, 8 and 9. B and C fail.
- Make
Databasea normal class. Create one object at start-up and pass it in. A test passes a fresh in-memory copy, so tests no longer share data.
10Sources
- The patterns: Gamma, Helm, Johnson and Vlissides, "Design Patterns" (1994). Summary: https://en.wikipedia.org/wiki/Design_Patterns (opens in a new tab) (checked 20 Oct 2026).
- The code was run with Python 3. The order and prices are made up. The book describes bigger forms of factory.
Try it now, no sign-up
Related lessons and practice
Ask the AI tutor about this lesson
The tutor has read this lesson. Ask in your own words, any time. Free account, 20 questions a day.
Reading never needs an account. The account is for the tutor, the quiz, your notes and your progress.
All lessons in Module 17: Low-level design and machine coding