Skip to main content
CleanCodeMastery

Pull Up Method: One Instruction Sheet for the Whole School

Learn the Pull Up Method refactoring with a school leave-application story — move methods duplicated across subclasses into the superclass, with safe steps in TypeScript and C#, IDE dialogs, and when to choose Form Template Method instead.

26 min read Updated June 11, 2026beginner
refactoringpull up methodinheritancesuperclassduplicate codetypescriptcsharp

📋 Three leave-application notices, three different rules

Back to Sunrise Public School. Remember the rule Headmistress Dr. Meena Kulkarni made in the Pull Up Field story? Shared information goes on the main office board; section boards keep only their own things. The address problem was solved, and head clerk Mr. Sharma finally had only one board to maintain. But a new problem was waiting — and this one was about instructions, not facts.

Each section's notice board carried a hand-written sheet titled "How to Apply for Leave." The 7A sheet, kept neat by class teacher Mrs. Iyer, said: write an application, get a parent's signature, submit to the class teacher one day before. The 7B sheet said almost the same — but somewhere along the way, a previous teacher of Mr. Bose's section had added "two days before". The 7C sheet, on Ms. Fernandes' sports-section board, matched 7A — except it forgot to mention the parent's signature at all.

Nobody had planned three different procedures. Years ago, one monitor wrote the first sheet, the next section copied it, the third copied the copy — and then small edits crept into each one separately. The sheets drifted, just like the address did. Facts drift; instructions drift worse, because instructions get "improved" by helpful people, one board at a time.

The trouble showed up on sports-day week. A 7C student took leave without a parent's signature — her sheet never asked for one. A 7B student, Arjun's younger cousin actually, submitted his application one day before, like his cousin in 7A — and got marked absent, because his sheet demanded two days. Same school, same rule in everyone's head, three behaviours in practice. Mr. Sharma's office spent the whole week sorting out which absences were real.

Dr. Kulkarni sighed and did the obvious thing: she wrote one "How to Apply for Leave" instruction sheet, pinned it on the main office board, and replaced all three section sheets with a single line: "Leave procedure: see the main office board." One sheet. One procedure. One place to update when the rule changes — and Mr. Sharma is its only keeper.

In code, an instruction sheet is a method — a procedure that says how something is done. When the same method body sits in several subclasses, we move it up into the superclass, so it is written once and inherited by everyone. This is Pull Up Method, and it is the headline refactoring of the whole "dealing with generalization" family.

Here is the week of a teacher who must apply the leave rule, before and after the fix:

Figure 1: A teacher's experience of keeping the leave procedure correct, before and after

📌 What is Pull Up Method?

Pull Up Method appears in Martin Fowler's Refactoring catalog with a beautifully short instruction:

When subclasses contain methods with identical bodies — or bodies that can honestly be made identical — move the method into the common superclass and delete it from each subclass.

Notice the difference from Pull Up Field. That refactoring moved information (the address). This one moves behaviour (the procedure). In real refactoring work they travel together: fields go up first, because a method cannot live in the parent while the data it touches is stuck in the children. Field is the enabler; method is the prize.

Why does duplicated behaviour appear? The same way the duplicate sheets did. A developer writes applyForLeave() in one subclass. A teammate, building the sibling subclass, copies it — copy-paste is the fastest keystroke in any editor. Or two developers, in different months, independently solve the same problem the same way, never knowing the other copy exists. Either way, the superclass stays silent about behaviour that every child performs, and you now own the classic Duplicate Code smell, hierarchy edition.

And duplicated behaviour is more dangerous than duplicated data, because behaviour carries rules. Fix a bug in one copy, and the siblings stay broken. Add a feature to one, and the others silently fall behind. Our 7B "two days before" edit is exactly this: someone improved one copy, and the other two never heard about it. In a codebase, that is the bug report that says "it works for checking accounts but not savings accounts" — same intended rule, drifted copies.

💡

One-line summary: Pull Up Method moves a method that several subclasses duplicate into their superclass, so the behaviour is written once and inherited everywhere — like replacing three drifting section instruction sheets with one school-wide sheet on the office board.

A pulled-up method also documents the hierarchy. When applyForLeave() sits in Section, the parent class openly declares: "every section in this school can do this, and does it this way." A reader no longer needs to open three subclass files and compare line by line to discover the shared contract. The shape of the code finally matches the shape of the truth.

College corner: Pull Up Method is DRY applied to behaviour, but with a stricter bar than for fields. A field has one job — hold a value — so "same meaning" is easy to verify. A method encodes logic, and logic has edge cases: an >= versus a >, an exception type a caller catches, an ordering of side effects. Two method bodies are only "the same knowledge" if they agree on all of these. This is why the safe recipe below insists on side-by-side reading and characterization tests before any merge — DRY removes redundant representations of one truth, never two different truths that happen to rhyme.

The whole landscape of moves, one more time, with this post's hero in bold position at the top of the pull-up branch:

Figure 2: Where Pull Up Method sits in the family of hierarchy moves

🔍 When do we need it?

Look for these signals:

  • Two or more subclasses define methods with the same body. Same statements, same logic, maybe different whitespace. This is textbook Duplicate Code — and Fowler calls duplication the number-one smell for a reason.
  • The bodies are almost the same. This is the more common, and more interesting, case. Compare the copies carefully. Often the differences are accidents — a forgotten update, a typo, a stale constant (our 7C sheet missing the signature line). Unifying them fixes a real bug. But if a difference is intentional, do not bulldoze it; we will see the right tool for that case below.
  • A bug fix had to be applied in several classes. If your last commit message was "fix validation in CheckingAccount AND SavingsAccount", the method is asking to live upstairs.
  • The same method name appears across siblings with the same purpose. Even before comparing bodies, identical names across siblings are worth investigating.

And the signals that say stop:

  • Only some subclasses share the method. If a behaviour belongs to 7C alone, hoisting it to Section forces 7A and 7B to inherit something irrelevant — that is Refused Bequest being created in real time. The inverse refactoring, Push Down Method, exists for moving a too-general parent method back down, exactly as Push Down Field does for data.
  • The bodies differ in intentional, meaningful ways. Merging them would silently change behaviour — and a refactoring must never change behaviour. If the copies share a skeleton but differ in a step or two, the right move is Form Template Method: pull up the skeleton, leave the differing steps as abstract methods.
  • The method depends on subclass-only members. It will not compile in the parent. First apply Pull Up Field to the data it reads, or declare abstract methods for the parts the parent cannot know.

Plotting our applyForLeave() method on the placement map shows why it is a textbook pull-up candidate:

Figure 3: applyForLeave is used by every section but lives in the subclasses — pull it up

👀 Before and after at a glance

Here is the leave procedure in code. Each section class carries its own copy, and the copies have drifted exactly like the paper sheets:

// BEFORE: three copies of "the same" procedure, already drifting
abstract class Section {
  protected leaveRegister: string[] = [];
}
 
class SectionSevenA extends Section {
  applyForLeave(student: string, days: number): void {
    if (days < 1) throw new Error("Leave must be at least 1 day");
    this.leaveRegister.push(`${student}: ${days} day(s), parent signed`);
  }
}
 
class SectionSevenB extends Section {
  applyForLeave(student: string, days: number): void {
    if (days < 1) throw new Error("Leave must be at least 1 day");
    // someone added an extra rule here, only here:
    if (days > 10) throw new Error("Long leave needs principal approval");
    this.leaveRegister.push(`${student}: ${days} day(s), parent signed`);
  }
}
 
class SectionSevenC extends Section {
  applyForLeave(student: string, days: number): void {
    if (days < 1) throw new Error("Leave must be at least 1 day");
    this.leaveRegister.push(`${student}: ${days} day(s)`); // forgot the signature!
  }
}

Study the drift. 7B gained a sensible rule (long leave needs approval) that the others never received. 7C lost the signature note. Neither difference was a decision — both were accidents of copy-paste maintenance. The school's real rule is one procedure, with the long-leave check for everyone.

After Pull Up Method:

// AFTER: one procedure, written once, inherited by all sections
abstract class Section {
  protected leaveRegister: string[] = [];
 
  applyForLeave(student: string, days: number): void {
    if (days < 1) throw new Error("Leave must be at least 1 day");
    if (days > 10) throw new Error("Long leave needs principal approval");
    this.leaveRegister.push(`${student}: ${days} day(s), parent signed`);
  }
}
 
class SectionSevenA extends Section {} // inherits applyForLeave
class SectionSevenB extends Section {} // inherits applyForLeave
class SectionSevenC extends Section {} // inherits applyForLeave

Three drifting copies became one authoritative procedure. 7B's good idea now protects every section; 7C's missing signature is restored. The next rule change will be one edit.

Figure 4: Before — each section class defines its own drifted copy of applyForLeave
Figure 5: After Pull Up Method — the method is defined once in the superclass and inherited by all

Counting the duplicated procedure copies before the move tells the same story as the field post — the problem is spread evenly, so no single section "owns" it, which is exactly why nobody fixes it:

Figure 6: Before the refactoring, each section carries one of the three drifting method copies

🪜 Step-by-step, the safe way

Pull Up Method needs a little more care than Pull Up Field, because behaviour can hide subtle differences that data cannot. The bird's-eye view first:

Figure 7: Three drifting instruction sheets converge into one office-board procedure

Now the recipe in detail.

  1. Read every copy, line by line, side by side. Do not skim. Open the copies in split view and compare. Your goal is a verdict for each difference: accidental (drift, bug, stale code) or intentional (a real per-subclass rule). In our example, 7B's long-leave check was a school-wide rule that never spread, and 7C's missing signature was a bug — both accidental, so unification is safe and even desirable.

  2. Check the method's dependencies. What fields and methods does the body touch? Everything it uses must be visible from the superclass. applyForLeave uses leaveRegister, which already lives in Section — good. If it used a subclass-only field, you would pause here and apply Pull Up Field first; if it called a subclass-only method, you would declare that method abstract on the parent.

  3. Make the copies textually identical first, inside the subclasses. Apply the agreed unification to each copy where it stands, running tests after each class. This is the intermediate state:

    // INTERMEDIATE: all three copies now identical, still in subclasses
    class SectionSevenC extends Section {
      applyForLeave(student: string, days: number): void {
        if (days < 1) throw new Error("Leave must be at least 1 day");
        if (days > 10) throw new Error("Long leave needs principal approval");
        this.leaveRegister.push(`${student}: ${days} day(s), parent signed`);
      }
    }

    Why bother with this step? Because it separates two risky things — changing behaviour and moving behaviour — into different commits. If a test breaks now, you know the unification was wrong, not the move.

  4. Copy the method into the superclass. Take one subclass's (now-identical) body and create the method on Section. The subclasses still have their copies; they simply override the parent with identical text. Compile and test — everything should be green.

  5. Delete the method from one subclass. Compile. Test. That subclass now inherits the parent's version. Behaviour is unchanged, so tests stay green.

  6. Repeat for each remaining subclass, one at a time. Delete from 7B, test. Delete from 7C, test. Three small, reversible steps instead of one big leap.

  7. If a copy refuses to unify, change strategy — do not force it. A stubborn intentional difference means you want Form Template Method: keep the shared skeleton in the parent and express the differing step as an abstract method each subclass implements its own way.

The method's life through this recipe is a small, strict state machine — note that unified sits between duplicated and hoisted, and tests guard both transitions:

Figure 8: The method's lifecycle — copies are unified in place before anything moves
⚠️

Tests are your safety rope here even more than in Pull Up Field, because method bodies can differ in ways your eyes miss — an >= versus a >, a reordered condition, a different error message a caller depends on. Run the suite after every unification edit and after every deletion. If your subclass methods have no tests at all, write a few characterization tests before touching anything — capture today's behaviour first, then refactor against that safety net.

After the move, the runtime picture is pleasantly boring. A call on any section object finds the one inherited implementation in the base — there is no copy left to choose between:

Figure 9: After the pull-up, every section dispatches to the single inherited method

🏗️ A bigger real-life example

Sections do not just process leave; they also prepare a monthly report for Mr. Sharma's office. Before refactoring, each section class has its own report method — and this example shows the interesting case, where most of the body is shared but one line genuinely differs per section:

// BEFORE: 90% shared report logic, copied into every section
abstract class Section {
  protected schoolAddress = "12 Lake Road, Pune 411001"; // pulled up earlier!
  protected studentCount = 0;
  protected absencesThisMonth = 0;
}
 
class SectionSevenA extends Section {
  monthlyReport(month: string): string {
    const header = `Sunrise Public School — ${this.schoolAddress}`;
    const attendance = 100 - (this.absencesThisMonth / this.studentCount) * 100;
    const special = "Math Olympiad practice: Tuesdays";   // differs per section
    return `${header}\nClass 7A — ${month}\nAttendance: ${attendance.toFixed(1)}%\n${special}`;
  }
}
 
class SectionSevenB extends Section {
  monthlyReport(month: string): string {
    const header = `Sunrise Public School — ${this.schoolAddress}`;
    const attendance = 100 - (this.absencesThisMonth / this.studentCount) * 100;
    const special = "Science club: Wednesdays";           // differs per section
    return `${header}\nClass 7B — ${month}\nAttendance: ${attendance.toFixed(1)}%\n${special}`;
  }
}
 
class SectionSevenC extends Section {
  monthlyReport(month: string): string {
    const header = `Sunrise Public School — ${this.schoolAddress}`;
    const attendance = 100 - (this.absencesThisMonth / this.studentCount) * 100;
    const special = "Swimming practice: Mon/Thu";         // differs per section
    return `${header}\nClass 7C — ${month}\nAttendance: ${attendance.toFixed(1)}%\n${special}`;
  }
}

Can we pull this up? The bodies are not identical — the section name and the special-activity line differ. But look at the shape: the skeleton is the same, and the differences are small, well-contained, and intentional. So we pull up the skeleton and leave the differences as abstract methods:

// AFTER: skeleton upstairs, genuine differences downstairs
abstract class Section {
  protected schoolAddress = "12 Lake Road, Pune 411001";
  protected studentCount = 0;
  protected absencesThisMonth = 0;
 
  monthlyReport(month: string): string {
    const header = `Sunrise Public School — ${this.schoolAddress}`;
    const attendance = 100 - (this.absencesThisMonth / this.studentCount) * 100;
    return `${header}\n${this.sectionName()}${month}\n` +
           `Attendance: ${attendance.toFixed(1)}%\n${this.specialActivity()}`;
  }
 
  protected abstract sectionName(): string;
  protected abstract specialActivity(): string;
}
 
class SectionSevenA extends Section {
  protected sectionName() { return "Class 7A"; }
  protected specialActivity() { return "Math Olympiad practice: Tuesdays"; }
}
 
class SectionSevenB extends Section {
  protected sectionName() { return "Class 7B"; }
  protected specialActivity() { return "Science club: Wednesdays"; }
}
 
class SectionSevenC extends Section {
  protected sectionName() { return "Class 7C"; }
  protected specialActivity() { return "Swimming practice: Mon/Thu"; }
}

This is Pull Up Method shading into Form Template Method, and the school analogy still works perfectly: the office sheet now says "Every section submits a monthly report in this format; fill in your section's name and special activity in the blanks." The procedure is shared; the blanks are local. Notice also how this builds on the previous post — schoolAddress had to be pulled up before this report method could move, which is why field comes before method in this series.

College corner: when a pulled-up method calls overridable steps, the Liskov Substitution Principle becomes a contract on the overrides. The skeleton in Section.monthlyReport() promises callers a report in a fixed format; each subclass's specialActivity() must honour the implicit expectations of that skeleton (return a printable line, not throw for normal sections, not secretly mutate the register). An override that strengthens preconditions or weakens postconditions breaks substitutability even though the code compiles. Template Method is therefore not just a duplication tool — it is a contract-splitting tool: the parent owns the invariant part of the contract, and each child owns only the variation points, which are small enough to reason about.

How do you decide between a plain pull-up and the template form? Use this little decision path:

Figure 10: Identical bodies move up whole; skeleton-plus-blanks becomes a template; truly different bodies stay down

And the maintenance arithmetic, in one chart. A leave-rule change before the refactoring meant editing three classes and hoping nobody missed one; afterwards it means editing one:

Figure 11: Classes to edit when the leave rule changes, before and after the pull-up

🐍 The same idea in Python

Python's version of the template-style result is short and very readable — NotImplementedError (or abc.abstractmethod) plays the role of the abstract blank:

# AFTER, in Python: skeleton upstairs, blanks downstairs
class Section:
    school_address = "12 Lake Road, Pune 411001"
 
    def apply_for_leave(self, student: str, days: int) -> None:
        if days < 1:
            raise ValueError("Leave must be at least 1 day")
        if days > 10:
            raise ValueError("Long leave needs principal approval")
        self.leave_register.append(f"{student}: {days} day(s), parent signed")
 
    def monthly_report(self, month: str) -> str:
        return (f"Sunrise Public School — {self.school_address}\n"
                f"{self.section_name()}{month}\n{self.special_activity()}")
 
    def section_name(self) -> str:
        raise NotImplementedError
 
    def special_activity(self) -> str:
        raise NotImplementedError
 
 
class SectionSevenC(Section):
    def __init__(self):
        self.leave_register: list[str] = []
 
    def section_name(self) -> str:
        return "Class 7C"
 
    def special_activity(self) -> str:
        return "Swimming practice: Mon/Thu"

Python makes the inheritance lookup visible: calling seven_c.apply_for_leave(...) finds no such method on SectionSevenC, walks up the MRO (method resolution order) to Section, and runs the one shared body. The walk up the class chain is the student walking downstairs to read the office board.

💼 The same refactoring in C#

C# spells inheritance with : and overridable steps with virtual/abstract/override, but the moves are identical. Before:

// BEFORE: duplicated method in each section class
abstract class Section
{
    protected List<string> _leaveRegister = new();
}
 
class SectionSevenA : Section
{
    public void ApplyForLeave(string student, int days)
    {
        if (days < 1) throw new ArgumentException("Leave must be at least 1 day");
        _leaveRegister.Add($"{student}: {days} day(s), parent signed");
    }
}
 
class SectionSevenB : Section
{
    public void ApplyForLeave(string student, int days)
    {
        if (days < 1) throw new ArgumentException("Leave must be at least 1 day");
        if (days > 10) throw new ArgumentException("Long leave needs principal approval");
        _leaveRegister.Add($"{student}: {days} day(s), parent signed");
    }
}

After unifying and pulling up — including the template-style report with abstract steps:

// AFTER: shared behaviour in the base class, blanks left abstract
abstract class Section
{
    protected List<string> _leaveRegister = new();
    protected string _schoolAddress = "12 Lake Road, Pune 411001";
 
    public void ApplyForLeave(string student, int days)
    {
        if (days < 1) throw new ArgumentException("Leave must be at least 1 day");
        if (days > 10) throw new ArgumentException("Long leave needs principal approval");
        _leaveRegister.Add($"{student}: {days} day(s), parent signed");
    }
 
    public string MonthlyReport(string month) =>
        $"Sunrise Public School — {_schoolAddress}\n" +
        $"{SectionName}{month}\n{SpecialActivity}";
 
    protected abstract string SectionName { get; }
    protected abstract string SpecialActivity { get; }
}
 
class SectionSevenC : Section
{
    protected override string SectionName => "Class 7C";
    protected override string SpecialActivity => "Swimming practice: Mon/Thu";
}

C#-specific notes worth remembering:

  • The members the pulled-up method uses must be at least protected in the base class — same visibility rule as Pull Up Field.
  • If subclasses still need to extend (not replace) the shared behaviour, mark the base method virtual and have overrides call base.ApplyForLeave(...) before adding their extra work. Prefer the abstract-step style above, though — "call the base or things break silently" is a fragile contract.
  • If the duplicated logic lives in constructors rather than ordinary methods, plain Pull Up Method cannot help, because constructors are not inherited. That special case has its own refactoring — Pull Up Constructor Body, the next post.

🛠️ IDE support

This refactoring is so common that every serious IDE automates it:

  • IntelliJ IDEA, Rider, PhpStorm (JetBrains family): Refactor → Pull Members Up… opens a dialog listing the class's members with checkboxes. Tick the method, choose the target superclass, and the IDE moves it and deletes the subclass copy. JetBrains' own documentation pitches the feature for exactly our situation: subclasses whose methods perform similar work, where pulling up "helps you to get rid of duplicate code." The dialog can also pull a method up as abstract — handy for the template-style result.
  • Visual Studio (C#): put the cursor on the method, press Ctrl+., choose Pull members up to base type, and pick the destination in the dialog. If no base class exists yet, "Pull member(s) up to new base class" creates one via the Extract Base Class dialog.
  • ReSharper: Refactor → Pull Members Up, with conflict analysis that warns when the method depends on members the base type cannot see — the tool literally checks our step 2 for you.
  • Eclipse (Java): Refactor → Pull Up…, which can also flag the sibling copies for deletion in the same pass.

The honest caution from the last post applies double here: the IDE moves text; it does not judge behaviour. It will not notice that 7B's copy had an extra rule the others lacked. The side-by-side reading in step 1, and the decision "accident or intention?", remain your job. Tools move methods; humans decide meaning.

⚖️ Benefits and risks

BenefitsRisks / costs
One definition, one place to fix — a bug fix lands in every subclass at onceForcing a merge of intentionally different bodies silently changes behaviour
The superclass openly documents the shared contract: "all my children do this"The pulled-up method may need its dependencies hoisted too, growing the change
Total code shrinks; the hierarchy's surface area dropsSubclasses lose the freedom to drift — usually good, occasionally constraining
Drift bugs (like 7C's missing signature) get found and fixed during unificationIf only some subclasses want the behaviour, you create Refused Bequest
Opens the door to Form Template Method when bodies share a skeletonBigger review diff than Pull Up Field — keep steps small and commits separate

The direction compass again, because Pull Up Method and Push Down Method are exact inverses, just like their field cousins. Here is the full decision table:

SituationRight moveWhy
Every subclass performs the behaviour identicallyPull Up MethodOne sheet on the office board
Every subclass performs it with small intentional blanksPull up skeleton + abstract steps (Form Template Method)Shared procedure, local blanks
Only one or two subclasses perform itPush Down MethodA parent method only 7C calls misleads every reader of the parent
Several-but-not-all subclasses share itConsider an intermediate parent, or compositionThe subset may be a real concept of its own

A hierarchy is healthy when each behaviour sits at the lowest level where everyone below still needs it — no lower (duplication), no higher (refused bequest). Pull up and push down are the two hands that keep nudging each member to that level.

🩺 Which smells does it cure?

SmellHow Pull Up Method helps
Duplicate CodeThe primary cure — N copies of a behaviour collapse into one definition
Shotgun SurgeryRule changes now touch one method instead of every sibling class
Long Class / bloated subclassesEach subclass sheds the copied bulk and keeps only what is truly its own
Refused BequestIndirect: applying the direction compass correctly (and knowing when to push down instead) prevents planting this smell

📦 Quick revision box

+--------------------------------------------------------------+
|                  PULL UP METHOD — REVISION                   |
+--------------------------------------------------------------+
| Story    : 3 sections, 3 drifting "How to Apply for Leave"   |
|            sheets -> wrong absences, missed signatures.      |
|            Fix: ONE school-wide sheet on the office board.   |
|                                                              |
| Move     : Method duplicated in subclasses ---> superclass   |
|                                                              |
| Do it when : bodies are identical, or differences are        |
|              ACCIDENTS you can safely unify                  |
| Don't when : differences are INTENTIONAL (-> Form Template   |
|              Method) or only SOME subclasses need it         |
|              (-> Push Down Method)                           |
|                                                              |
| Safe steps : read copies side by side -> hoist dependencies  |
|              (Pull Up Field first!) -> make copies identical |
|              IN PLACE -> copy body to parent -> delete from  |
|              subclasses ONE AT A TIME, testing every step    |
|                                                              |
| Inverse    : Push Down Method                                |
| Friends    : Pull Up Field (enabler), Pull Up Constructor    |
|              Body (for constructors), Form Template Method   |
| Cures      : Duplicate Code, Shotgun Surgery                 |
+--------------------------------------------------------------+

✏️ Practice exercise

Your turn to be the Headmistress. The school's fee software has this hierarchy:

abstract class FeeAccount {
  protected balanceDue = 0;
}
 
class TuitionAccount extends FeeAccount {
  recordPayment(amount: number): string {
    if (amount <= 0) throw new Error("Payment must be positive");
    this.balanceDue -= amount;
    return `Receipt: Rs.${amount} received. Balance: Rs.${this.balanceDue}`;
  }
}
 
class TransportAccount extends FeeAccount {
  recordPayment(amount: number): string {
    if (amount <= 0) throw new Error("Payment must be positive");
    this.balanceDue -= amount;
    return `Receipt: Rs.${amount} received towards transport. Balance: Rs.${this.balanceDue}`;
  }
}
 
class HostelAccount extends FeeAccount {
  recordPayment(amount: number): string {
    if (amount < 0) throw new Error("Payment must be positive"); // look closely!
    this.balanceDue -= amount;
    return `Receipt: Rs.${amount} received towards hostel. Balance: Rs.${this.balanceDue}`;
  }
}

Your tasks:

  1. Compare the three bodies side by side. List every difference you find. (There are two kinds hiding here.)
  2. HostelAccount uses < where the others use <=. Accident or intention? What does each choice allow? Decide what the school's real rule should be, and write a characterization test for it before changing anything.
  3. The receipt wording differs per account ("towards transport", "towards hostel"). Is this an accidental drift or an intentional blank? Choose between a full pull-up and a skeleton-plus-abstract-step design, and justify your choice in one sentence.
  4. Perform the refactoring in the safe order: unify in place (tests), copy to FeeAccount (tests), delete from one subclass at a time (tests each time).
  5. Bonus: a new LibraryFineAccount subclass arrives next term. Count how many lines it needs in your refactored design versus the original. That difference is the dividend this refactoring pays every future developer.

When the duplicated procedure lives in one place and each subclass keeps only its honest blanks, you have mastered Pull Up Method. Next, we tackle the one place this technique cannot reach directly — constructors — with Pull Up Constructor Body.

Frequently asked questions

What does Pull Up Method do in one sentence?
When two or more subclasses contain methods with identical (or unifiable) bodies, Pull Up Method moves that method into the common superclass and deletes the subclass copies, so the behaviour is defined exactly once and inherited by all.
What if the method bodies are similar but not exactly identical?
First check whether the differences are accidental — often one copy is simply buggy or outdated, and unifying them fixes a real defect. If the differences are intentional, do not force a merge. Pull up the shared skeleton and leave the differing steps as abstract or overridable methods — that is the Form Template Method refactoring.
The method I want to pull up uses a field that only exists in the subclasses. What now?
Move the dependencies first. Apply Pull Up Field so the data the method needs lives in the superclass, or declare abstract methods on the parent that subclasses implement. Only when everything the method touches is visible from the parent can the method itself move up.
Can a pulled-up method still behave differently per subclass?
Yes. The shared body lives in the superclass, and any step that must vary is expressed as a call to an abstract or virtual method that each subclass overrides. The common part is written once; only the genuinely different part stays downstairs.
When is Push Down Method the right move instead?
When a method sits in the superclass but only one or two subclasses actually use it. Keeping it up forces all other children to inherit behaviour that is irrelevant to them — the Refused Bequest smell. Push Down Method moves it into exactly the subclasses that need it; it is the precise inverse of Pull Up Method.

Further reading

Related Lessons