设计模式之简单工厂模式
哈哈哈,从读书起就不喜欢写作的我决定从今天开始写点东西了。嗯,就是像某某前辈说的 “从设计模式开始写博客吧,它会让你受益无穷”。那我就从设计模式开始吧。我学习的是《大话设计模式》这本书。本着理论-实践-总结的学习方式,我现在的总结肯定是来源于该书,主要记录我的学习过程。
题目:“请用面向对象语言实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果”。我一看到这题目就在脑中搜索控制台输入的方法。居然只是模糊的有点印象。看来这脑子不好使呀,学过的东西都还给书本了。
方法一:
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图也并不是那么难嘛,哈哈。
总结:编程是一门技术,更加是一门艺术。不能只满足于写能正确运行的代码,还要考虑如何让代码更加简练,更加容易维护,容易扩展和复用。
上一篇: 设计模式-建造者模式
下一篇: 设计模式-装饰模式(Decorator)