欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

设计模式之简单工厂模式

程序员文章站 2022-07-10 17:44:45
哈哈哈,从读书起就不喜欢写作的我决定从今天开始写点东西了。嗯,就是像某某前辈说的 “从设计模式开始写博客吧,它会让你受益无穷”。那我就从设计模式开始吧。我学习的是《大话设计模式》这本书。本着理论-实践-总结的学习方式,我现在的总结肯定是来源于该书,主要记录我的学习过程。 题目:“请用面向对象语言实现 ......

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

题目:“请用面向对象语言实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果”。我一看到这题目就在脑中搜索控制台输入的方法。居然只是模糊的有点印象。看来这脑子不好使呀,学过的东西都还给书本了。

方法一:

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图也并不是那么难嘛,哈哈。

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