👉文章示例代码👈
定义
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
开闭原则强调的是用抽象构建框架,用实现来扩展细节。
场景示例
笔者这里以超市中的商品做一个简单的示例来方便理解。
创建商品接口
首先创建一个商品接口,同时在接口内部定义获取商品ID、名称、价格的各个方法。一般来说,一个商品是一个实体,后续我们可以去写一个类包含商品ID、名称、价格这三个成员变量,这里仅仅为了演示开闭原则。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
public interface IGoods {
Integer getId();
String getName();
Double getPrice(); }
|
创建商品子类
我们的商品其实是有很多分类的,比如食品、用品、电器等等。
下面笔者以食品为例,创建食品类的同时实现商品接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
public class Food implements IGoods {
private Integer id;
private String name;
private Double price;
public Food(Integer id, String name, Double price) { this.id = id; this.name = name; this.price = price; }
public Integer getId() { return this.id; }
public String getName() { return this.name; }
public Double getPrice() { return this.price; } }
|
测试类及输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public class Test {
public static void main(String[] args) { IGoods iGoods = new Food(1, "巧克力", 9.9);
String msg = String.format("商品ID: %s, 商品名称: %s, 商品价格: %s", iGoods.getId(), iGoods.getName(), iGoods.getPrice());
System.out.println(msg); } }
|
测试类的输出结果如下:
商品ID: 1, 商品名称: 巧克力, 商品价格: 9.9
类结构图
以上示例类的结构图如下所示

需求变动
假设现在超市进行一系列的商品促销活动,全场食品类的商品打6折,那我们如何来实现这个需求?
方式一
针对上述的场景示例,我们想当然地会进行如下的改动,既快速又方便。
1 2 3 4 5 6 7 8 9 10 11
|
public interface IGoods { Double getDiscountPrice(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
|
public class Food implements IGoods { public Double getDiscountPrice() { return getPrice() * 0.6; } }
|
上述简单的小改动,看样子确实是满足了我们的需求,但是仔细观察你会发现,这种改动不仅修改了接口,同时也对具体的实现类也进行了改动。
问题也就来了。假设我们商品分类有很多,那么所有的商品实现类都需要去实现接口新增的方法。而接口作为一种契约,是不应该经常变化的,它应该是稳定且可靠的。
方式二
通过扩展新建一个食品的子类用来处理食品类打折,覆写食品类获取价格的方法,同时提供一个获取商品原价的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public class FoodDiscount extends Food {
public FoodDiscount(Integer id, String name, Double price) { super(id, name, price); }
public Double getOriginPrice() { return super.getPrice(); }
@Override public Double getPrice() { return this.getOriginPrice() * 0.6; } }
|
测试类及输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
public class Test {
public static void main(String[] args) { IGoods iGoods = new FoodDiscount(1, "巧克力", 9.9);
FoodDiscount foodDiscount = (FoodDiscount) iGoods;
String msg = String.format("商品ID: %s, 商品名称: %s, 商品原价: %s, 商品折后价: %s", foodDiscount.getId(), foodDiscount.getName(), foodDiscount.getOriginPrice(), foodDiscount.getPrice());
System.out.println(msg); } }
|
测试类的输出结果如下:
商品ID: 1, 商品名称: 巧克力, 商品原价: 9.9, 商品折后价: 5.94
类结构图
方式二改动后类的结构图如下所示

好处
- 不修改底层的基类和接口,防止了风险的扩散。
- 通过继承基类,使得对扩展开放,对于修改接口和基类是关闭的。
优点
提高软件系统的可复用性和可维护性
参考
- 《Head First 设计模式》
- 《大话设计模式》