设计模式之简单工厂模式

哈哈哈,从读书起就不喜欢写作的我决定从今天开始写点东西了。嗯,就是像某某前辈说的 “从设计模式开始写博客吧,它会让你受益无穷”。那我就从设计模式开始吧。我学习的是《大话设计模式》这本书。本着理论-实践-总结的学习方式,我现在的总结肯定是来源于该书,主要记录我的学习过程。

题目:“请用面向对象语言实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果”。控制台输入的方法:

方法一:

int inputA = System.in.read();

System.out.println("inputA: " + inputA);

方法二:
Scanner scan = new Scanner(System.in);

String inputB = scan.nextLine();

System.out.println("inputB: " + inputB);

方法三:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String inputC = br.readLine();
System.out.println("inputC: " + inputC);

第一个方法获取的是字节,以int类型返回。第二和第三个方法都可以获取整行字符。但我没有发现这两个方法之间的区别。网上有些人说Scanner取得的输入以space, tab, enter 键为结束符,要想取得包含space在内的输入,用java.io.BufferedReader类来实现。但我发现Scanner也是能获取space的。额,这个还是留一边继,续我们的设计模式之旅吧。

第一个实现版本:

public class Calculator {
  static final int ADD = 43;
  static final int SUBTRACT = 45;
  static final int MULTIPLY = 42;
  static final int DIVIDE = 47;
  public static void main(String[] args) throws IOException {

  Scanner scan = new Scanner(System.in);
  System.out.println("请输入第一个运算数:");
  String input1 = scan.nextLine();
  System.out.println("请输入运算操作符: ");
  String operatorTemp = scan.nextLine();
  int operator = operatorTemp.charAt(0);
  System.out.println("请输入第二个运算数:");
  String input2 = scan.nextLine();

  //只实现了功能,没有考虑代码的灵活性,可扩展性,需要使用设计模式
  switch(operator){
    case ADD : System.out.println(Integer.valueOf(input1) + Integer.valueOf(input2)); break;
    case SUBTRACT : System.out.println(Integer.valueOf(input1) - Integer.valueOf(input2)); break;
    case MULTIPLY : System.out.println(Integer.valueOf(input1) * Integer.valueOf(input2)); break;
    case DIVIDE : System.out.println(Integer.valueOf(input1) / Integer.valueOf(input2)); break;
  }
  }

}

这个实现把计算和显示糅合在一块了,显然不够灵活,可扩展性差。

第二个实现版本:

运算类:

public class Operation {
  static final int ADD = 43;
  static final int SUBTRACT = 45;
  static final int MULTIPLY = 42;
  static final int DIVIDE = 47;
  public static double operate(String input1, String input2, String operatorTemp){
  int operator = operatorTemp.charAt(0);
  double result = 0;
  switch(operator){
    case ADD : result = Double.valueOf(input1) + Double.valueOf(input2); break;
    case SUBTRACT : result = Double.valueOf(input1) - Double.valueOf(input2); break;
    case MULTIPLY : result = Double.valueOf(input1) * Double.valueOf(input2); break;
    case DIVIDE : result = Double.valueOf(input1) / Double.valueOf(input2); break;
  }
  return result;
  }
}

客户端代码:

public class Calculator {

  public static void main(String[] args) throws IOException {  

    Scanner scan = new Scanner(System.in);
    System.out.println("请输入第一个运算数:");
    String input1 = scan.nextLine();
    System.out.println("请输入运算操作符: ");
    String operatorTemp = scan.nextLine();
    int operator = operatorTemp.charAt(0);
    System.out.println("请输入第二个运算数:");
    String input2 = scan.nextLine();

    double result = Operation.operate(input1, input2, operatorTemp);
    System.out.println("result: " + result);

  }

}

把计算单独放在一个类的方法里面,这样就可复用了。这里用到了封装思想。但可扩展性并不好,如果要增加一种运算方法,我们还得修改运算类。这个修改有风险,我们有可能把之前的加法算法也改了,而且还得重新编译此类。那该如何做比较好呢?这里得使用面向对象的继承、多态特性。

第三个版本:

运算类接口

public interface IOperation {
  public double operate(String input1, String input2);
}

运算实现类(加法)

public class OperationAdd implements IOperation {

  @Override
  public double operate(String input1, String input2) {
    double result = Double.valueOf(input1) + Double.valueOf(input2);
    return result;
  }

}

当然还有其他运算方法,我在这里就不贴出来了。接下来主角来了:

工厂类

public class OperationFactory {
  static final int ADD = 43;
  static final int SUBTRACT = 45;
  static final int MULTIPLY = 42;
  static final int DIVIDE = 47;
  static final int SQRT = 97;
  public static IOperation create(String operatorTemp){
    int operator = operatorTemp.charAt(0);
    IOperation operation = null;
    switch(operator){
      case ADD : operation = new OperationAdd(); break;
      case SUBTRACT : operation = new OperationSub(); break;
      case MULTIPLY : operation = new OperationMul(); break;
      case DIVIDE : operation = new OperationDiv(); break;
      case SQRT : operation = new OperationSqrt(); break;
    }
    return operation;
  }
}

这就是简单的工厂模式了,如果需要扩展,则需要新增运算子类,再修改switch分支。

类的结构图:(类的属性和方法和我代码中的不一定一样,但类间关系是一样的)

看了此UML图,是不是发现对类之间的连线、箭头之类所表示的含义模糊?不用着急,我也是。UML即Unified Modeling Language 统一建模语言。一幅图就基本包括了UML类图中的基本图示法。

矩形框:比如“动物”矩形框,它代表一个类。类图分三层,第一层显示类的名称,如果是抽象类就用斜体显示。第二层是类的特性,通常就是字段和属性。第三层是类的操作,通常是方法和行为。‘+’表示public,‘-’表示private,‘#’表示protected。

接口图:左下角的‘飞翔’就是一个接口图, 顶端有《interface》显示,第一行是接口名称,第二行是接口方法。接口还有另一种表示方法,俗称棒棒糖表示法,就是唐老鸭实现讲人话的接口。

继承关系:用空心三角形+实线来表示,比如动物,鸟,鸭,唐老鸭之间的关系。

实现接口:用空心三角形+虚线来表示,比如大雁实现飞翔接口。

关联关系:用实线箭头表示,比如企鹅与气候有很大的关联,企鹅需要知道‘气候’的变化,需要‘了解’气候规律。当一个类‘知道’另一个类时,可以用关联(association)。在企鹅类中会引用到气候对象。

聚合关系:用空心的菱形+实线箭头表示。表示一种弱的‘拥有’关系,体现A对象可以包含B对象,但B对象不是A对象的一部分。像大雁与雁群的关系,在雁群类中,可以有大雁的数组对象。

合成(组合)关系:用实心的菱形+实线箭头表示。合成关系连线的两端有数字,被称为基数,表明这一端的类可以有几个实例。像一只鸟可以有两只翅膀,有无数个实例就用n表示。关联关系和聚合关系也是可以有基数的。合成关系是一种强的‘拥有’关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。

依赖关系:用虚线箭头表示。动物的几大特征,比如新陈代谢,能繁殖。而动物要有生命力,需要氧气、水、食物等。也就是说动物依赖氧气和水。

看来UML图也并不是那么难嘛,哈哈。

总结:编程是一门技术,更加是一门艺术。不能只满足于写能正确运行的代码,还要考虑如何让代码更加简练,更加容易维护,容易扩展和复用。

原文地址:https://www.cnblogs.com/shicaiyou/p/9097442.html