OCP开闭原则
什么是OCP OCP存在的意义是什么 它解决了什么问题
- 什么是OCP 软件、类。函数中对扩展是开放的,对修改是关闭的即开闭原则
- 简单举例 当A类所实现的业务需求发生变化时,我们不应该直接修改A类,而是应该新建一个B类来实现新的业务需求
- 那么OCP存在的意义是什么,它是为了保证软件的扩展性 可维护性,以及最重要的稳定性而诞生的概念
- 怎么实现OCP如果没有按照OCP原则进行开发会怎么样
例1:没有用OCP的代码
public class Revn {
public void Q() {
System.out.print("evn release Q");
}
public void W() {
System.out.print("evn release W");
}
public void E() {
System.out.print("evn release E");
}
public void R() {
System.out.print("evn release R");
}
}
public class Yasuo {
public void Q(){
System.out.print("yasuo release Q");
}
public void W(){
System.out.print("yasuo release W");
}
public void E(){
System.out.print("yasuo release E");
}
public void R(){
System.out.print("yasuo release R");
}
}
public class Main {
public static void main(String[] args) {
String type = Main.getPlayerInput();
switch (type) {
case "Yasuo":
Yasuo yasuo = new Yasuo();
yasuo.R();
break;
case "Revn":
Revn reven = new Revn();
reven.R();
break;
}
}
public static String getPlayerInput() {
Scanner scanner = new Scanner(System.in);
String name = scanner.nextLine();
return name;
}
}
在上述伪代码中是没有实现OCP的,因此它是不具备扩展性不具备稳定性的代码,每增加一个具体类都要修改main函数,这无疑大大增加了软件出bug的风险,是非常糟糕的代码
怎么实现OCP
- 一个业务逻辑的完成是通过对象的实例化 调用对象中方法完成的,那么类实例化与调用方法的过程就是造成 当前代码块不稳定的主要因素
- 例1在增加新类 或者修改类方法的过程中不可避免要修改main函数那么久造成了main函数的不稳定
- 但是要完成一个业务逻辑,对象的实例化过程与调用方法是不可避免的,那么就需要将这些不稳定的代码块维持在一个容器内
- 要使得当前代码块变得稳定就要抽离对象的实例化过程与方法调用的过程,例如使用工厂模式与面向抽象的编程方式
例2 使用抽象接口调用实例方法完成业务逻辑
public interface ISkill {
void Q();
void W();
void E();
void R();
}
public class Revn implements ISkill {
public void Q() {
System.out.print("evn release Q");
}
public void W() {
System.out.print("evn release W");
}
public void E() {
System.out.print("evn release E");
}
public void R() {
System.out.print("evn release R");
}
}
public class Yasuo implements ISkill {
public void Q() {
System.out.print("yasuo release Q");
}
public void W() {
System.out.print("yasuo release W");
}
public void E() {
System.out.print("yasuo release E");
}
public void R() {
System.out.print("yasuo release R");
}
}
public class Main {
public static void main(String[] args) throws Exception {
String type = Main.getPlayerInput();
ISkill iSkill;
switch (type) {
case "Yasuo":
iSkill = new Yasuo();
break;
case "Revn":
iSkill = new Revn();
break;
default:
throw new Exception();
}
iSkill.R();
}
public static String getPlayerInput() {
Scanner scanner = new Scanner(System.in);
String name = scanner.nextLine();
return name;
}
}
- 在上述代码中可以看到interface可以统一方法的调用但是不能统一对象的实例化
- 面向对象主要做两件事:实例化对象 方法调用(完成业务逻辑)
- 如果一个代码中存在实例化对象的过程,那么它是不稳定的,要保持一段代码的相对稳定它其中不能包含new关键词
例3使用简单工厂模式抽离对象实例化过程
public class Revn implements ISkill {
public void Q() {
System.out.print("evn release Q");
}
public void W() {
System.out.print("evn release W");
}
public void E() {
System.out.print("evn release E");
}
public void R() {
System.out.print("evn release R");
}
}
public class Yasuo implements ISkill {
public void Q() {
System.out.print("yasuo release Q");
}
public void W() {
System.out.print("yasuo release W");
}
public void E() {
System.out.print("yasuo release E");
}
public void R() {
System.out.print("yasuo release R");
}
}
public interface ISkill {
void Q();
void W();
void E();
void R();
}
public class HeroFactory {
public static ISkill getHero(String type) throws Exception {
ISkill iSkill;
switch (type) {
case "Yasuo":
iSkill = new Yasuo();
break;
case "Revn":
iSkill = new Revn();
break;
default:
throw new Exception();
}
return iSkill;
}
}
public class Main {
public static void main(String[] args) throws Exception {
String type = Main.getPlayerInput();
ISkill iSkill = HeroFactory.getHero(type);
iSkill.R();
}
public static String getPlayerInput() {
Scanner scanner = new Scanner(System.in);
String name = scanner.nextLine();
return name;
}
}
- 例3中使用interface统一了方法调用 使用工厂模式抽离了对象实例化过程 看似使得main函数相对稳定
- 那么代码真的变得稳定了吗?在这里HeroFactory依然是一个具体类,所以它只是相对意义上的稳定
- 软件随着业务需求的变化迭代是不可避免的,OCP也不可能实现代码的绝对稳定性,它的意义只是将代码的不稳定性控制在一定范围内
- 假设main函数我们就认为它是稳定的,我们将对象实例化过程都抽离到一个巨大的容器中,那么从软件维护的角度也就是相对的稳定了
例4:使用反射与元类消除HeroFactory的变化
public class Revn implements ISkill {
public void Q() {
System.out.print("evn release Q");
}
public void W() {
System.out.print("evn release W");
}
public void E() {
System.out.print("evn release E");
}
public void R() {
System.out.print("evn release R");
}
}
public class Yasuo implements ISkill {
public void Q() {
System.out.print("yasuo release Q");
}
public void W() {
System.out.print("yasuo release W");
}
public void E() {
System.out.print("yasuo release E");
}
public void R() {
System.out.print("yasuo release R");
}
}
public interface ISkill {
void Q();
void W();
void E();
void R();
}
public class HeroFactory {
public static ISkill getHero(String type) throws Exception {
ISkill iSkill;
String className = "com.company.awkword." + type;
Class<?> cla = Class.forName(className);
Object obj = cla.newInstance();
return (ISkill) obj;
}
}
public class Main {
public static void main(String[] args) throws Exception {
String type = Main.getPlayerInput();
ISkill iSkill = HeroFactory.getHero(type);
iSkill.R();
}
public static String getPlayerInput() {
Scanner scanner = new Scanner(System.in);
String name = scanner.nextLine();
return name;
}
}
通过简单工厂模式与接口实现了代码的相对稳定
总结:
- OCP是一种开发原则为了实现代码的可扩展性和稳定性
- 在开发生命周期较长的项目与迭代频繁的项目中我们应该遵循OCP原则
- interface只能实现方法的统一调用不能统一管理对象实例化过程
- 面向对象主要做两件事 对象实例化 对象方法的调用
- 如果一段代码想要保持稳定性,就不应该负责对象实例化的过程
- 没有绝对的稳定性,开发过程中对象实例化是不可避免的,将对象实例化过程转移到其他代码片段里实现相对的稳定性
- 稳定性要做到将不稳定代码隔离,保证其他代码是稳定的