今天我们为大家带来面向对象程序设计原则系列之 - 开闭原则
定义
开闭原则(Open/Closed Principle,OCP)是面向对象设计中最重要的原则之一,它规定:“软件实体(类、模块、函数等)应该对扩展开放,对修改封闭”。这意味着,一个实体应该能够通过扩展来满足新的需求,而不必修改其自身代码。
最早提出者
开闭原则最早是由美国计算机科学家伯特兰·迈耶(Bertrand Meyer)在1988年出版的著作《面向对象软件构造》(Object-Oriented Software Construction)中提出的。
理解
我们应该尽量通过扩展已有的代码来实现新功能,而不是修改已有的代码。这样可以降低对现有代码的影响,提高系统的可维护性和可扩展性。
扩展 VS 修改
扩展
有一点我们需要稍微理解一下,引入新的功能点,就必然会引入修改,扩展也是某种意义上的修改,只不过扩展是因为要引入一个新的逻辑,对原有代码的一点小小的改动,这是属于对原有逻辑的扩展,这是允许的。修改
原则中提到的对修改关闭,不是不允许对代码做任何的改变,指的是:- 不修改核心业务逻辑
- 不改变已有接口
- 避免修改核心算法,核心数据结构
SHOW ME THE CODE
电商场景案例
假设我们有一个电商系统,其中有不同类型的商品,比如书籍、衣服和电子产品。我们需要计算订单的总价,并且每种商品都有不同的计价方式。
首先,我们设计一个接口 Product 来表示商品,其中包含一个方法用于计算商品的价格
interface Product {
double getPrice();
}
然后,我们实现不同类型的商品类,比如书籍和衣服,注意例子中,书籍不打折,而衣服是打2折的,打折的逻辑写在了getPrice()方法里。
class Book implements Product {
private double price;
public Book(double price) {
this.price = price;
}
public double getPrice() {
return price;
}
}
class Cloth implements Product {
private double price;
public Cloth(double price) {
this.price = price;
}
public double getPrice() {
return price * 0.2; // 衣服打 2 折
}
}
现在,我们有了一个可扩展的商品系统,如果需要新增一种商品类型,只需要实现 Product 接口即可,比如增加一个Computer类实现Product接口,而不需要修改已有的代码。这符合开闭原则的要求。
但现在我们要引入一个新的功能,我们希望所有商品都能支持打折功能,且具体的折扣方案可能在将来还会变化。按照现在的代码,要支持这个功能,就需要修改具体的实现类,这显然是违反了开闭原则。
class Cloth implements Product {
public double getPrice() {
double price = this.price;
case 不同的打折活动
//618 促销
price = price * 0.5
//双十一促销
price = price * 0.1
return price;
}
}
怎么重构
为了符合开闭原则,我们可以通过引入抽象层来解耦商品和促销活动。我们可以定义一个抽象的 Discount 接口:
interface Discounter {
double applyDiscount(double price);
}
增加具体的折扣类:
class EightyPercentDiscounter implements Discounter {
public double applyDiscount(double price) {
return price * 0.8;
}
}
扩展原有产品类:
class Book implements Product {
private double price;
private Discounter discounter;
public Book(double price) {
this.price = price;
}
//扩展性修改
public void setDiscounter(Discounter discounter)
{
this.discounter = discounter;
}
public double getPrice() {
//扩展性修改
if (this.discounter == null) return price;
return discounter.applyDiscount(price);
}
}
class SalesActivity
{
Product book = new Book(1.5);
book.setDiscounter(new EightyPercentDiscounter());
book.getPrice();
}
我们从这个例子中可以看到打折逻辑放到了具体的折扣类中,当我们还需要新增其它的折扣类时,产品类已经无需修改了,重构后的代码已经符合开闭原则了。
思考点
上面的例子只展示了片断,我们可以思考一下,现在流行的电商平台他们的折扣系统是如何实现的?欢迎关注我的公众号,留言和交流。