风袖电商springboot之浅谈开闭原则(OCP)

首先简单的说明一下什么是开闭原则(OCP)

对扩展开放,对修改关闭,这就是开闭原则。

接下来,我们通过LOL这个案例来实现开闭原则

可以看到,在lol01包下,刚开始英雄只有两个,Main方法如下:

public class Main {

    public static void main(String[] args) {
        String name = Main.getPlayerInput();
        switch (name){
            case "Diana":
                Diana diana = new Diana();
                diana.r();
                break;
            case "Irelia":
                Irelia irelia = new Irelia();
                irelia.r();
                 break;
        }

    }

    private static  String getPlayerInput(){
        System.out.println("Enter a Hero's name:");
        Scanner sc = new Scanner(System.in);
        return sc.nextLine();
    }
}

 

哈哈,相信上述代码,大家都能看懂,如果新增了个英雄,就在switch里在增加方法(初学者写出来的),比方说新增了个英雄

接下来来改进一下,第二版代码如下:

public class Main {


    public static void main(String[] args) throws Exception {
        String name = Main.getPlayerInput();
        ISkill iSkill;
        switch (name){
            case "Diana":
                iSkill = new Diana();
                break;
            case "Irelia":
                iSkill= new Irelia();
                break;
            case "Camille":
                iSkill= new Camille();
                break;
            default:
                throw new Exception();
        }
        iSkill.r();
    }

    private static  String getPlayerInput(){
        System.out.println("Enter a Hero's name:");
        Scanner sc = new Scanner(System.in);
        return sc.nextLine();
    }
}

定义了一个ISkill接口,并使所有的英雄类都实现了这个接口

public interface ISkill {
    void q();
    void w();
    void e();
    void r();
}
public class Camille implements ISkill {
    public void q(){
        System.out.println("Camille q");
    }
    public void w(){
        System.out.println("Camille w");
    }
    public void e(){
        System.out.println("Camille e");
    }
    public void r(){
        System.out.println("Camille r");
    }
}

第二版代码只是单纯的使用了interface接口,那么和第一版代码比较,有什么优势和缺点

单纯的interface可以统一方法的调用,但是它不能统一对象的实例化(是不是一脸懵逼了)

面向对象的过程一般只做两种事

      1.实例化对象

      2.调用对象的方法(完成业务逻辑)

     3.只有一段代码中没有new的出现,才能保持代码的相对稳定,才能逐步实现OCP

     4.实质上,一段代码要想保持稳定,就不应该负责对象的实例化,但在面向对象编程中,对象实例化是不可能被消除的

     5.把对象实例化的过程转移到其他代码片段中(这不就导致其他代码块也就不稳定了吗

 

第三代码比第二版代码多了一个工厂模式中静态工厂,将swich中的代码移到静态方法中

这时,main中的方法就很简单了

public class Main {


    public static void main(String[] args) throws Exception {
        String name = Main.getPlayerInput();
        ISkill iSkill = HeroFactory.getHero(name);
        iSkill.r();
    }

    private static  String getPlayerInput(){
        System.out.println("Enter a Hero's name:");
        Scanner sc = new Scanner(System.in);
        return sc.nextLine();
    }
}

想一想,在第三版代码里,对于main方法来说,确实实现了OCP,代码也相对稳定,但对于HeroFactory类来说,当要新增一个类时,不是还要修改代码,new一个对象吗?

代码中总是会存在不稳定,隔离这些不稳定,保证其他代码是稳定的

拿上面的例子来说,所有不稳定的代码片段都被封装在HeroFactory类,那么除HeroFactory类,那么其他类中的代码都是相对稳定的。但是如果main中的方法足够复杂,这时就有许多的factory类,那么,问题来了,变动的factory会有很多,更改起来也很分散。假想一下,如果有一个超级大的工厂,可以把项目中所有的变动都封装在一起,这样是不是就符合代码的相对稳定性(写到这有没有感觉一丝丝熟悉,好像这就是IOC的雏形)

拿HeroFactory工厂类来分析,代码之所以不稳定,表面上看是因为new不同的对象。实际上是因为不同的变化需求。假想一下,如果用户输入的不是一个字符串,而是一个类,那么这段代码是不是就稳定了。那么,第四版代码出来了:

HeroFactory代码如下:
public class HeroFactory {
    public static ISkill getHero(String name) throws Exception {

       String classStr = "lol4.hero."+name;
       // Class.forName("类的全限名")。
        Class<?> clazz = Class.forName(classStr);
        // T newInstance() ;  创建Class对象关联类的对象,其实底层也是调用无参数构造器,已经被淘汰。
        Object obj = clazz.newInstance();

        return (ISkill)obj;
    }
}

根据用户传过来的字符串进行拼接,获得类的权限名,通过反射获取到这个类。在main方法中进行测试(代码与第三版的一样)

通过工厂模式加反射机制,很好的消除了变化的部分,使得代码相对稳定。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值