《大话设计模式》之简单工厂模式(Java版)

##导火线: “请用C++、Java、C#或VB.NET任意一种面向对象语言实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果。”

#菜鸟阶段的编程:

public class demo {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入数字A:");
        String A = scanner.nextLine();
        System.out.println("请选择运算符号(+、-、*、/):");
        String B = scanner.nextLine();
        System.out.println("请输入数字B:");
        String C = scanner.nextLine();
        String D = "";

        if ("+".equals(B)){
            D = (Double.parseDouble(A) + Double.parseDouble(C)) + "";
        }
        if ("-".equals(B)){
            D = (Double.parseDouble(A) - Double.parseDouble(C)) + "";
        }
        if ("*".equals(B)){
            D = (Double.parseDouble(A) * Double.parseDouble(C)) + "";
        }
        if ("/".equals(B)){
            D = (Double.parseDouble(A) / Double.parseDouble(C)) + "";
        }
        System.out.println("结果是:"+ D);
    }

上述代码存在的毛病:
代码毛病
上述代码代码规范及优化:

public class demo {
    public static void main(String[] args) {
        try {
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入数字A:");
            String strNumberA = scanner.nextLine();
            System.out.println("请选择运算符号(+、-、*、/):");
            String strOperator = scanner.nextLine();
            System.out.println("请输入数字B:");
            String strNumberB = scanner.nextLine();
            String strResult = "";

            switch (strOperator)
            {
                case "+":
                    strResult = (Double.parseDouble(strNumberA) + Double.parseDouble(strNumberB)) + "";
                    break;
                case "-":
                    strResult = (Double.parseDouble(strNumberA) - Double.parseDouble(strNumberB)) + "";
                    break;
                case "*":
                    strResult = (Double.parseDouble(strNumberA) * Double.parseDouble(strNumberB)) + "";
                    break;
                case "/":
                    if (!"0".equals(strNumberB)){
                        strResult = (Double.parseDouble(strNumberA) / Double.parseDouble(strNumberB)) + "";
                    }
                    else{
                        strResult = "除数不能为0";
                    }
                    break;
            }
            System.out.println("结果是:"+ strResult);
        }catch (Exception ex){
            System.out.println("您的输入有错:" + ex.getMessage());
        }
    }
}

#面向对象的编程
“所有编程初学者都会有这样的问题,就是碰到问题就直觉地用计算机能够理解的逻辑来描述和表达待解决的问题及具体的求解过程。这其实是用计算机的方式去思考,比如计算器这个程序,先要求输入两个数和运算符号,然后根据运算符号判断如何运算,得到结果,这本身没有错,但这样的思维却使得我们的程序只为满足实现当前的需求,程序不容易维护,不容易扩展,更不容易复用。从而达不到高质量代码的要求。”
例子:【活体印刷】
这里写图片描述
三国时期,由于还未发明活字印刷,所有要改字的时候,就必须要整个刻板全部重新刻!实在是很吐血很蛋痛!而到了后来,活体印刷的出现,此景此情此处只需更改四字即可,其余工作都未白做,岂不妙哉!
“第一,要改,只需更改要改之字,此为可维护;第二,这些字并非用完这次就无用,完全可以在后来的印刷中重复使用,此乃可复用;第三,此诗若要加字,只需刻字加入即可,这是可扩展;第四,字的排列其实可能是竖排也可能是横排,此时只需将活字移动就可做到满足排列需求,此是灵活性好。”
“可维护、可复用、可扩展、灵活性好这四点,不仅仅是面向对象编程的一个起始点更是我们对待每一个程序应用一个较好的编程思想!(感悟)”

##导火线:基于以上的情景,要求你再写一个Windows的计算器,你现在的代码能不能复用呢?

“有人说直接把代码复制过去不就可以了吗?改动不多不算麻烦。这其实是非常不好的编码习惯,因为当你的代码中重复的代码多到一定程度,维护的时候可能就是一场灾难。越大的系统,这种方式带来的问题越严重,编程有一原则,就是用尽可能的办法去避免重复。回过头看看我们写的代码,有哪些是和控制台(界面逻辑)无关的,而哪些又与计算器(业务逻辑)有关呢?”
“准确地说,就是让业务逻辑与界面逻辑分开,让它们之间的耦合度下降。只有分离开,才可以达到容易维护或扩展。”
#业务封装的编程:

/**
 *  运算类
 * @author CHEN-JY
 *
 */
public class Operation {
    public static double GetResult(double numberA, double numberB, String operate)
    {
        double result = 0d;
        switch (operate)
        {
         case "+":
             result = numberA + numberB;
             break;
         case "-":
             result = numberA - numberB;
             break;
         case "*":
             result = numberA * numberB;
             break;
         case "/":         
                 result = numberA / numberB;                   
             break;
        }
        return result;
    }
}
/**
 * 业务与界面分离
 * @author CHEN-JY
 *
 */
public class ClassB {
    public static void main(String[] args) {        
        try {
             Scanner scanner = new Scanner(System.in);
             System.out.println("请输入数字A:");
             String strNumberA = scanner.nextLine();
             System.out.println("请选择运算符号(+、-、*、/):");
             String strOperator = scanner.nextLine();
             System.out.println("请输入数字B:");
             String strNumberB = scanner.nextLine();
             String strResult = "";
             strResult = Operation.GetResult(Double.parseDouble(strNumberA), Double.parseDouble(strNumberB), strOperator) + "";
             System.out.println("结果是:"+ strResult);
        } catch (Exception e) {
            System.out.println("您的输入有错:" + e.getMessage());
        }
    }
}

如此的代码编程,不单是Windows程序,Web版程序需要运算也可以用它,PDA、手机等需要移动系统的软件需要运算也可以用它。但仅此而已,还谈不上完全面向对象,此处仅仅只用了面向对象三大特性中的其中一个(封装),还有两个没用(继承和多态)呢!
#紧耦合 VS 松耦合
:“基于以上的代码,是否已做到很灵活的可修改和扩展呢?”
条件:“现在如果我希望增加一个开根(sqrt)运算,你会如何改?”
菜鸟回答:“那只需要改Operation类就行了,在switch中加一个分支就行了。”
分析:“问题是你要加一个平方根运算,却需要让加减乘除的运算都得来参与编译,如果你一不小心把加法运算改成了减法,这岂不是大大的糟糕。打个比方,如果现在公司要求你为公司的薪资管理系统做维护,原来只有技术人员(月薪),市场销售人员(底薪+提成),经理(年薪+股份)三种运算算法,现在要增加兼职工作人员(时薪)的算法,但按照上述的程序写法,公司就必须要把包含原三种算法的运算类给你,让你修改,你如果心中小算盘一打,‘TMD!公司给我的工资这么低,这下有机会了’,于是你除了增加了兼职算法以外,在技术人员(月薪)算法中写了一句。

if (技术人员是我)
{
    salary *= 1.1;
}

如此就意味着,你的月薪每月都会增加10%(小心被抓去坐牢),本来是让你加一个功能,却使得原有运行良好的功能代码产生了变化,这个风险太大了!”
+继承思想的编程:

/**
 * 运算类
 * @author CHEN-JY
 *
 */
public abstract class Operation {

    private double _numberA = 0;
    private double _numberB = 0;

    public double get_numberA() {
        return _numberA;
    }
    public void set_numberA(double _numberA) {
        this._numberA = _numberA;
    }
    public double get_numberB() {
        return _numberB;
    }
    public void set_numberB(double _numberB) {
        this._numberB = _numberB;
    }

    public abstract double getResult();
}
/**
 * 加减乘除类 -- 加法类,继承运算类
 * @author CHNE-JY
 *
 */
public class OperationAdd extends Operation {   
    @Override
    public double getResult() {
        double result = 0;
        return get_numberA() + get_numberB();
    }
}
/**
 * 加减乘除类 -- 减法类,继承运算类
 * @author CHNE-JY
 *
 */
public class OperationSub extends Operation {
    @Override
    public double getResult() {
        double result = 0;
        return get_numberA() * get_numberB();
    }

}
/**
 * 加减乘除类 -- 乘法类,继承运算类
 * @author CHNE-JY
 *
 */
public class OperationMul extends Operation {   
    @Override
    public double getResult() {
        double result = 0;
        return get_numberA() + get_numberB();
    }
}
/**
 * 加减乘除类 -- 除法类,继承运算类
 * @author CHNE-JY
 *
 */
public class OperationDiv extends Operation {   
    @Override
    public double getResult() {
        double result = 0;
        return get_numberA() / get_numberB();
    }
}

此时编程到了这,这样要修改任何一个算法就不需要提供其他算法的代码了。但问题来了,我如何让计算器知道我是希望用哪一个算法呢?”
#简单工厂模式的编码:

/**
 * 简单运算工厂类
 * @author CHEN-JY
 *
 */
public class OperationFacotry {
    public static Operation createOperate(String operate){      
        Operation oper = null;
        switch (operate)
        {
            case "+":
                oper = new OperationAdd();
                break;
            case "-":
                oper = new OperationSub();
                break;
            case "*":
                oper = new OperationMul();
                break;
            case "/":
                oper = new OperationDiv();
                break;
        }
        return oper;
    }   
}
/**
 * 客户端代码
 * @author CHEN-JY
 *
 */
public class ClassC {

    public static void main(String[] args) {
        Operation oper;
        oper = OperationFacotry.createOperate("-");
        oper.set_numberA(1);
        oper.set_numberB(2);
        double result = oper.getResult();
        System.out.println("结果是:" + result);
    }

}

如此一来,那么我们需要增加各种复杂运算,比如平方根,立方根,自然对数,正弦余弦等,只需增加相应的运算子类就可以了。
这里写图片描述
简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。 – 百度百科解释

猜你喜欢

转载自blog.csdn.net/weixin_39250980/article/details/81675506