OCP:开放封闭原则:
软件实体(类、模块、函数)应该是可以扩展的,但是不可修改。
前言
使用OCP是意图在软件设计阶段能够应对未来可能出现的需求变更。达成一种能够应对需求变更又不影响现有代码的设计结构。
需求变更时我们要变更什么?
在面向对象语言中模块以包的形式体现,函数以类方法的形式体现。所以我们要变更的是类和使用这个类的方法。OCP不允许修改。那么只剩下新增了。(我们不讨论现在面向对象语言当中的特殊的扩展写法)
如何通过新增类来防止修改原来的类
通过继承?
继承一个类能够拥有这个类的全部属性和方法,再将需求变更所要求的修改写入,达成了OCP。
通过实现接口?
实现一个接口能够保证拥有一致的接口方法,可以定制实现细节,达成了OCP。
是的,以上两个方式都是实现OCP的方式。
关于继承类还有一个类型叫做抽象类
抽象类和接口是面向对象编程应对需求变化的两个法宝。
- 抽象类和接口都是不可以被实例化的。
- 接口只能定义抽象方法。
- 抽象类可以定义抽象方法也可以定义实现方法(子类不用再写了)。
- 只能继承一个抽象类或者类。
- 可以实现多个接口。
隔离变化
注:我们先不考虑抽象类和接口中允许出现的静态变量以及静态方法(因为这些可以当作全局变量以及全局方法)。
抽象类
不如说将变化抽象,然后使用这个抽象来代替具体的类作为参数传递。
例如:吃饭,吃是一个方法 ,饭是一个方法参数。eat(rice) 第二天不想吃饭了要吃面包 eat(bread)。方法要不要改?要改的。不想改怎么办。抽象啊。吃啥都是吃食物 eat(food) 。rice继承food,bread继承food。
甭管你是谁,让我跟你爹说话。大概是这个意思。
关于使用类的变化问题解决了,那么方法要是变了可咋整呢。
例如:动物类,狮子继承动物类有方法跑,鸽子继承动物类有方法飞。可能有人会想说不如统一方法叫做移动不就完事了嘛。
好主意 ,那么给动物类上加一个移动方法。诶?不一样了。狮子是用腿走鸽子用翅膀飞。需要在动物类中区分是鸽子还是狮子,这太扯了违反了OCP,鱼还在水里游呢总不能加一种动物改一次动物类吧。
那么动物类做成抽象类,移动变成抽象方法。狮子、鸽子、鱼啥的自己去实现移动方法。
好主意,这样使用动物类的移动方法就可以支持各种动物的变化了。
综上我们可以认为抽象类的存在,是为了将类型中统一的部分抽离,将变化的部分作为其子类存在。
接口
接口中只能定义抽象方法。实现了接口就等于确定实现类中拥有了这个方法。接口可以作为参数类型传递。
使用上面的例子。现在有一个方法要区分动物是飞翔还是走因为要使用动物的飞翔方法而不是走的方法,因为我们通过抽象类的移动方法来实现。显然我们无法分清楚到底哪些动物是用走的哪些动物是用飞的,因为参数只限制了传递进去的是动物。
接口可以实现这种需要。
我们先定义一个走的接口,再定义一个飞行的接口。狮子实现走接口,鸽子实现走和飞行接口。方法参数变成接口类型,这样传入狮子接口时编译器就会报错,保障程序正常运行。
接口是我们知道或预计一个方法有多种实现时使用(并且使用方法时需要知道是哪种实现,例如:通用的数据库接口,Mysql变成Oracle)。这时使用抽象类中的抽象方法就不太合适了,可能会导致增加实现时还要进行区分增加额外的代码修改。