英雄联盟的小案例理解Java中如何实现OCP原则

案例:
英雄联盟的英雄、道具、地图,每年都会进行频繁变更
如果没有使用软件工程的开发思想,随便改其中一个道具的属性,就可能会导致非常严重的错误

要实现变更/增加英雄时,可选英雄数量和玩家开始一局游戏时选择一个英雄的操作
第一版代码 创建一个英雄,方法代表英雄拥有的技能
英雄联盟的小案例理解Java中如何实现OCP原则
文章图片

玩家输入英雄名字,代表选择该英雄
英雄联盟的小案例理解Java中如何实现OCP原则
文章图片

英雄联盟的小案例理解Java中如何实现OCP原则
文章图片

问题:随着游戏的英雄越来越多的时候,应该怎么做???
增加英雄:
英雄联盟的小案例理解Java中如何实现OCP原则
文章图片



又新增一个英雄,又要在main中变更
英雄联盟的小案例理解Java中如何实现OCP原则
文章图片


英雄联盟的小案例理解Java中如何实现OCP原则
文章图片

这个代码的问题就在于,我每次有新的英雄添加进来的时候,都是需要去修改这块代码的,不满足我们想要实现的开闭原则。我们要保证主体的方法具有一定的稳定性,不要大幅度的去修改它,一修改就会容易引起BUG
JAVA这个语言为要写出可维护的代码实现了很多的特性,这样子写出来的代码,并没有使用JAVA语言的特性,可以用其他任意的动态语言都是可以代替的
第二版代码 我们最终的目的就在于,当我们新增一个英雄的时候,尽量能够少改动main方法里的代码,让代码保证一定的稳定性

方法:使用interface写出抽象的代码
新建一个接口
英雄联盟的小案例理解Java中如何实现OCP原则
文章图片


让每个英雄都实现这个接口
英雄联盟的小案例理解Java中如何实现OCP原则
文章图片

英雄联盟的小案例理解Java中如何实现OCP原则
文章图片



面向对象的编程思想就是反复的在做两件事情:实例化一个对象、调用对象的方法
这一版代码的缺陷在于:当添加新的英雄时,依旧要在Main函数中添加Case分支,依旧无法统一对象的实例化
但是interface的优势就在于,它可以统一方法的调用。在第一版代码中,我们在每个Case中都要调用英雄的方法,而在第二版代码中,我们在创建完英雄后,统一调用方法
统一方法的调用是非常有意义的,在真实的项目中,对于一个实例方法的调用是非常多的,将实例方法的调用统一起来,意义就非常大了。第二版代码的switch代码就不再有业务逻辑,而是只有对象的创建
所以:抽象的难点就在于如何统一对象的实例化

第三版代码 我们最终的目的是:在新增一个英雄的时候,不去修改main函数里的代码
只有一段代码不负责对象实例化,代码才能保持稳定。但是对象的实例化是不可能消除的
最简单的解决方案就是,把对象实例化的过程,转移到其他的代码片段里

使用工厂模式实现分离对象实例化
新增一个工厂类,让工厂类负责对象的实例化
英雄联盟的小案例理解Java中如何实现OCP原则
文章图片

英雄联盟的小案例理解Java中如何实现OCP原则
文章图片

现在main函数的代码就相对稳定了,当我们新增英雄的时候,这块代码就不再需要改变了,这块代码实现了OCP。
但是存在两个问题:虽然这块的代码块不需要修改了,但是工厂类里面的代码块却需要修改。这边调用了HeroFactory类,如果HeroFactory类变更了,这边的代码还是需要进行改动。


对于第一个问题,难道这样做就没有意义了吗?是有意义的。代码总是会存在不稳定的,将不稳定的代码隔离起来,保证其他的代码是稳定的,也是非常有意义的。这样做就可以保证系统里面除了Factory方法的代码外,其他代码是稳定的,那么我们的目的就达到了。IOC的思想其实就是将所有不稳定的代码隔离和封装到了一块,保证其他地方的代码都是稳定的。
对于第二个问题,使用抽象工厂的设计模式就可以解决。如果我们将所有对象的实例化都写在一个Factory中,就可以看做这个代码是稳定的

第四版代码
变化是导致代码不稳定的本质原因
变化主要体现在1.用户输入的不同 2.需求变更

有没有办法直接将用户输入的字符串转换成对象?
方法:通过反射机制消除所有的变化
【英雄联盟的小案例理解Java中如何实现OCP原则】英雄联盟的小案例理解Java中如何实现OCP原则
文章图片


反射的作用就是能够帮助我们动态的创建类。

总结:
面向对象编程主要做两件事情:实例化对象和调用方法(完成业务逻辑)
只有一段代码中内有new的出现,才能保持代码的相对稳定,才能逐步实现OCP原则。即一段代码如果要保持稳定,就不应该负责对象的实例化。
想要实现OCP原则,就要面向抽象编程。

  1. 单纯interface可以统一方法的调用,但是它不能统一对象的实例化。
  2. 对象实例化是不可能消除的。代码里总会存在不稳定,隔离这些不稳定,保证其他的代码是稳定的。解决的方式就是把对象实例化的过程转移到其他的代码片段里,即使用工厂模式,将对象的实例化全部封装到工厂类中。
  3. 最后,可以使用反射机制帮助我们动态的创建类。

    推荐阅读