从简易计算器到设计模式(策略、简单工厂)

前言

最近在拜读《大话设计模式》这本书,作为一个小白中的小白,读完以后感触还是很深的。再次做一个学习的记录,同时也算给自己多一次实践的机会。

问题

相信大家在上学时都写过“简易计算器”,就是要求很简单的键盘输入两个数字,然后再给定一个符号,去做加减乘除运算。最简单的计算器实现,实在是大学老师最爱的练习小demo之一呢hhhh

V1.0版本

大家思路都差不多,这不是直接一个switch就解决了吗?这不是有手就行?立马感觉自己能进BAT了呢hhhh(瞎掰的)

public static void main(String[] args) {
        //假装这是用户输入的东西
        Double num = null;
        Double otherNum = null;
        String operator = "*";
        Double result = null;

        //使用switch进行比对
        switch (operator){
            case "+":
                result = num+otherNum;
                break;
            case "-":
                result = num-otherNum;
                break;
            case "*":
                result = num*otherNum;
                break;
            case "/":
                //判断被除数不能为0
                if (otherNum != 0){
                   result = num/otherNum; 
                }
                break;
        }
    }

这样写就存在两个问题。

1.如果用户输出的是"/",那么前三次对比就相当于做了无用功,虽然在这种微乎其微的程序上没影响,但是一旦数量多了起来那就难以想象了。

2.从面向对象的设计原则上说,其实有违开闭原则。

开闭原则:对扩展开放,对修改封闭

以上述例子来说就是,允许增加新的计算方式,比如增加取余运算。但是原则上不允许修改原有的计算方式(加减乘除)!当然也不会有人有病去瞎改hhh,但是指不定手滑呢,就可能会导致原有的代码运行出问题。

于是2.0版本就应运而生!

V2.0版本

脑子一转,那可以考虑工具类。将业务逻辑部分进行封装。这样就遵循开闭原则了吧,至少是部分遵守了hhhh

public class OperationUtils {

    public static Double GetResult(double num,double otherNum,String operator){
        Double result = null;
        switch (operator){
            case "+":
                result = num+otherNum;
                break;
            case "-":
                result = num-otherNum;
                break;
            case "*":
                result = num*otherNum;
                break;
            case "/":
                //判断被除数不能为0
                if (otherNum != 0){
                    result = num/otherNum;
                }
                break;
        }
        return result;
    }
}

但是这也只是仅仅达到了封装的目的,如果按照我们以上的设想,当我们需要增加运算方式时,其实还是要去修改工具类里的东西,所以根本上还是没有遵守开闭原则。

于是v3.0版本应运而生..........

V3.0版本

首先设计一个计算的接口,其中只有一个得到计算结果的方法。至于如何实现,就根据不同实现类的计算方式即可。

计算操作接口

public interface Operation {
    /**
     * 计算结果的方法
     * @param num
     * @param otherNum
     * @return
     */
    Double getResult(double num,double otherNum);
}

加法实现类

/**
 * 加法实现类
 */
public class AddOperation implements Operation{
    //重写加法的计算方法
    @Override
    public Double getResult(double num, double otherNum) {
        Double result = null;
        result = num + otherNum;
        return result;
    }
}

减法实现类

/**
 * 减法实现类
 */
public class SubOperation implements Operation {
    //重写减法的计算方法
    @Override
    public Double getResult(double num, double otherNum) {
        Double result = null;
        result = num - otherNum;
        return result;
    }
}

剩余的实现类相信各位大佬肯定知道咋写了吧,这里本人就不一一展示了,就当给各位练练手吧(有懒狗,我不说是谁)

主函数

 public static void main(String[] args) {
            //假装这是用户输入的东西
            Double num = null;
            Double otherNum = null;
            String operator = "*";
            Double result = null;
            //加法实现
            AddOperation addOperation = new AddOperation();
            result = addOperation.getResult(num,otherNum);
            //减法实现
            SubOperation subOperation = new SubOperation();
            result = subOperation.getResult(num,otherNum);
}

至此,看起来就比较符合开闭原则了。

如果还需要增加计算方式,只需要实现计算方法接口即可。在增加时,因为根本不是同一个实现类,也就不要担心手滑碰到原有的代码从而导致被老板开除hhhh。

扩展知识点:面向接口编程

这也就是大家常说的面向接口编程,可以通俗理解为只将方法暴露在外,实际实现的逻辑其实在不同的实现类中。面向接口编程最早的体现(针对我来说)——为啥在三层架构中,业务逻辑层为啥要弄个Interface,然后又要搞个实现类去重写方法。这不是脱裤子放屁吗?我直接写我需要的方法不就好了。

现在想想当初的自己还是天真了hhhh,不得不感叹面向对象的博大精深啊...

策略模式

另外诸如此类的实现方法,也体现了设计模式的策略模式。

通俗理解:将不同的算法(加法,减法,乘法,除法)进行封装,这就可以让不同的地方使用相同的算法。也就是我那一天心血来潮新写了计算器plus,我就不用再把加减乘除的方法重新写一遍,直接把之前封装好的拿过来用就好了。(这样也避免了大量分支判断的存在,导致整个代码显得非常臃肿并且后期难以维护和修改)

因为本小弟也是初学者,理解得可能不是很透彻,有兴趣的hxd往这走

有趣的设计模式——解救抓狂的商场收银员

V4.0版本

以为3.0就完结了?必定不可能!其实在计算方面确实做的差不多了(以我的能力而言),4.0更多的是针对现有代码基础的优化。

我们虽然写了不同的计算实现类,但是在每次需要使用的时候,都要new一个对应实现类的对象,然后再调用getResult()方法进行计算。这样计算的东西少一点还好,要是用户加减乘除挨个来一遍,再倒着来一遍,那岂不是new对象都要花费很多时间???

简单工厂模式

通俗理解:我们需要一个各种实现类的集中制造基地,我们只要告诉工厂我要加法或者减法,它就直接给我们即可,不需要我们繁琐的自己去new一个新的对象。

简单计算实现类工厂

public class OperationFactory {
    /**
     * 获取实现类对象方法
     * @param operator
     * @return
     */
    public static Operation createOperation(String operator){
        Operation operation = null;
        //看运算符来判断需要生成的实现类
        switch (operator){
            case "+":
                operation = new AddOperation();
                break;
            case "-":
                operation = new SubOperation();
                break;
            case "*":
                operation = new MultiplicaOperation();
                break;
            case "/":
                operation = new DivOperation();
                break;
        }
        return operation;
    }
}

 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------更新于2022.09.26----------------------------------------------------------

简单工厂模式PRO

最近看到一个新的写法,就此记录下。首先我们分析下现有的简单工厂,主要存在两个问题。

1.代码入侵性比较强,每次在添加新的“计算方法”的时候,都要在OperationFactory类中进行新增,其实是有违开闭原则的。

2.多分支的判断,导致做了很多无用功。比如说:当前需要除法,但是必须case到最后一个分支才能得到,也就是前三次对比其实是无用功。

那么我们考虑通过反射来进行代码的优化

思路:通过反射直接进行对象的实例化

创建一个相关配置文件,后期更加方便修改。其中存对应的包名。

从简易计算器到设计模式(策略、简单工厂)_第1张图片

 更改简单工厂类

public class OperationFactory {

    public static Operation createOperation(String name){
        //首先读取配置文件,获取对应包名
        Properties properties = new Properties();
        Class cls = null;
        Operation operation = null;
        try {
            //读取配置文件
            properties.load(new FileInputStream("src/main/resources/name.properties"));
            //获取包名
            String className = properties.getProperty(name);
            //通过反射实例化对象
            cls = Class.forName(className);
            operation = (Operation) cls.newInstance();
        }catch (Exception e){
            System.out.println("处理异常...");
        }
        return operation;
    }
}

测试类

public class Test {

        public static void main(String[] args) {
            //假装这是用户输入的东西
            Double num = 10.5;
            Double otherNum = 2.5;
            Double result = null;
            //加法实现
            Operation addOperation = OperationFactory.createOperation("Add");
            result = addOperation.getResult(num,otherNum);
            System.out.println("加法计算结果:"+result);
            //减法实现
            Operation subOperation = OperationFactory.createOperation("Sub");
            result = subOperation.getResult(num,otherNum);
            System.out.println("减法计算结果:"+result);
    }
}

 结果展示

从简易计算器到设计模式(策略、简单工厂)_第2张图片

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------更新于2022.09.27-----------------------------------------------------

工厂方法模式

昨天的我无法理解工厂方法模式,今天的我突然茅塞顿开,再次记录一下。

何为工厂方法模式?和简单工厂的区别是什么?

工厂方法模式是对简单工厂的再一次抽象,克服了简单工厂的一些缺点。

举个栗子来说

=========================================================================

简单工厂模式:一个手机工厂同时造安卓手机(对象)和水果手机(对象)

工厂方法模式:手机工厂分为安卓手机厂(工厂)和水果手机厂(工厂),安卓手机厂只造安卓手机,水果厂只造水果手机

=========================================================================

书接上文,直接上代码。

1.首先准备一个公共工厂接口,需要实现创建实例的方法

public interface Factory {

    Operation createOperation();
}

2.加法工厂的实现,通过实现工厂接口从而重写方法

public class AddOperationFactory implements Factory{
    @Override
    public Operation createOperation() {
        AddOperation addOperation = new AddOperation();
        return addOperation;
    }
}

减法工厂,乘法工厂,除法工厂以此类推,就不(有)演(懒)示(狗)了。

小结(简单工厂和工厂方法的区别)

1.简单工厂其实是违背开闭原则的,因为每次新增创建实例的判断,其实都需要在原来的地方进行修改。而工厂方法只是实现接口,也就是不会修改原有的代码,只是做新增。

2.简单工厂将创建什么的实例(加法还是减法)的逻辑判断放在了内部,而工厂方法其实把这个判断移到了客户端进行,只有在创建时由客户端进行判断。

示例:

简单工厂测试类

public class Test {

        public static void main(String[] args) {
            //假装这是用户输入的东西
            Double num = 10.5;
            Double otherNum = 2.5;
            Double result = null;
            //加法实现
            //用户只负责讲运算符传入,具体实例哪个对象在内部进行判断
            Operation addOperation = OperationFactory.createOperation("Add");
            result = addOperation.getResult(num,otherNum);
            System.out.println("加法计算结果:"+result);
          
    }
}

工厂方法测试类

public class Test {

        public static void main(String[] args) {
            //假装这是用户输入的东西
            Double num = 10.5;
            Double otherNum = 2.5;
            Double result = null;
            //在客户端时,就知道需要创建的是加法工厂,而不是在内部进行判断
            Factory factory = new AddOperationFactory();
            Operation addOperation = factory.createOperation();
            result = addOperation.getResult(num,otherNum);
    }
}

工厂方法模式后续优化同样可以考虑由反射进行实例创建,这里留个坑过后补上。

任何设计模式没有绝对的好与坏,只看那种情况下的适配性最好,什么时候是最佳选择!

总结

至此真的完结了,仅仅看了第一小节,感觉腰不疼了,腿不酸了呢,实乃上古神书之一!

以上纯属瞎掰。

分享书中一句话:编程是一门技术,更是一门艺术。

曾经我就很好奇,大家都是流水线式的逻辑进行业务的实现,这还能分出好坏?现在看来实在是当初自己太年轻了hhh。

如有问题,请各位大佬指正。-----记一个菜狗的学习记录

你可能感兴趣的:(学习成长,设计模式)