设计原则之开闭原则

102 阅读2分钟

大家好,我是Ant,今天学习的是设计原则之一的开闭原则(Open Closed Principe, OCP)。

什么是开闭原则?

开闭原则的定义是:对修改关闭,对扩展开放。
开闭原则是最基础的设计原则。

为什么使用开闭原则呢?

我们假设一个场景:程序员小A开发了一个查询年月日的项目,别人可以直接使用他的Jar调用对应的接口查询。

有一天小A将这个接口修改了,不再返回年月日,而只返回年。那么使用的人如果升级了这个Jar之后,业务逻辑是不是就出现问题了呢?

如果小A想要一个只返回年的接口,那么他可以新增加一个,这样既不影响原业务逻辑,也扩展了新功能。

所以我们要对修改关闭,对扩展开放。

示例

假设我们有一个商品对象,商品包含了价格、名称等。

public class Goods{  
    private String name;  
    private Double price;  
  
    Goods(String name, Double price) {  
        this.name = name;  
        this.price = price;  
    }  
  
    public Double getPrice() {  
        return price;  
    }  
    public String getName() {  
        return name;  
    }  
}  

现在有了一个新业务,我们需要对商品进行打折,如果我们直接修改getPrice(), 那么我们可能再需要原价的时候就没有了入口,或者修改再修改代码,这样做系统明显不健壮,也不符合开闭原则:

public Double getPrice() {  
    return price * 0.6;  
}  

那么符合的做法是什么呢?
原接口不变,新增一个接口?

public Double getDiscountsPrice(){  
    return price * 0.6;  
}  

如果这么做的话,看起来是符合的,但我们需要去修改对应的业务逻辑,比如:

public class GoodsTest{  
    public static void main(String[] args) {  
        Goods goods = new Goods("苹果", 5.0);  
        int num = 5;  
        System.out.println("买了" + num + goods.getName() + ", " +  
            "总价:" + getTotalPrice(goods, num));  
    }  
  
/**  
* 查询购买指定数量的商品总价  
*/  
    public static Double getTotalPrice(Goods goods, int num) {  
        return goods.getPrice() * num;  
    }  
}  

这种情况下,我们就需要将每个goods.getPrice()修改成goods.getDiscountsPrice(),
上面的例子中只有一个,但如果是有很多呢?要知道就算我们不改,代码也是不会报错的,但结果却不是我们想要的了。
那么有没有更好的办法呢?
使用继承,不修改原对象,在新对象中修改价格

public class GoodsDiscounts extends Goods {  
    GoodsDiscounts(String name, Double price) {  
        super(name, price);  
    }  
  
    public Double getPrice() {  
        super.getPrice() * 0.6;  
    }  
    public Double getOriginPrice() {  
        super.getPrice();  
    }  
}  

这样我们只需要将GoodsTest中的new Goods修改即可

Goods goods = new GoodsDiscounts("打折苹果", 5.0);  

这样就万事大吉了...吗?
其实不然,这里虽然暂时符合了开闭原则,但违背了另一个原则:里氏替换原则

那么具体怎么操作让上面的代码都符合呢?我们下回分解。