Replace Constructor with Factory Method: Order a Named Thali, Let the Kitchen Decide
Learn the Replace Constructor with Factory Method refactoring with a thali restaurant story, before/after TypeScript and C#, safe step-by-step migration, and a clear comparison with the full Factory Method design pattern.
🍛 The thali counter problem
Come with me to Annapurna Bhavan, a busy lunch restaurant in Pune. The owner is Sunita tai, a no-nonsense lady who has run the place for twenty years. The head cook is Mohan, who can make dal that people cross the city for. And today a new customer walks in — Arjun, a first-year engineering student who has just shifted to the hostel nearby and is hungry enough to eat the menu board itself.
Except there is no menu board. In its early days, Annapurna Bhavan had a strange system. Every customer walked straight to the kitchen counter and assembled their own thali, item by item, following rules that were written nowhere.
Take a steel plate. Two rotis from that basket — the left basket, not the right one, the right one is for parcels. Dal from the second pot, not the first — the first is for staff. Rice, one scoop, using the flat ladle. Sabzi of the day. Pickle on the left edge of the plate, always the left. Papad balanced on top so it does not get soggy.
Arjun knows none of this. He takes three rotis and no rice. The uncle behind him takes dessert but no spoon. A school kid fills his entire plate with papad and nothing else, and pays full thali price for it. Mohan spends half his day shouting corrections across the counter: "Not that pot! One scoop only! Where is your rice?"
And here is the worse problem, the one Sunita tai loses sleep over. Whenever the kitchen changes anything — a new dal pot, new portion sizes, a price revision — every single customer has to relearn the steps. Two hundred customers, all carrying the recipe in their own heads, all slightly wrong in their own way.
One Monday, Sunita tai does one simple thing. She prints a menu with named thalis. "Veg Thali." "Special Thali." "Mini Thali." Now Arjun only says a name. Mohan's kitchen decides how to assemble it. The kitchen can swap today's sabzi, reuse pre-portioned dal cups during the lunch rush, and quietly serve a festival version on Diwali with kheer added — and no customer needs to know any of this. The name carries the intent; the freedom of preparation stays inside the kitchen.
In code, calling a raw constructor like new Meal(true, false, 2, "steel") is Arjun assembling the plate at the counter from a recipe he half-remembers. A named factory method like Meal.vegThali() is ordering from the menu. Today's refactoring, Replace Constructor with Factory Method, is the act of printing that menu.
Notice what the journey diagram is really saying. In the self-assembly days, both Arjun and Mohan are miserable, because the recipe lives in two hundred heads. In the menu days, both are happy, because the recipe lives in exactly one kitchen. Refactorings that make both the caller and the maintainer happier at the same time are rare and precious — this is one of them.
📜 What is Replace Constructor with Factory Method?
A constructor is a special method, and it comes with three hard limits that no mainstream language lets you escape.
- It has only one name — the class name. You cannot call one constructor
openSavingsand anotheropenChecking. You can only pile up overloads, and overloads with similar parameter lists are a famous source of confusion. Quick — what doesnew Account(true)mean? Nobody knows without opening the class. - It always returns exactly its own class. A
Shapeconstructor can never hand back aCircle. It cannot return an interface. It cannot say "the kind depends on the input." The moment the caller writesnew Shape(...), the decision is already made, at the wrong place. - It always creates a fresh object. It cannot return a cached one, a pooled one, or a shared one. By the time the constructor body runs, memory has already been allocated. The decision "do we even need a new object?" was never available.
Replace Constructor with Factory Method says: write a normal static method whose job is creation. Inside, it calls the constructor (or one of several constructors, or a subclass constructor). Then point all callers at this method instead of at new. Finally, hide the constructor so the named method becomes the only door.
Martin Fowler describes this move in his classic Refactoring catalog — the second edition calls it Replace Constructor with Factory Function, because in JavaScript and similar languages the factory may be a free-standing function rather than a static method. The idea is identical: give creation a name and a brain.
One-line summary: a constructor is a door with the class name written on it and no guard inside; a factory method is a named door with a guard who can check you, redirect you, or hand you something already prepared.
Because the factory is an ordinary method, it can do everything ordinary methods do. It can carry an intention-revealing name. It can validate inputs and refuse politely. It can pick which subclass to build. It can return an interface so callers never learn the concrete type. It can hand back a cached instance — refactoring.guru highlights exactly this: a factory method can return an already created object, while a constructor must always allocate a new one.
Here is the whole idea as one picture, before we dive into code.
And here is the same comparison as a table you can revise from. Keep it next to you during interviews — this exact question gets asked.
| Ability | Plain constructor | Static factory method |
|---|---|---|
| Carry a descriptive name | No — always the class name | Yes — openSavings(), vegThali() |
| Return a subclass | No — always its own class | Yes — caller never knows which |
| Return an interface type | No | Yes — hides the concrete class |
| Return a cached or pooled instance | No — always allocates | Yes — may return an existing object |
| Refuse to create (validate first) | Only by throwing mid-construction | Yes — check before any allocation |
| Be passed around as a function value | Awkward in most languages | Yes — it is just a method reference |
| Be discovered automatically by DI containers and serializers | Yes — this is their default | Often needs explicit registration |
That last row is the honest one. The constructor's plainness is also its convenience: every framework on earth knows how to call it. We will return to this trade-off in the risks section.
🔍 When do we need it?
Watch for these signals in real code.
- Constructor overloads that fight each other.
new Account(true)vsnew Account(false)— what do the booleans mean? When you wish you could name the variations, you want a factory:Account.openSavings(),Account.openChecking(). - Callers choosing the subclass themselves. Code like
kind === "circle" ? new Circle() : new Square()copy-pasted in five files. The creation decision belongs in one kitchen, not at every table. - You want to hide the concrete class. Callers should depend on a
PaymentGatewayinterface, not onnew RazorpayGateway(...). A constructor cannot return an interface; a factory can. - Creation must be controlled. Pooling database connections, caching flyweight objects, limiting instances, registering each new object somewhere — a constructor cannot do any of this honestly.
- Validation before birth. If half-built objects must never exist, the factory checks first and throws a clear error instead of constructing garbage.
When Sunita tai's nephew (a CSE student, naturally) audited the billing codebase, he categorised every creation-related bug from the last year of code review comments. The result looked roughly like this — and it is typical of codebases that pass raw constructors around.
Every slice of that pie is a constructor limitation wearing a different costume. Wrong argument order happens because (2, true, false, 120) has no names. Copy-pasted recipes drift because the recipe lives at every call site. Invalid objects exist because nothing guards the door. Wrong subclasses get chosen because the caller was forced to decide something the kitchen should have decided.
🧭 The refactoring vs the full design pattern
Now, one very important clarification. This refactoring is related to, but smaller than, the Factory Method pattern from the Gang of Four book. Students mix these up constantly, so let us separate them cleanly.
- The refactoring (this page) replaces
new Thing(...)withThing.create(...)— usually one static method on one class. It is a five-minute move with immediate benefits. - The full design pattern is an architecture. There is an abstract creator class with an abstract creation method, and subclasses of the creator override that method to decide which concrete product to build. It shines in frameworks, where the framework calls the creation method and your subclass supplies the product.
Think of it like this. The refactoring is printing the thali menu in one restaurant. The pattern is a restaurant chain where every branch follows the same menu format but each branch kitchen prepares its local version — Annapurna Bhavan Pune makes the Special Thali with paneer, the Chennai branch makes it with avial, and head office only ever talks about "the Special Thali" without knowing either recipe. Do the refactoring first. If, later, you find yourself switching on a type inside the factory again and again, the full pattern (or Abstract Factory) is the natural next stop. Fowler himself notes that this simple move is the gateway toward those bigger patterns.
⚖️ Before and after at a glance
Here is the restaurant's billing software before the refactoring. Customers — I mean callers — assemble meals by hand.
// BEFORE: every caller assembles the thali at the counter
class Meal {
constructor(
public rotis: number,
public hasRice: boolean,
public hasDessert: boolean,
public price: number,
) {}
}
// Five files across the codebase do this, each slightly differently:
const lunch = new Meal(2, true, false, 120); // "veg thali"... I think?
const special = new Meal(3, true, true, 180); // hope the price is right
const broken = new Meal(0, false, false, 120); // an empty plate for 120!Nothing stops the empty plate. Nothing explains what (2, true, false, 120) means. And if the Special Thali price changes, we must hunt through every file.
After the refactoring, the menu exists.
// AFTER: named factory methods — the kitchen decides the recipe
class Meal {
private constructor(
public rotis: number,
public hasRice: boolean,
public hasDessert: boolean,
public price: number,
) {}
static vegThali(): Meal {
return new Meal(2, true, false, 120);
}
static specialThali(): Meal {
return new Meal(3, true, true, 180);
}
static miniThali(): Meal {
return new Meal(1, true, false, 80);
}
}
const lunch = Meal.vegThali(); // reads like the menu
const special = Meal.specialThali(); // price lives in ONE place
// new Meal(0, false, false, 120) -> compile error: constructor is privateThree wins at once. The call sites read like English. The recipe and price for each thali live in exactly one place. And the empty-plate bug is now impossible to write, because the constructor is private.
🪜 Step-by-step, the safe way
Like every good refactoring, we move in tiny steps and keep the program working after each one. Here is the route, following Fowler's mechanics.
Step 1: Create the factory method, delegating to the constructor. Do not change any caller yet. The new method simply wraps the old door.
class Meal {
constructor( /* ...same as before, still public... */
public rotis: number,
public hasRice: boolean,
public hasDessert: boolean,
public price: number,
) {}
// NEW: the factory just forwards for now
static vegThali(): Meal {
return new Meal(2, true, false, 120);
}
}The program compiles. Tests stay green. Nothing has moved yet — we only added a second door beside the first.
Step 2: Give the factory an intention-revealing name and the most abstract sensible return type. If callers only need an interface, return the interface. This is also the moment to decide names — vegThali() beats create1() forever.
Step 3: Find every new Meal(...) call and replace it, one at a time. Use your IDE's "find usages." After each replacement, compile and run the tests. If a call site does not match any existing factory, that is useful news — either add a new named factory or question whether that call site was correct in the first place. (Our empty-plate bug would be caught exactly here.)
Step 4: Move creation-time logic into the factory. Any validation, any subclass selection, any default-filling that callers were doing by hand now moves inside. The kitchen takes over the recipe.
Step 5: Make the constructor private (or protected, if subclasses need it). This is the lock on the old door. From now on, the named factories are the only way in.
Step 6: Run the full test suite, including a test for each named factory. Each thali deserves its own small test proving its recipe.
The migration has a clear shape — it is a small state machine, and the dangerous mistake is jumping straight from the first state to the last.
Do not make the constructor private in step 1. If you lock the old door before all callers have moved to the new one, the whole codebase breaks at once and you will fix dozens of errors in a panic. The safe order is always: add the new door, migrate callers one by one with green tests in between, and lock the old door last. Also check your frameworks first — DI containers, serializers, and ORMs often need a reachable constructor, and a suddenly-private one can fail at runtime, not compile time.
After the refactoring, the runtime flow of a single order looks like this. The customer talks only to the factory; the constructor has become an internal kitchen tool.
🏪 A bigger real-life example
Let us grow the example to something with real decision-making. Annapurna Bhavan now takes online orders, and Sunita tai wants three behaviours that a plain constructor simply cannot give.
- On festival days,
specialThali()should silently return the upgradedFestivalThalisubclass with kheer added. - The standard veg thali configuration should be shared (cached), not rebuilt for every one of the 400 lunch orders — Mohan pre-portions dal cups during the rush for exactly the same reason.
- No thali may ever be created with zero rotis — a validation rule in one place.
Here is the code that does all three. Notice how much intelligence now lives behind the named doors.
// The base class with a guarded, private constructor
class Thali {
private static vegTemplate: Thali | null = null;
protected constructor(
readonly name: string,
readonly rotis: number,
readonly items: string[],
readonly price: number,
) {
if (rotis <= 0) {
throw new Error(`A thali needs at least one roti, got ${rotis}`);
}
}
// 1) Caching: the same immutable template serves every order
static vegThali(): Thali {
if (Thali.vegTemplate === null) {
Thali.vegTemplate = new Thali(
"Veg Thali", 2, ["dal", "rice", "sabzi", "pickle"], 120,
);
}
return Thali.vegTemplate;
}
// 2) Subclass selection: the caller never knows which kind it got
static specialThali(today: Date = new Date()): Thali {
if (isFestival(today)) {
return new FestivalThali();
}
return new Thali(
"Special Thali", 3, ["dal", "rice", "paneer", "papad"], 180,
);
}
}
class FestivalThali extends Thali {
constructor() {
super("Festival Special", 3,
["dal", "rice", "paneer", "papad", "kheer"], 180);
}
}
// Callers stay blissfully simple:
const order1 = Thali.vegThali(); // cached — no new allocation
const order2 = Thali.specialThali(); // maybe FestivalThali, maybe notTry to achieve any one of these three behaviours with a raw public constructor. You cannot. A constructor must allocate a fresh object, must return its own class, and runs after the caller already committed to creating something. The factory method removes all three limits in one stroke.
The class structure now looks like a small family, with the factory methods as the only public doors.
Read the arrows carefully: OrderScreen depends only on Thali. It has no arrow to FestivalThali at all. On Diwali, hundreds of customers receive kheer without a single line of ordering code knowing the subclass exists. That invisible arrow — the one that is not in the diagram — is the whole point.
💼 The same refactoring in C#
C# gives this refactoring extra polish. Static factory methods are a beloved idiom in .NET — think of TimeSpan.FromMinutes(5), Task.FromResult(x), or Guid.NewGuid(). All of them are named factories that the standard library chose over public constructors, for exactly the reasons on this page.
public class Account
{
public string Id { get; }
public decimal Balance { get; private set; }
public decimal InterestRate { get; }
// Private constructor: the only door is a named factory
private Account(string id, decimal balance, decimal interestRate)
{
Id = id;
Balance = balance;
InterestRate = interestRate;
}
public static Account OpenSavings(string id) =>
new Account(id, balance: 0m, interestRate: 0.04m);
public static Account OpenChecking(string id) =>
new Account(id, balance: 0m, interestRate: 0m);
public static Account OpenFixedDeposit(string id, decimal amount)
{
if (amount < 1000m)
throw new ArgumentException(
"Fixed deposit needs a minimum of 1000.", nameof(amount));
return new Account(id, amount, interestRate: 0.07m);
}
}
// Call sites read like a bank form:
var savings = Account.OpenSavings("AC-101");
var fd = Account.OpenFixedDeposit("AC-102", 50_000m);Compare Account.OpenFixedDeposit("AC-102", 50000) with new Account("AC-102", 50000, 0.07m, 2). The first one a class 7 student can read. The second one even its author will misread in three months.
One honest C# warning. Some .NET tooling expects constructors: the generic constraint where T : new() requires a public parameterless constructor, many serializers (and some ORMs) construct objects reflectively, and DI containers resolve services through constructors by default. None of these are blockers — serializers can be configured, and containers accept factory delegates like services.AddSingleton(sp => Account.OpenSavings("AC-1")) — but check before you lock the door.
🐍 And a quick look in Python
Python expresses the same idea with @classmethod, and the standard library is full of examples: dict.fromkeys(...), datetime.fromtimestamp(...), int.from_bytes(...). The convention of starting factory names with from or of makes the data source obvious.
class Thali:
def __init__(self, name: str, rotis: int, price: int) -> None:
if rotis <= 0:
raise ValueError(f"A thali needs at least one roti, got {rotis}")
self.name = name
self.rotis = rotis
self.price = price
@classmethod
def veg(cls) -> "Thali":
return cls("Veg Thali", rotis=2, price=120)
@classmethod
def from_order_row(cls, row: dict) -> "Thali":
# a factory that adapts a foreign shape into our class
return cls(row["item_name"], int(row["roti_count"]), int(row["amount"]))
lunch = Thali.veg()
imported = Thali.from_order_row({"item_name": "Mini", "roti_count": "1", "amount": "80"})Python cannot truly lock a constructor — there is no private keyword — but the team convention "always create through the classmethods" plus a leading-underscore constructor argument achieves the same discipline in practice. The from_order_row factory also shows a use we have not stressed yet: factories make wonderful adapters, converting messy external data into a clean object in one guarded place.
College corner: constructors vs static factories, the famous debate. If you study Java, this whole page is Item 1 of Joshua Bloch's Effective Java: "Consider static factory methods instead of constructors." Bloch lists advantages we have met — names, caching, subclass returns — plus one more that matters in API design: a static factory can return an object of a non-public class, so a library can expose an interface and keep every implementation class completely hidden (this is how Collections.unmodifiableList works — you receive a List and have no idea, and no need to know, which class it is). The standard naming vocabulary is worth memorising: of and from for conversions (List.of, Date.from), valueOf for boxed values, getInstance when the same instance may be reused, newInstance when a fresh one is guaranteed. Bloch is also honest about the costs: classes without public constructors cannot be subclassed by outsiders, and static factories are harder to find in documentation than constructors, which IDEs list automatically. That is why disciplined naming conventions exist — they make factories discoverable again. When your professor asks "why does Integer.valueOf(127) == Integer.valueOf(127) return true but new Integer(127) == new Integer(127) returned false in old Java" — the answer is this exact page: valueOf is a factory that returns cached objects for small values, while new was forced to allocate every time.
🤔 Should this class get a factory?
Not every class deserves a menu. A tiny data holder with one obvious recipe is better off with a plain constructor — printing a menu for a shop that sells only water is bureaucracy. Here is a map for the decision.
Bottom-left — a small DTO with one shape — keep new. Bottom-right — many meaningful variations but each is a plain fresh object — factories earn their keep purely through names. Top-left — one recipe but instances must be cached or pooled — the factory exists for return control. Top-right — many recipes and special returns, like our thali menu with caching and festival subclasses — the factory method is not optional there; it is the only honest design.
And what does the win look like in numbers? Sunita tai's nephew measured the simplest possible metric: when the Special Thali recipe changed (price revision in April), how many files needed editing?
Nine files down to one. That difference is the Shotgun Surgery smell being cured in real time — and it compounds with every future change.
🛠️ IDE support
This is one of the lucky refactorings with first-class automation in major IDEs.
- IntelliJ IDEA (Java/Kotlin).
Refactor → Replace Constructor with Factory Methoddoes the whole dance for you: creates the static method, rewrites every constructor call across the project, and makes the constructor private — all in one safe, atomic operation. You choose the method name and even which class hosts the factory. - ReSharper and Rider (C#). Offer the same refactoring for .NET code, generating the static method and updating all usages.
- Visual Studio (without ReSharper). No single-click version, but the combination of "Find All References", "Change Signature", and Quick Actions makes the manual route fast and safe.
- TypeScript in VS Code. No automated refactoring exists, so follow the step-by-step section above by hand. "Find All References" on the constructor (place the cursor on the class name and search for
new ClassName) gives you the migration checklist. The compiler becomes your safety net the moment you mark the constructorprivate— every missed caller turns into a red squiggle.
Even with automation, do the naming yourself. The IDE will happily call the method create, and create says nothing. OpenSavings says everything.
⚖️ Benefits and risks
Every refactoring is a trade. Here is the honest ledger.
| Benefits | Risks / costs |
|---|---|
Named creation expresses intent — openSavings() beats overloaded constructors | Extra indirection; for a simple class with one obvious recipe, plain new is clearer |
| Can return subclasses or interface types, hiding concrete classes from callers | Some DI containers, serializers, and new() generic constraints expect a public constructor |
| Can return cached or pooled instances instead of always allocating | Caching adds shared state — cached objects must be immutable or trouble follows |
| Validation and creation rules live in one place, impossible to skip | A crowd of factory methods can make it harder to discover "how do I build one of these?" |
| Natural first step toward the full Factory Method and Abstract Factory patterns | The constructor must actually be made non-public, or callers will quietly bypass the factory |
| Call sites become testable seams — tests can read the named intent | Slightly more code: one method per named variation |
A fair rule of thumb: if you cannot name even one concrete benefit from the left column for your class, keep the constructor. The menu is for restaurants with more than one dish.
🧹 Which smells does it cure?
| Smell | How this refactoring helps |
|---|---|
| Switch Statements | A creation switch copy-pasted across callers collapses into one factory; later it can dissolve into polymorphism |
| Large Class | Tangled constructor overloads and creation flags move out into clearly named factory methods |
| Long Parameter List | Named factories with sensible defaults replace constructors taking six mysterious arguments |
| Duplicate Code | The same "assemble the object correctly" dance repeated at many call sites funnels into one place |
| Shotgun Surgery | Changing a recipe (price, defaults, subclass choice) touches one factory, not every caller |
📋 Quick revision box
+------------------------------------------------------------------+
| REPLACE CONSTRUCTOR WITH FACTORY METHOD - REVISION CARD |
+------------------------------------------------------------------+
| Problem : constructors cannot be named, cannot return another |
| type, and always allocate a fresh object |
| Solution : static factory method wraps the constructor; |
| then make the constructor private |
| |
| THE FACTORY CAN (constructor cannot): |
| - carry a meaningful name (Account.OpenSavings) |
| - return a subclass/interface (kitchen decides the thali) |
| - return a cached instance (no fresh plate every time) |
| - validate and refuse (no zero-roti thali) |
| |
| SAFE ORDER : add factory -> migrate callers one by one |
| -> lock constructor LAST |
| Remember : refactoring = one named method; |
| full GoF pattern = subclass overrides creation |
+------------------------------------------------------------------+✏️ Practice exercise
Your turn. A travel app creates tickets like this, and the call sites are full of mystery booleans.
class Ticket {
constructor(
public train: string,
public isAC: boolean,
public isTatkal: boolean,
public fare: number,
) {}
}
// scattered across the codebase:
const t1 = new Ticket("12952 Rajdhani", true, false, 3200);
const t2 = new Ticket("12952 Rajdhani", true, true, 3700); // tatkal: +500
const t3 = new Ticket("11077 Jhelum", false, true, 950); // is this fare right?Do the refactoring yourself, step by step:
- Add three static factories without touching any caller:
Ticket.acTicket(train, fare),Ticket.tatkalTicket(train, baseFare)(it should add the ₹500 tatkal charge inside the factory), andTicket.sleeperTicket(train, fare). - Migrate the three call sites one at a time, compiling after each.
- Make the constructor
privateand confirm the compiler now rejects any directnew Ticket(...). - Add a validation rule in one place: no ticket may have a fare below ₹50.
- Bonus thinking: the app now wants
Ticket.tatkalTicketto return aTatkalTicketsubclass with abookingWindowproperty. Which limit of constructors does this exercise demonstrate? Write one sentence. - Stretch goal: describe (in words, no code) what would have to change if ticket families — train, bus, and flight tickets each with AC/non-AC variants — needed creating together. Which pattern from the creational patterns family is calling you?
- Diagram practice: sketch the Figure 5 state machine for your migration of
Ticket, marking which state you are in after each of steps 1 to 3.
If you can explain to a friend why the ₹500 tatkal charge belongs inside the factory and not at the call sites — the way Arjun can now explain why the kheer decision belongs to Mohan's kitchen and not to the customer — you have understood this refactoring completely.
Frequently asked questions
- How is this refactoring different from the Factory Method design pattern?
- The refactoring is one small move — wrap a constructor call inside a named static method on the same class. The full design pattern is bigger — a hierarchy of creator classes where subclasses override a creation method to decide which product to build. The refactoring is often the first step that later grows into the pattern, but most of the time the simple static method is all you need.
- Should every constructor be replaced with a factory method?
- No. For a small class with one obvious way to build it, the plain constructor is shorter and clearer. Reach for a factory method only when you need a meaningful name, a different return type, a subclass decision, caching, or validation before creation.
- Why do we make the constructor private at the end?
- If the constructor stays public, callers can still bypass the factory and create objects the raw way. Making it private forces every creation through the single named door, so the rules inside the factory can never be skipped.
- Can a factory method return something other than a new object?
- Yes, and that is its superpower. It can return a subclass, an interface type that hides the concrete class, a cached or pooled instance instead of a fresh one, or it can refuse to create anything and fail with a clear error.
- Do factory methods cause problems with frameworks?
- Sometimes. Many DI containers, serializers, and generic constraints expect a public constructor and do not discover static factory methods automatically. Check your framework before locking the constructor down, or register the factory explicitly with the container.
Further reading
Related Lessons
Factory Method Pattern: Let the Branch Decide the Vehicle
Learn the Factory Method design pattern with a simple Indian tiffin service story, easy TypeScript and C# code, diagrams, real examples, and practice.
Abstract Factory Pattern: One Order, One Matching Thali
Understand the Abstract Factory design pattern through an Indian wedding catering story, with simple TypeScript and Python code, diagrams, and practice.
Switch Statements: The Receptionist With the Giant Rulebook
Learn the Switch Statements code smell with a school receptionist story, duplicated switch examples in TypeScript and C#, and the polymorphism cure.
Large Class: The School Bag That Carries Everything
Understand the Large Class code smell — why god classes grow, how to spot low cohesion, and how Extract Class splits them into small, focused classes.