一、什么是开闭原则
对修改关闭,对扩展开放。比如有一个书城,售卖书籍有原价,现在要新增一个打折价。那么,一定要保证已有功能不被修改破坏,而是把新功能扩展出来,不要影响原有功能,而是把新功能扩展出来,不要动源代码,可以自己写一个类,继承原来的类。也就是能实现原价功能,又能扩展新增的打折价格功能。
二、为什么使用开闭原则
第一:开闭原则非常有名,只要是面向对象编程,在开发时都会强调开闭原则
第二:开闭原则是最基础的设计原则,其它的五个设计原则都是开闭原则的具体形态,也就是说其它的五个设计原则是指导设计的工具和方法,而开闭原则才是其精神领袖。依照java语言的称谓,开闭原则是抽象类,而其它的五个原则是具体的实现类。
第三:开闭原则可以提高复用性
在面向对象的设计中,所有的逻辑都是从原子逻辑组合而来,不是在一个类中独立实现一个业务逻辑。只有这样的代码才可以复用,粒度越小,被复用的可能性越大。那为什么要复用呢?减少代码的重复,避免相同的逻辑分散在多个角落,减少维护人员的工作量。那怎么才能提高复用率呢?缩小逻辑粒度,直到一个逻辑不可以分为止。
第四:开闭原则可以提高维护性
一款软件量产后,维护人员的工作不仅仅对数据进行维护,还可能要对程序进行扩展,维护人员最乐意的事是扩展一个类,而不是修改一个类。让维护人员读懂原有代码,再进行修改,是一件非常痛苦的事情,不要让他在原有的代码海洋中游荡后再修改,那是对维护人员的折磨和摧残。
第五:面向对象开发的要求
万物皆对象,我们要把所有的事物抽象成对象,然后针对对象进行操作,但是万物皆发展变化,有变化就要有策略去应对,怎么快速应对呢?这就需要在设计之初考虑到所有可能变化的因素,然后留下接口,等待“可能”转变为“现实”。
三、如何使用开闭原则
第一:抽象约束
抽象是对一组事物的通用描述,没有具体的实现,也就表示它可以有非常多的可能性,可以跟随需求的变化而变化。因此,通过接口或抽象类可以约束一组可能变化的行为,并且能够实现对扩展开放,其包含三层含义:
通过接口或抽象类约束扩散,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法。
参数类型,引用对象尽量使用接口或抽象类,而不是实现类,这主要是实现里氏替换原则的一个要求
抽象层尽量保持稳定,一旦确定就不要修改
第二:元数据(metadata)控件模块行为
编程是一个很苦很累的活,那怎么才能减轻压力呢?答案是尽量使用元数据来控制程序的行为,减少重复开发。什么是元数据?用来描述环境和数据的数据,通俗的说就是配置参数,参数可以从文件中获得,也可以从数据库中获得。
第三:制定项目章程
在一个团队中,建立项目章程是非常重要的,因为章程是所有人员都必须遵守的约定,对项目来说,约定优于配置。这比通过接口或抽象类进行约束效率更高,而扩展性一点也没有减少。
第四:封装变化
对变化封装包含两层含义:
(1)将相同的变化封装到一个接口或抽象类中
(2)将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。
封装变化,也就是受保护的变化,找出预计有变化或不稳定的点,我们为这些变化点创建稳定的接口。
四、简单事例
以一个简单的书籍售卖为例,书籍有价格,名字。
书籍类:
/**
* 项目名称:OpenCloseRole
* 类 名 称:Book
* 类 描 述:TODO
* 创建时间:2021/1/4 上午10:27
* 创 建 人:wteng
*/
public class Book {
private String name;
private float price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
}
测试类为:
/**
* 项目名称:OpenCloseRole
* 类 名 称:Main
* 类 描 述:TODO
* 创建时间:2021/1/4 上午10:29
* 创 建 人:wteng
*/
public class Main {
public static void main(String[] args) {
Book book = new Book();
book.setName("水浒传");
book.setPrice(100);
System.out.println(book.getName()+"的价格为:"+book.getPrice());
}
}
但是现在想要拿到一个打折的价格,打折价格是原价的8折,此时我们如果直接对Book类的getPrice方法进行修改,那么就不符合开闭原则,也不应该在源码中新增方法。那么我们按照上边说的,应该新增一个类,来继承Book类,扩展一个方法。
扩展类:
/**
* 项目名称:OpenCloseRole
* 类 名 称:DiscountPrice
* 类 描 述:TODO
* 创建时间:2021/1/4 上午10:34
* 创 建 人:wteng
*/
public class DiscountPrice extends Book{
@Override
public double getPrice() {
return super.getPrice() * 0.8;
}
}
测试类:
/**
* 项目名称:OpenCloseRole
* 类 名 称:Main
* 类 描 述:TODO
* 创建时间:2021/1/4 上午10:29
* 创 建 人:wangteng
*/
public class Main {
public static void main(String[] args) {
Book book = new Book();
book.setName("水浒传");
book.setPrice(100);
DiscountPrice discountPrice = new DiscountPrice();
discountPrice.setPrice(book.getPrice());
System.out.println(book.getName()+"的价格为:"+book.getPrice());
System.out.println(book.getName()+"的打折价格为:"+discountPrice.getPrice());
}
}
结果为: