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.

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

login 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.