[把你的理性思维慢慢变成条件反射]
本文开始,我们将介绍常见的设计模式。文章结构为:经典问题,思路分析,示例工程,错误写法,推荐写法,模式总结,反思等部分构成。在正式开始之前,按照惯例,请各位看官准备好如下练习环境:
操作系统:win7 x64
其他软件:eclipse mars,jdk7
--------------------------------------------------------------------------------------------------------------------------------------------------------
经典问题:
各位看官,在大学期间,或者面试时,经常能够遇到的一道基础题目:请用任何一种面向对象语言实现实现一个计算器控制台程序。
思路分析:
要点一:面向对象语言
要点二:实现计算器功能。扩展思路:加减乘除等运算
要点三:控制台。
综合以上要点,得到程序中至少出现的几种类型的角色:输入,输出,运算方法(多个)。
示例工程:
错误写法:
创建Calculator.java,具体内容如下:
package com.csdn.ingo.gof_SimpleFactory.base;
import java.util.Scanner;
public class Calculator {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("please enter first number:");
int numA = scan.nextInt();
System.out.println("please enter the operator:");
String op = scan.next();
System.out.println("please enter second number:");
int numB = scan.nextInt();
System.out.println("the equation result is:");
if(op.equals("+")){
System.out.println(numA+numB);
}
if(op.equals("-")){
System.out.println(numA-numB);
}
if(op.equals("*")){
System.out.println(numA*numB);
}
if(op.equals("/")){
System.out.println(numA/numB);
}
}
}
错误原因:
虽然代码功能是正确的,但是几乎违反了前文我们介绍的所有原则。这样的代码非常的不利于维护与扩展,并且,在某些场景下对于系统安全性存在巨大的侵害。
修改版代码:
在version_one包下,创建Operation.java文件,具体代码如下:
package com.csdn.ingo.gof_SimpleFactory.version_one;
public class Operation {
public static double getResult(double A, double B, String op) {
double result = 0d;
switch (op) {
case "+":
result = A + B;
break;
case "-":
result = A - B;
break;
case "*":
result = A * B;
break;
case "/":
result = A / B;
break;
}
return result;
}
}
在version_one包下,创建Window.java文件,具体代码如下:
package com.csdn.ingo.gof_SimpleFactory.version_one;
import java.util.Scanner;
public class Window {
public static void main(String[] args) {
try {
Scanner scan = new Scanner(System.in);
System.out.println("please enter first number:");
int numA = scan.nextInt();
System.out.println("please enter the operator:");
String op = scan.next();
System.out.println("please enter second number:");
int numB = scan.nextInt();
System.out.println("the equation result is:");
System.out.println(Operation.getResult(numA, numB, op));
} catch (Exception e) {
e.printStackTrace();
}
}
}
错误原因:
version_one下的代码虽然实现了输入输出,运算方法的分离,但还是违反了“单一职责原则”“开闭原则”等。具体的讲:运算方法类过多的集成了运算种类,并且,对运算方法的某一个的修改,会因为开发人员的疏忽,从而非常容易的扩散给其他运算方法,进而导致原有功能的错误与缺失,甚至会导致发生严重的安全性问题。
推荐写法:
在version_two包下,创建Operation.java,具体内容如下:
package com.csdn.ingo.gof_SimpleFactory.version_two;
public class Operation {
private double numA = 0;
private double numB = 0;
public double getNumA() {
return numA;
}
public void setNumA(double numA) {
this.numA = numA;
}
public double getNumB() {
return numB;
}
public void setNumB(double numB) {
this.numB = numB;
}
public double getResult(Operation op){
double result = 0;
return result;
}
}
在version_two包下,创建Add.java,Div.java,Mul.java,Sub.java四个文件,鉴于篇幅关系,仅展示一个文件,具体内容如下:
package com.csdn.ingo.gof_SimpleFactory.version_two;
public class Add extends Operation {
@Override
public double getResult(Operation op) {
double result = 0;
result = op.getNumA() + op.getNumB();
return result;
}
}
在version_two包下,创建OperationFactory.java,具体内容如下:
package com.csdn.ingo.gof_SimpleFactory.version_two;
public class OperationFactory {
public static Operation createOperate(String op){
Operation oper = null;
switch (op) {
case "+":
oper = new Add();
break;
case "-":
oper = new Sub();
break;
case "*":
oper = new Mul();
break;
case "/":
oper = new Div();
break;
}
return oper;
}
}
在version_two包下,创建Window.java,具体内容如下:
package com.csdn.ingo.gof_SimpleFactory.version_two;
import java.util.Scanner;
public class Window {
public static void main(String[] args) {
try {
Operation oper = new Operation();
Scanner scan = new Scanner(System.in);
System.out.println("please enter first number:");
double numA =scan.nextInt();
oper.setNumA(numA);
System.out.println("please enter the operator:");
String op = scan.next();
System.out.println("please enter second number:");
double numB =scan.nextInt();
oper.setNumB(numB);
System.out.println("the equation result is:");
System.out.println(OperationFactory.createOperate(op).getResult(oper));
} catch (Exception e) {
e.printStackTrace();
}
}
}
推荐原因:
- 符合“单一职责原则”,“开闭原则”等。
- 输入,输出与运算分离。即多个对象关系解耦。
- 每个运算方法都是独立的,易扩展,已维护,修改的影响范围有限。
- 新增任意多个运算方法时,不会涉及原有运算方法,不涉及原有调用方式,仅涉及factory文件。
- 运算方法采用继承多肽(接口亦可),保证子类正确运行(非逻辑正确)
模式总结:
UML结构图:
概念总结:
简单工厂模式:定义一个工厂类,它可以根据参数的不同返回不同类的实例对象,被创建的实例通常继承自相同的父类或者实现了相同的接口。注意:在该模式中,创建实例的方式是静态方法。
抽象的组成部分为:抽象父类,具体子类,工厂类,三部分组成。
用法总结:
只需要传入正确的参数即可,就能够得到与之对应的对象,使用者因此不用关注创建细节作为拓展功能,在某些场景下,为了节省代码的编译时间及系统运行时策略调整。该模式中factory所接受的参数可由字符串变为配置文件的形式。此方法,建议根据实际情况进行操作。
反思:
应用场景:
- 需要创建有限个,较少的对象,并且对象数量存在变动的可能。
- 工厂方法的使用者,完全不关心创建过程。
优点:
- 工厂类包含有限个判断逻辑,可以随时决定,并修改创建的实例类型。
- 客户端使用者,移除对象的创建职责。
- 客户端使用者,完全丢弃对具体产品类型的关注,减少代码耦合度。
缺点:
- 工厂类包含的逻辑判断,需要极高的稳定性,安全性。否则,整个逻辑处理将会发生错误。
- 每个具体的产品都会独立成为一个文件,一定程度上增加了代码管理成本。
- 注意刚刚提及的静态创建方法,由于静态方法不能够被实现为多态。因此,会极大的限制工厂类使用多态扩展的能力。
-------------------------------------------------------------------------------------------------------------------------------------------------------
至此,被说了很多遍的设计模式---简单工厂模式 结束
参考资料:
图书:《大话设计模式》
其他博文:http://blog.csdn.NET/lovelion/article/details/7563445