【简单工厂模式】我以为我真的了解面向对象编程

433 阅读6分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

你可能并不理解面向对象

 对于大部分学Java的人来说,面向对象这四个字好像深入骨髓。直到我看了《大话设计模式》后我才发现,我对面向对象编程一无所知,初学Java的初学者都知道面向对象编程有三大特性:封装,继承,多态。对,这三个特性似乎已经“性人一体”了,但是我现在问你:什么是面向对象编程?什么是面向的对象的三大特性?如何应用到实际开发中呢?前两个问题很多人基本脱口而出,但是第三个问题基本没啥想法,我第一次被问到这个问题的时候就是脑子空白!所以准备通过《大话设计模式》中的一个例子由浅入深,说清楚三大特性在开发中的用法。

代码小编用Java实现,原书是C++实现,但是二者都是面向对象的语言,不影响观看。

需求:我想要一个计算器功能,能够实现四则运算。

故事分析及设计

四则运算就是加减乘除,大概我们需要做这些事:

  1. 输入第一个数
  2. 输入运算类型("+","-","*","/")
  3. 输入第二个数
  4. 得到运算结果

功能实现

 刚学Java不久的小林看到这个用户故事后,心里想:就这需求?那不是有手就行?于是小林写下下这段代码

public class Program {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入第一个数:");
        int num1 = sc.nextInt();
        System.out.println("请输入运算符号:");
        String symbol = sc.next();
        System.out.println("请输入第二个数:");
        int num2 = sc.nextInt();

        int result = 0;
        switch (symbol){
            case "+" :
                result = num1 + num2;
                break;
            case "-":
                result = num1 - num2;
                break;
            case "*":
                result = num1 * num2;
                break;
            case "/":
                result = num1 / num2;
                break;
        }
        System.out.println("计算结果是:" + result);
    }
}

我们来测试一下:

 结果是对的,回到面向对象的三大特性:封装,继承,多态。我似乎在小林的代码中看不到它们的身影,这是面向对象吗?答案显而易见,这是面向过程,并不是面向对象!好了我们改进一下,代码像下面这样:

业务类代码:Operation计算类

public class Operation {
    public static int numCount(int num1, String symbol, int num2) {
        int result = 0;
        switch (symbol) {
            case "+" :
                result = num1 + num2;
                break;
            case "-":
                result = num1 - num2;
                break;
            case "*":
                result = num1 * num2;
                break;
            case "/":
                result = num1 / num2;
                break;
        }
        return result;
    }
}

界面类代码:运行类

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入第一个数:");
        int num1 = sc.nextInt();
        System.out.println("请输入运算符号:");
        String symbol = sc.next();
        System.out.println("请输入第一个数:");
        int num2 = sc.nextInt();
        int result = Operation.numCount(num1, symbol, num2);
        System.out.println("计算结果是:" + result);
    }
}

​  好了,小林代码已经改进好了。这时候小林过来了,看见代码后哈哈大笑,说道:你把我的代码分成两部分就叫面向对象了?你这不是在逗我吗?

​  我不知道你们是否有类似的想法,说下原来在一个类中分成两个类的过程中体现了面向对象的那个特性。我们单独写了一个Operation类,这个类里面的代码是可复用的,这就是封装,将可复用的代码提取出来。      重点来了,回到我们最初的需求,做一个可以进行四则运算的计算器。对,我们做的是计算器,那么控制台输出的跟计算器有关吗?好像没有关系,所以应该把计算逻辑跟控制台输出分开,这样抽出的来的Opertion类是否就能复用了。

  小林似乎还是有些问题:封装我理解了,这么简单的需求你给我用继承跟多态实现一下试试?我希望你能亮瞎我24k黄金狗眼。

​  有求必应,我们来给小林在改进一些代码,我们做了如下的改动:

计算父类   这个类定义两个参与运算的数字跟获取计算结果的方法即可。

@Data
public class Operation {
    private int num1;
    private int num2;

    public int getResult(){

        int result = 0;
        return result;
    }
}

四则运算子类   所有子类继承计算父类,重写计算结果方法。

// 加法
public class OperationAdd extends Operation {
    @Override
    public int getResult() {
        int result = 0;
        result = this.getNum1() + this.getNum2();
        return result;
    }
}

// 除法
public class OperationDiv extends Operation {
    @Override
    public int getResult() {
        int result = 0;
        if (this.getNum2() == 0){
            System.out.println("数学Evader1997教的?");
        }
        result = this.getNum1() / this.getNum2();
        return result;
    }
}

// 乘法
public class OperationMul extends Operation {
    @Override
    public int getResult() {
        int result = 0;
        result = this.getNum1() * this.getNum2();
        return result;
    }
}

// 减法
public class OperationSub extends Operation {
    @Override
    public int getResult() {
        int result = 0;
        result = this.getNum1() - this.getNum2();
        return result;
    }
}

 改进完成了,简单明了这就是继承,我们继承了运算父类,重写了父类计算结果的方法。

 小林端着一杯刚没的奶茶过来了:改这么久,没给我改简单点,给我改出来四五个类?这算哪门子改进,继承还不如刚才的代码呢。

 小林你听我说:如果我让你添加一个取余的运算你怎么处理?把一个类拆开,添加方法吗? 确实是可以,但是有两点不好的地方,你可能手误会改错原来写好的代码(出现的可能性比较小,万一不小心改了,你同事该拿40米大砍刀找你了),第二个开发中有个原则叫开闭原则,对扩展开放,对修改关闭。

 基于这两点,我请大家思考继承是使代码更简单还是更复杂,我想很多人都能体会到时更简单,因为有了继承的身影,我们新增一个区域方法新建一个取余类继承运算类即可,不用修改原来写好的增删改查类即可扩充功能,可能有些人听不太懂这段话,但是我确认你能体会到继承带来的力量!

细心的小林稍作思考,问到:封装,继承都有了,多态如何运用到上面的代码中呢?我是在是想不出来了!!!

 到这里,请问下自己什么是多态? 如果还不清楚的话,建议再去看看基础概念。这边简略说明一下,多态分为继承多态和接口多态。实际上上面的代码已经可以运用到多态了。比如这样:

  这就是多态,用父类引用接收子类对象。这时候我们发现我一个计算器好像被拆成四个了,我new了四个不同的对象,四则运算写成四个子类是毋庸置疑的,但是多态这么写,明面上我看着不舒服。   别担心,我们用简单工厂模式来修改一下!

public class OperationFactory {

    public static Operation createOperation(String operate){
        Operation operation = null;
        switch (operate){
            case "+" :
                operation = new OperationAdd();
                break;
            case "-":
                operation = new OperationSub();
                break;
            case "*":
                operation = new OperationMul();
                break;
            case "/":
                operation = new OperationDiv();
                break;
        }
        return operation;
    }

}
public class Main {
    public static void main(String[] args) {
        Operation operation = null;
        operation = OperationFactory.createOperation("+");
        operation.setNum1(1);
        operation.setNum2(2);
        System.out.println("运算结果是:" + operation.getResult());
    }
}

  好勒,皆大欢喜,这就是实现一个计算器的代码,它包含了封装,继承,多态。此时我觉得你应该感觉到了震撼。   

UML类图

UML类图能独立画出来,说明你已经掌握了这个设计模式!

  自信源于努力!大家好,我是小林,我们下期见!