1.前言
1.1 官方定义
- 开闭原则(Open Close Principle),俗称OCP原则,是Java世界里最基础的设计原则, 它指导我们如何建立一个稳定的、 灵活的系统。
- 怎么定义开闭原则呢?
定义:一个软件实体如类、 模块和函数应该对扩展开放、对修改关闭,用抽象构建框架,用实现扩展细节! - 怎么理解定义?
意思就是说,当软件需要变化时,一个软件实体应该尽量通过扩展来实现变化, 而不是通过修改已有的代码来实现变化。 - 软实体是?
项目或软件产品中按照一定的逻辑规则划分的模块、类、函数(方法)。
1.2 本文案例的业务场景
2.举例说明
2.1 正例(正在使用的业务场景)
- 如果看了上面的依赖倒置原则,这个例子可以跳过了,就是用抽象类优化后的正例。
(1)类图
(2)代码说明
(3)测试
(4)简单分析
- 可以看到目前代码如果新增饲养小羊或者小猪的话,直接新增一个猪成长过程的实体类即可,不用修改 FamilyFarm 类,这是我们上篇说的抽象不应该依赖细节,细节应该依赖抽象的依赖倒置原则,不明白的小伙伴可以先看上篇案例的讲解!
2.2 反例(在2.1上新增需求后的反例代码)
2.2.1 新增需求内容
- 但是我们现在需求升级了,不再新增饲养小羊或者🐖🐖了,请看下面需求:
- 我们想对根据宠物的年龄进行判断要不要饲养,以下是两种情况的需求
需求1:宠物年龄不超过2岁的可以饲养员会领养 需求2:狗狗的年龄不超过1岁,小猫的年龄不超过2岁
2.2.2 针对两个需求的反例代码
(1)针对需求1的
- 类图:—>到这种程度了,其实看不看都可以,代码也是一目了然的,我还是放上吧
- 代码如下:
- 测试:
- 好了,改为之后感觉代码动的也没什么问题,功能也实现了,别急,我们看完完需求2拿来一起分析
(2)针对需求2的
- 代码如下:
- 测试:
- 好了,改为之后感觉代码动的也是没什么问题,功能也实现了,我们接下来来说一说
2.2.3 代码说明
- 看完之后如果你觉得两个需求完成的感觉都还可以,朋友请加油!
- 看着不错,挺好呀,没啥问题呀,代码也不多,那问题是?先来分析:
**需求1:**我们是针对所有宠物的年龄都是1的控制,而现实是业务很少这么提需求,因为每种宠物不一样,像狗狗大家肯定喜欢从小养起,像羊呀牛呀,肯定大了好呀,为啥,所以年龄很大可能不一样,即便一样,也是前期需求,后续改动的可能性很大; 需求2:你可以理解需求2就是在需求1的基础上,业务优化了需求,而你根据需求1直接改动了代码,如果需求2不是你开发的,后续假如是一个盲从的码农,也是你的思路带着他像上面的代码那样开发,不动脑子的面向业务实现功能开发! - 问题现在是什么你看出来吗?
① 如果现在有饲养了羊呀,猪呀,牛呀,需求1实现的代码被否定了,因为前期上线的只有狗和猫,猪羊牛的需求可能没有年龄限制!你还得改动 FamilyFarm 类的代码,你看新增需求还要改动和需求没关系的已经上线的代码,我就问你烦不烦,关键是你这个改动的需求点(关于年龄的)后续可能会经常维护,这种很可能会变动的,这是重点!!! ② 如果后期猪呀羊呀牛呀兔呀……都有不同的年龄现在,你的 if else 判断打算写多长,耦合性考虑了没有,等你自己看不下去代码又不想优化的时候,你辞职拍屁股走人了,你屎一样的代码还得别人给你刷屁股!!! - 那怎么改?原则是什么?
如果你理解依赖倒置原则的话,其实是一样的道理,新增功能尽量避免动已经实现的代码,所以说是对扩展开发,对修改关闭! 你也可以这么理解,FamilyFarm 类的 raisePet方法好不容易是别人优化好的,你用了之后不满足你小小的需求而已,你就又干掉了规范,你就想想别人看你这么干什么心情吧!我是给你用的,不是给你改的,不满足你你扩展呀,修改的权力是我,NO You!! - 好了,我练习一下打字,废话说了这么多应该理解了吧!怎么改呢?往下看,其实很简单!
2.2.4 简单分析
2.3 正例(解决2.2的问题)
(1)类图
- 看完类图,你可能有点懵逼,怎么 FamilyFarm 类的方法变成 isCanBeRaised 了,先看看代码吧,估计看完代码你还笑呢,我说的是呵呵
(2)代码说明
- 再来看最后一处改动
- 你改不会想讽刺说不是不修改这个 FamilyFarm 类吗,还修改这么多?灵活什么是灵活,是尽量不修改,就拿这个需求来说,具体实现类里,有3个方法,我总不能一个一个来判断吧,所以就合了,再说了 写个FamilyFarm 的人只让你用不让你修改,你可以找他协商呀,总比你一个方法一个方法判断年龄好,那后续再有这个点上的需求变动是不是就不修改FamilyFarm 了!
- 这也可以说前期如果这么设计会更好,既然是饲养,直接考虑到饲养条件,给一个方法即可!
- 而且,公司项目框架为啥总是重构,从ssh到dubbo再到cloud,这是架构上的一个优化,能说以前的代码不好?不全是,只是不适用了而已,所以不得不动,而不是随意乱动!!
(3)测试
3.总结
- 好了,我上面说的便于理解但过于啰嗦,咱来看看官方语言的总结!
3.1 作用
- 对软件测试的影响软件遵守开闭原则的话,软件测试时只需要对扩展的代码进行测试就可以了,因为原有的测试代码仍然能够正常运行。
- 可以提高代码的可复用性粒度越小,被复用的可能性就越大;在面向对象的程序设计中,根据原子和抽象编程可以提高代码的可复用性。
- 可以提高软件的可维护性遵守开闭原则的软件,其稳定性高和延续性强,从而易于扩展和维护。
3.2 怎么使用?
- 可以通过“抽象约束”来实现开闭原则
- “封装变化”来实现开闭原则
对变化的封装包含两层含义: 第一, 将相同的变化封装到一个接口或抽象类中; 第二,将不同的变化封装到不同的接口或抽象类中, 不应该有两个不同的变化出现在同一个接口或抽象类中。 封装变化, 也就是受保护的变化(protected variations) , 找出预计有变化或不稳定的点, 我们为这些变化点创建稳定的接口, 准确地讲是封装可能发生的变化, 一旦预测到或“第六感”发觉有变化, 就可以进行封装。 - 一句话就是,通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。
- 当然,编程规范很重要,约束自己约束团队,制定项目章程
3.3 总结
- 通过接口或者抽象的方式将功能拓展出去,达到程序适应多样性的运行
- 避免修改内部代码,导致引用的部分代码功能无法使用实在运行奔溃等灾难性问题
- 编程中,ocp原则是核心,所有原则都是为了实现ocp原则!
- 好了,总结就到这里吧,最近本来很是郁闷,写起文章感觉哪有时间郁闷,看来忙起来也是一副治心病的良药,所以遛完麦兜就回来分享来了!
4.附代码
第一个例子
package com.liu.susu.principle.ocp.example1;
public class FamilyFarm {
public void raisePet(petGrowthProcess petGrowthProcess){
petGrowthProcess.petBorn();
petGrowthProcess.petGrowUp();
petGrowthProcess.petBeRaised();
}
}
abstract class petGrowthProcess{
abstract void petBorn();
abstract void petGrowUp();
abstract void petBeRaised();
}
class DogGrowthProcess extends petGrowthProcess{
public void petBorn(){
System.out.println("狗狗-->麦兜出生了……");
}
public void petGrowUp(){
System.out.println("狗狗-->麦兜长大了……");
}
public void petBeRaised(){
System.out.println("狗狗-->麦兜被饲养员带走了……");
}
}
class CatGrowthProcess extends petGrowthProcess{
public void petBorn(){
System.out.println("小猫-->阿苔出生了……");
}
public void petGrowUp(){
System.out.println("小猫-->阿苔长大了……");
}
public void petBeRaised(){
System.out.println("小猫-->阿苔被饲养员带走了……");
}
}
class ClientTest{
public static void main(String[] args) {
FamilyFarm familyFarm = new FamilyFarm();
familyFarm.raisePet(new DogGrowthProcess());
familyFarm.raisePet(new CatGrowthProcess());
}
}
第二例子
package com.liu.susu.principle.ocp.example2;
public class FamilyFarm {
public void raisePet(petGrowthProcess petGrowthProcess){
petGrowthProcess.petBorn();
petGrowthProcess.petGrowUp();
petGrowthProcess.petBeRaised();
}
public void isCanBeRaised(petGrowthProcess petGrowthProcess){
if (petGrowthProcess.age<=2){
System.out.println("小猫-->"+petGrowthProcess.name+"的年龄是"+petGrowthProcess.age
+",是饲养员最喜欢的年龄段……");
raisePet(petGrowthProcess);
}else {
System.out.println("小猫-->"+petGrowthProcess.name+"的年龄是"+petGrowthProcess.age
+",饲养员想考虑一下年龄小点的、顽皮的宠物");
}
}
}
abstract class petGrowthProcess{
String name;
int age ;
abstract void petBorn();
abstract void petGrowUp();
abstract void petBeRaised();
}
class DogGrowthProcess extends petGrowthProcess{
DogGrowthProcess(){
super.name="麦兜";
super.age=3;
}
public void petBorn(){
System.out.println("狗狗-->麦兜出生了……");
}
public void petGrowUp(){
System.out.println("狗狗-->麦兜长大了……");
}
public void petBeRaised(){
System.out.println("狗狗-->麦兜被饲养员带走了……");
}
}
class CatGrowthProcess extends petGrowthProcess{
CatGrowthProcess(){
super.name="阿苔";
super.age=1;
}
public void petBorn(){
System.out.println("小猫-->阿苔出生了……");
}
public void petGrowUp(){
System.out.println("小猫-->阿苔长大了……");
}
public void petBeRaised(){
System.out.println("小猫-->阿苔被饲养员带走了……");
}
}
class ClientTest{
public static void main(String[] args) {
FamilyFarm familyFarm = new FamilyFarm();
familyFarm.isCanBeRaised(new DogGrowthProcess());
familyFarm.isCanBeRaised(new CatGrowthProcess());
}
}
第三个例子
package com.liu.susu.principle.ocp.example3;
public class FamilyFarm {
public void raisePet(petGrowthProcess petGrowthProcess){
petGrowthProcess.petBorn();
petGrowthProcess.petGrowUp();
petGrowthProcess.petBeRaised();
}
public void isCanBeRaised(petGrowthProcess petGrowthProcess){
if(petGrowthProcess instanceof DogGrowthProcess){
if (petGrowthProcess.age<=1){
System.out.println("狗狗-->"+petGrowthProcess.name+"的年龄是"+petGrowthProcess.age+",是饲养员最喜欢的年龄段……");
raisePet(petGrowthProcess);
}else {
System.out.println("狗狗-->"+petGrowthProcess.name+"-->的年龄是"+petGrowthProcess.age+",饲养员想考虑一下年龄小点的、顽皮的宠物");
}
}else if (petGrowthProcess instanceof CatGrowthProcess){
if (petGrowthProcess.age<=2){
System.out.println("小猫-->"+petGrowthProcess.name+"的年龄是"+petGrowthProcess.age+",是饲养员最喜欢的年龄段……");
raisePet(petGrowthProcess);
}else {
System.out.println("小猫-->"+petGrowthProcess.name+"的年龄是"+petGrowthProcess.age+",饲养员想考虑一下年龄小点的、顽皮的宠物");
}
}
}
}
abstract class petGrowthProcess{
String name;
int age ;
abstract void petBorn();
abstract void petGrowUp();
abstract void petBeRaised();
}
class DogGrowthProcess extends petGrowthProcess{
DogGrowthProcess(){
super.name="麦兜";
super.age=3;
}
public void petBorn(){
System.out.println("狗狗-->麦兜出生了……");
}
public void petGrowUp(){
System.out.println("狗狗-->麦兜长大了……");
}
public void petBeRaised(){
System.out.println("狗狗-->麦兜被饲养员带走了……");
}
}
class CatGrowthProcess extends petGrowthProcess{
CatGrowthProcess(){
super.name="阿苔";
super.age=1;
}
public void petBorn(){
System.out.println("小猫-->阿苔出生了……");
}
public void petGrowUp(){
System.out.println("小猫-->阿苔长大了……");
}
public void petBeRaised(){
System.out.println("小猫-->阿苔被饲养员带走了……");
}
}
class ClientTest{
public static void main(String[] args) {
FamilyFarm familyFarm = new FamilyFarm();
familyFarm.isCanBeRaised(new DogGrowthProcess());
familyFarm.isCanBeRaised(new CatGrowthProcess());
}
}
第四个例子
package com.liu.susu.principle.ocp.example4;
public class FamilyFarm {
public void isCanBeRaised (petGrowthProcess petGrowthProcess){
petGrowthProcess.isCanBeRaised();
}
}
abstract class petGrowthProcess{
String name;
int age ;
abstract void petBorn();
abstract void petGrowUp();
abstract void petBeRaised();
abstract void isCanBeRaised();
}
class DogGrowthProcess extends petGrowthProcess{
DogGrowthProcess(){
super.name="麦兜";
super.age=3;
}
public void isCanBeRaised(){
if (this.age<=1){
System.out.println("狗狗-->"+this.name+"的年龄是"+this.age+",是饲养员最喜欢的年龄段……");
petBorn();
petGrowUp();
petBeRaised();
}else {
System.out.println("狗狗-->"+this.name+"-->的年龄是"+this.age+",饲养员想考虑一下年龄小点的、顽皮的宠物");
}
}
public void petBorn(){
System.out.println("狗狗-->麦兜出生了……");
}
public void petGrowUp(){
System.out.println("狗狗-->麦兜长大了……");
}
public void petBeRaised(){
System.out.println("狗狗-->麦兜被饲养员带走了……");
}
}
class CatGrowthProcess extends petGrowthProcess{
CatGrowthProcess(){
super.name="阿苔";
super.age=1;
}
public void isCanBeRaised(){
if (this.age<=1){
System.out.println("小猫-->"+this.name+"的年龄是"+this.age+",是饲养员最喜欢的年龄段……");
petBorn();
petGrowUp();
petBeRaised();
}else {
System.out.println("小猫-->"+this.name+"-->的年龄是"+this.age+",饲养员想考虑一下年龄小点的、顽皮的宠物");
}
}
public void petBorn(){
System.out.println("小猫-->阿苔出生了……");
}
public void petGrowUp(){
System.out.println("小猫-->阿苔长大了……");
}
public void petBeRaised(){
System.out.println("小猫-->阿苔被饲养员带走了……");
}
}
class ClientTest{
public static void main(String[] args) {
FamilyFarm familyFarm = new FamilyFarm();
familyFarm.isCanBeRaised(new DogGrowthProcess());
familyFarm.isCanBeRaised(new CatGrowthProcess());
}
}
|