Fundamentals 9 min read

Open‑Closed Principle Explained with a Hospital Billing Example

This article explains the Open‑Closed Principle of design patterns, illustrates it with Java code that models a hospital’s medicine‑selling process, compares naive and extensible implementations, and shows how to keep software open for extension while closed for modification.

Full-Stack Internet Architecture
Full-Stack Internet Architecture
Full-Stack Internet Architecture
Open‑Closed Principle Explained with a Hospital Billing Example

The Open‑Closed Principle (OCP) states that software entities should be open for extension but closed for modification. The article introduces this principle with a real‑world analogy of buying medicine in a hospital.

First, a simple implementation shows a Hospital class selling a fixed‑price Medicine to a patient:

public class OcpTest {
    public static void main(String[] args) {
        Hospital hospital = new Hospital();
        IPatient xiaoMing = new Patient("小明");
        hospital.sellMedicine(xiaoMing);
    }
}

class Medicine {
    private String name;
    private BigDecimal price;
    public Medicine(String name, BigDecimal price) {
        this.name = name;
        this.price = price;
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public BigDecimal getPrice() { return price; }
    public void setPrice(BigDecimal price) { this.price = price; }
}

class Hospital {
    private Medicine medicine = new Medicine("阿司匹林", new BigDecimal(20));
    public void sellMedicine(IPatient patient) {
        BigDecimal money = patient.pay(medicine);
        System.out.println(patient.getName() + " 花了 " + money.setScale(2, BigDecimal.ROUND_UP) + " 块钱买了药:" + medicine.getName());
    }
}

interface IPatient {
    String getName();
    BigDecimal pay(Medicine medicine);
}

class Patient implements IPatient {
    private String name;
    public Patient(String name) { this.name = name; }
    @Override
    public BigDecimal pay(Medicine medicines) { return medicines.getPrice(); }
    @Override
    public String getName() { return name; }
}

This version works but hard‑codes the price, violating OCP when new discount rules appear.

Next, the article shows a version that adjusts the price based on social‑security levels using conditional logic inside the Patient class, which still requires modifying existing code for each new level.

class Patient implements IPatient {
    private String name;
    private int level;
    public Patient(String name) { this.name = name; }
    @Override
    public BigDecimal pay(Medicine medicines) {
        if (level == 1) {
            return medicines.getPrice().multiply(new BigDecimal(0.7));
        } else if (level == 2) {
            return medicines.getPrice().multiply(new BigDecimal(0.8));
        } else if (level == 3) {
            return medicines.getPrice().multiply(new BigDecimal(0.9));
        }
        return medicines.getPrice();
    }
    @Override
    public String getName() { return name; }
}

Finally, an OCP‑compliant solution introduces separate classes for each social‑security tier, each implementing IPatient and providing its own pay method. Adding a new tier now only requires creating a new class, leaving existing code untouched.

class OneLevelSocialSecurityPatient implements IPatient {
    private String name;
    public OneLevelSocialSecurityPatient(String name) { this.name = name; }
    @Override
    public BigDecimal pay(Medicine medicine) { return medicine.getPrice().multiply(new BigDecimal(0.7)); }
    @Override
    public String getName() { return name; }
}

class TwoLevelSocialSecurityPatient implements IPatient {
    private String name;
    public TwoLevelSocialSecurityPatient(String name) { this.name = name; }
    @Override
    public BigDecimal pay(Medicine medicine) { return medicine.getPrice().multiply(new BigDecimal("0.8")); }
    @Override
    public String getName() { return name; }
}

class ThreeLevelSocialSecurityPatient implements IPatient {
    private String name;
    public ThreeLevelSocialSecurityPatient(String name) { this.name = name; }
    @Override
    public BigDecimal pay(Medicine medicine) { return medicine.getPrice().multiply(new BigDecimal("0.9")); }
    @Override
    public String getName() { return name; }
}

public static void main(String[] args) {
    Hospital hospital = new Hospital();
    IPatient xiaoMing = new Patient("小明");
    hospital.sellMedicine(xiaoMing);
    IPatient xiaoHong = new OneLevelSocialSecurityPatient("小红");
    hospital.sellMedicine(xiaoHong);
    IPatient xiaoHua = new TwoLevelSocialSecurityPatient("小花");
    hospital.sellMedicine(xiaoHua);
    IPPatient xiaoJie = new ThreeLevelSocialSecurityPatient("小杰");
    hospital.sellMedicine(xiaoJie);
}

The article concludes that the third approach respects the Open‑Closed Principle: future extensions (e.g., a fourth social‑security level) can be added without altering existing classes, demonstrating why many frameworks rely heavily on abstractions and interfaces.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Design Patternsjavasoftware designOOPOpen/Closed Principle
Full-Stack Internet Architecture
Written by

Full-Stack Internet Architecture

Introducing full-stack Internet architecture technologies centered on Java

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.