一、开闭原则
1. 概念
当我们需要添加一个新的功能时,应该在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)。
开闭原则有一个很重要的特性就是:对扩展开放,对修改关闭。
在程序需要拓展的时候,不能去修改原有代码,实现一个热插拔的效果(即插即用),这样写出来的程序才能具有良好的扩展性,易于维护性。
2. 如何才能实现开闭原则呢?
想要达到这样的效果,我们需要使用接口和抽象类。
因为抽象/接口灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节可以从抽象派生来的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。
举个例子:
class Order {
private double totalAmount;
public Order(double totalAmount) {
this.totalAmount = totalAmount;
}
// 计算折扣后的金额
public double getDiscountedAmount(String discountType) {
double discountedAmount = totalAmount;
if (discountType.equals("FESTIVAL")) {
discountedAmount = totalAmount * 0.9; // 节日折扣,9折
} else if (discountType.equals("SEASONAL")) {
discountedAmount = totalAmount * 0.8; // 季节折扣,8折
}
return discountedAmount;
}
}
上述代码要完成的是电商的折扣卷计算的方法,Order 类包含一个计算折扣金额的方法,它根据不同的折扣类型应用折扣。当我们需要添加新的折扣类型时,就不得不需要修getDiscountedAmount 方法的代码,这显然是不合理的,这就违反了开闭原则。
正确遵守开闭原则的代码:
// 抽象折扣策略接口
interface DiscountStrategy {
double getDiscountedAmount(double totalAmount);
}
// 节日折扣策略
class FestivalDiscountStrategy implements DiscountStrategy {
@Override
public double getDiscountedAmount(double totalAmount) {
return totalAmount * 0.9; // 9折
}
}
// 季节折扣策略
class SeasonalDiscountStrategy implements DiscountStrategy {
@Override
public double getDiscountedAmount(double totalAmount) {
return totalAmount * 0.8; // 8折
}
}
class Order {
private double totalAmount;
private DiscountStrategy discountStrategy;
public Order(double totalAmount, DiscountStrategy discountStrategy) {
this.totalAmount = totalAmount;
this.discountStrategy = discountStrategy;
}
public void setDiscountStrategy(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
// 计算折扣后的金额
public double getDiscountedAmount() {
return discountStrategy.getDiscountedAmount(totalAmount);
}
}
3. 练习
我们应该都玩过王者荣耀吧,王者荣耀里面挣钱的就是皮肤,天美工作室给某个英雄出了一款新皮肤,如果我们是游戏开发工程师应该如何写?
/**
* 抽象皮肤类,用来定义所有皮肤的规则
*
* @author wty
*/
public abstract class AbstractSkin {
/**
* 用来显示皮肤
*/
public abstract void display();
}
/**
* 默认皮肤
*
* @author wty
*/
public class DefaultSkin extends AbstractSkin {
@Override
public void display() {
System.out.println("默认皮肤");
}
}
/**
* 新上架的皮肤
*
* @author wty
*/
public class NewSkin extends AbstractSkin {
@Override
public void display() {
System.out.println("妲己的新皮肤");
}
}
/**
* 王者荣耀游戏
*/
public class HonorOfKingsOutput {
private AbstractSkin skin;
public void setSkin(AbstractSkin skin) {
this.skin = skin;
}
public void display() {
skin.display();
}
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
HonorOfKingsOutput honorOfKingsOutput = new HonorOfKingsOutput();
honorOfKingsOutput.setSkin(new NewSkin());
honorOfKingsOutput.display();
}
}
假如我们妲己最近玩的人很多,又想出一款新的皮肤,那么我们只需要创建一个新的类去实现抽象皮肤类去实现方法即可。