开闭原则(OCP)Open Closed Principle
对于软件、函数和类而言,修改时封闭的,扩展是开放的。
例如:上一节讲到的接口,为什么要创建一个v1和v2版本,因为修改时封闭的,扩展时开放的;例如面向对象中,类的封装和扩展
IOC 【控制反转】和DI【依赖注入】 的目的也是为了实现开闭原则。
面向抽象编程可以实现OCP,为了编写可维护代码
1、interface【接口】、abstract【抽象类】,多态性。
2、设计模式:工厂模式
3、IOC和DI
面向对象:1、实例化对象 2、调用方法(完成业务逻辑)
引入英雄联盟英雄小案例
菜单
demo1:面向对象编程,缺点明显,笨重。
####创建package:akwward.hero,在下面分别创建3个类
####类1:Camille
package com.company.awkward.hero;
public class Camille {
public void q(){
System.out.println("Camille Q");
}
public void w(){
System.out.println("Camille W");
}
}
####类2:Diana
package com.company.awkward.hero;
public class Fox {
public void q(){
System.out.println("Fox Q");
}
public void w(){
System.out.println("Fox W");
}
}
####类3:Fox
package com.company.awkward.hero;
public class Fox {
public void q(){
System.out.println("Fox Q");
}
public void w(){
System.out.println("Fox W");
}
}
####main的方法
package com.company;
import com.company.awkward.hero.Camille;
import com.company.awkward.hero.Diana;
import com.company.awkward.hero.Fox;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// write your code here
String HeroName = Main.getPlayerInput();
switch (HeroName){
case "Diana":
Diana diana = new Diana();
diana.q();
break;
case "Camille":
Camille camille = new Camille();
camille.q();
break;
case "Fox":
Fox fox = new Fox();
fox.q();
break;
}
}
private static String getPlayerInput(){
System.out.println("Enter a Hero's Name:");
Scanner scanner = new Scanner(System.in);
return scanner.nextLine();
}
}
改造思路V2:引入interface
例子说明:
demo2:单纯interface可以统一方法的调用,但它不能统一对象的实例化。
例如对比demo1,把方法的调用抽离了。
####创建package:abstraction.hero,在下面分别创建3个类
####创建一个interface在package:abstraction
###ISkill
package com.company.abstraction;
public interface ISkill {
void q();
void w();
}
###类1:CamilleImpl
package com.company.abstraction.hero;
import com.company.abstraction.ISkill;
public class CamilleImpl implements ISkill {
public void q(){ System.out.println("Camille Q"); }
public void w(){ System.out.println("Camille W"); }
}
###类2:CamilleImpl
package com.company.abstraction.hero;
import com.company.abstraction.ISkill;
public class DianaImpl implements ISkill {
public void q(){ System.out.println("Diana Q"); }
public void w(){ System.out.println("Diana W"); }
}
###类3:FoxImpl
package com.company.abstraction.hero;
import com.company.abstraction.ISkill;
/* 类实现接口 规范化类的名字后面追加Impl*/
public class FoxImpl implements ISkill {
public void q(){ System.out.println("Fox Q"); }
public void w(){ System.out.println("Fox W"); }
}
###Main
package com.company;
import com.company.abstraction.ISkill;
import com.company.abstraction.hero.CamilleImpl;
import com.company.abstraction.hero.DianaImpl;
import com.company.abstraction.hero.FoxImpl;
import com.company.awkward.hero.Camille;
import com.company.awkward.hero.Diana;
import com.company.awkward.hero.Fox;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
String HeroName = Main.getPlayerInput();
ISkill iSkill;
switch (HeroName){
case "Diana":
iSkill = new DianaImpl();
break;
case "Camille":
iSkill = new CamilleImpl();
break;
case "Fox":
iSkill = new FoxImpl();
break;
default:
throw new IllegalStateException("Unexpected value: " + HeroName);
}
iSkill.q();
}
private static String getPlayerInput(){
System.out.println("Enter a Hero's Name:");
Scanner scanner = new Scanner(System.in);
return scanner.nextLine();
}
}
改造思路V3:如果代码中没有new实例化的出现,就能保持代码相对稳定,逐步实现OCP
如果一段代码要保持稳定,就不应该负责对象的实例化。由于对象实例化不能取消,只能转移实例化。
demo3:在interface基础上引入工厂模式
工厂模式分三种
- 简单工厂模式
- 工厂模式
- 抽象工厂模式
####创建package:factory.hero,在下面分别创建3个类
####创建一个interface在package:factory
###ISkill
package com.company.factory;
public interface ISkill {
void q();
void w();
}
###类1:CamilleImpl
package com.company.factory.hero;
import com.company.factory.ISkill;
public class CamilleImpl implements ISkill {
public void q(){ System.out.println("Camille Q"); }
public void w(){ System.out.println("Camille W"); }
}
###类2:CamilleImpl
package com.company.factory.hero;
import com.company.factory.ISkill;
public class DianaImpl implements ISkill {
public void q(){ System.out.println("Diana Q"); }
public void w(){ System.out.println("Diana W"); }
}
###类3:FoxImpl
package com.company.factory.hero;
import com.company.factory.ISkill;
/* 类实现接口 规范化类的名字后面追加Impl*/
public class FoxImpl implements ISkill {
public void q(){ System.out.println("Fox Q"); }
public void w(){ System.out.println("Fox W"); }
}
####创建一个类在package:factory,HeroFactory
####HeroFactory
package com.company.factory.hero;
import com.company.factory.ISkill;
public class HeroFactory {
public static ISkill getHero(String HeroName){
ISkill iSkill;
switch (HeroName){
case "Diana":
iSkill = new DianaImpl();
break;
case "Camille":
iSkill = new CamilleImpl();
break;
case "Fox":
iSkill = new FoxImpl();
break;
default:
throw new IllegalStateException("Unexpected value: " + HeroName);
}
return iSkill;
}
}
###Main
package com.company;
import com.company.factory.ISkill;
import com.company.factory.hero.CamilleImpl;
import com.company.abstraction.hero.DianaImpl;
import com.company.abstraction.hero.FoxImpl;
import com.company.factory.hero.HeroFactory;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
String HeroName = Main.getPlayerInput();
/* import ISkill 的目录要改成 factory*/
ISkill iSkill = HeroFactory.getHero(HeroName);
iSkill.q();
}
private static String getPlayerInput(){
System.out.println("Enter a Hero's Name:");
Scanner scanner = new Scanner(System.in);
return scanner.nextLine();
}
}
本次改进,使用了简单工厂模式,对比起demo2,把main方法中的实例化迁移至HeroFactory。此时的main方法就不受类的增多或改变而影响。
不过此方法仍然不是最佳,因为在HeroFactory依然需要实例化。如何改进呢?
domo4:使用反射,用户属于名字就可以实例化。
这里就使用到JDK8的newInstance()
如果JDK11等,clazz.getDeclaredContructor().newInstance()
####创建package:reflect.hero,在下面分别创建3个类
####创建一个interface在package:reflect
###ISkill
package com.company.reflect;
public interface ISkill {
void q();
void w();
}
###类1:CamilleImpl
package com.company.reflect.hero;
import com.company.reflect.ISkill;
public class CamilleImpl implements ISkill {
public void q(){ System.out.println("Camille Q"); }
public void w(){ System.out.println("Camille W"); }
}
###类2:CamilleImpl
package com.company.reflect.hero;
import com.company.reflect.ISkill;
public class DianaImpl implements ISkill {
public void q(){ System.out.println("Diana Q"); }
public void w(){ System.out.println("Diana W"); }
}
###类3:FoxImpl
package com.company.reflect.hero;
import com.company.reflect.ISkill;
/* 类实现接口 规范化类的名字后面追加Impl*/
public class FoxImpl implements ISkill {
public void q(){ System.out.println("Fox Q"); }
public void w(){ System.out.println("Fox W"); }
}
####创建一个类在package:reflect,HeroFactory
####HeroFactory
package com.company.reflect.hero;
import com.company.reflect.ISkill;
public class HeroFactory {
public static ISkill getHero(String HeroName) {
String classStr = String.format("reflect.hero.%s",HeroName);
Class<?> cla = null;
try {
cla = Class.forName(classStr);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Object obj=new Object();
try {
obj = cla.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return (ISkill)obj;
}
}
###Main
package com.company;
import com.company.reflect.ISkill;
import com.company.factory.hero.CamilleImpl;
import com.company.abstraction.hero.DianaImpl;
import com.company.abstraction.hero.FoxImpl;
import com.company.reflect.hero.HeroFactory;
import java.util.Scanner;
public class Main {
/* demo3~domo4 main方法*/
public static void main(String[] args) {
String HeroName = Main.getPlayerInput();
/* import ISkill 的目录要改成 factory*/
ISkill iSkill = HeroFactory.getHero(HeroName);
iSkill.q();
}
private static String getPlayerInput(){
System.out.println("Enter a Hero's Name:");
Scanner scanner = new Scanner(System.in);
return scanner.nextLine();
}
}
使用Interface+工厂模式+反射模式,可以让代码变得稳定;但并应用 IOC 【控制反转】和DI【依赖注入】的原理。
像上述的例子,如果我们的工厂模式是一个大工厂,可以给我们所需要对象,那么是否就可以实现理想化的IOC呢?
Spring的IOC跟上面的逻辑类似,引入了容器(类似一个大工厂),用户第二次输入的时候,从缓存容器,提升性能
关于 DIP( 依赖倒置 )、IOC和DI的介绍
DIP:抽象不应该依赖具体的实例/方法。细节应该依赖抽象
例如demo3和demo4,把main方法是通过依赖抽象【工厂模式+interface】
DI的简介【依赖注入】: 由以前主要像容器要对象,如(HeroFactory.getHero或new 实例化),改成容器提供对象(this.getHero)
- 属性注入
- 构造注入
- 接口注入
引入容器后,类与类之间没有依赖关系,让系统变得更加稳定。
IOC的简介:DI是IOC的一种实现/应用
IOC的思想:main方法中,直接就可以使用这个对象,而不用实例化。 例如DI【依赖注入】由容器提供对象
控制反转:
从代码的角度看,原本在具体方法(主)实例化后,调用对象(从)改变成由容器提供对象给具体的方法。
从业务的角度看,由原来程序员决定具体功能,改成产品经理改变具体的功能。因为程序员只能新增类提供给具体方法即可。
如这上面的例子(demo1~demo3):如果没有实现IOC的话,新增英雄,除了新建英雄的类,还要在具体方法中修改。
引入了IOC后,主动权交了产品经理,程序员只需要新增英雄的类即可