前言


简单工厂模式,工厂方法模式,抽象工厂模式,这三个模式,当然还有单例模式,建造者模式等等,应该是日常工作中常用的,尤其是工厂模式,应该是最最常见的模式,对理解面向对象有重要的实际意义。

简单工厂模式

最简单,最直接,能满足大部分日常需求,不足是工厂类太简单——无法满足开闭原则,对多个产品的扩展不利



工厂方法模式——交给子类去创建

工厂方法模式,有了进步,把工厂类进行改进,提升为一个抽象类(接口),把对具体产品的实现交给对应的具体的子类去做,解耦多个产品之间的业务逻辑。



前面都是针对一个产品族的设计,如果有多个产品族的话,就可以使用抽象工厂模式

抽象工厂模式


抽象工厂模式的工厂,不再维护一个产品等级的某个产品(或说一个产品结构的某个产品更好理解),而是维护产品结构里的所有产品(横向x轴),具体到代码就是多个抽象方法去对应产品等级结构的各个产品实例

具体的工厂类实现抽象工厂接口,去对应各个产品族,每一个具体工厂对一个产品族,获得该产品族的产品结构(所有产品)



抽象工厂模式中的方法对应产品等级结构(每个类型中的具体产品),具体子工厂对应不同的产品族(产品类型)

面试题——计算器

 想到一个面试题: 写一个简单的计算器,满足加减乘除运算。逻辑比较简单:接受计算数据的输入,进行计算,返回结果。用Java实现。

面向过程版

面向过程的设计本身没有错,但是如果面试的是 java
的相关职位,使用一门面向对象的语言这样写是非常危险的。因为这样写,谁都会,但凡学过编程的,没有不会的。更重要的问题是,这样写的目的仅仅是为了完成任务,没有任何面向对象的思维体现。实际业务中,类似的程序一旦扩展,这样的代码是没有办法维护的。

缺点:完全面向过程设计,所有逻辑都集中在一个类(方法、函数里),缺少代码的重用……

这里的除 0 异常检测也是考点之一。
public class Main { public static void main(String[] args) { // 1、先接受数据的输入 //
2、进行计算// 3、返回计算结果 System.out.println("******计算器********\n 请输入第一个数:"); Scanner
scanner= new Scanner(System.in); String num1 = scanner.nextLine();
System.out.println("请输入运算符:"); String operation = scanner.nextLine();
System.out.println("请输入第二个数:"); String num2 = scanner.nextLine();
System.out.println("开始计算。。。。。。"); double result = 0; if ("+".equals(operation))
{ result= Double.parseDouble(num1) + Double.parseDouble(num2); } else if ("-"
.equals(operation)) { result= Double.parseDouble(num1) -
Double.parseDouble(num2); }else if ("*".equals(operation)) { result =
Double.parseDouble(num1) * Double.parseDouble(num2); } else if ("/"
.equals(operation)) {if (Double.parseDouble(num2) != 0) { result =
Double.parseDouble(num1) / Double.parseDouble(num2); } else {
System.out.println("除数不能为0!"); return; } } System.out.println(num1 + operation
+ num2 + " = " + result); } }
简单面向对象版

面向对象的设计就是把各个操作抽象为一个个的类,加法类,减法类,乘法类,除法类……每个运算类的职责就是进行属于自己的运算符的计算,客户端去调用对应的运算类即可。

代码如下:
public abstract class Operation { private double num1; private double num2;
public double getNum1() { return num1; } public void setNum1(double num1) { this
.num1 = num1; } public double getNum2() { return num2; } public void setNum2(
double num2) { this.num2 = num2; } public abstract double getResult(); }
具体的运算符子类,只用加法举例,其他省略。
public class Add extends Operation { @Override public double getResult() {
return this.getNum1() + this.getNum2(); } }
客户端代码
// 1、计算数据的输入 // 2、进行计算 // 3、返回计算结果 System.out.println("******计算器********\n
请输入第一个数:"); Scanner scanner = new Scanner(System.in); String num1 =
scanner.nextLine(); System.out.println("请输入运算符:"); String operation =
scanner.nextLine(); System.out.println("请输入第二个数:"); String num2 =
scanner.nextLine(); System.out.println("开始计算。。。。。。"); double result = 0; // 类型转换
double a = Double.parseDouble(num1); double b = Double.parseDouble(num2); if
("+".equals(operation)) { Operation o = new Add(); o.setNum1(a); o.setNum2(b);
result= o.getResult(); }
写到这里,貌似比之前也没什么大的改变,只是使用了面向对象的一丢丢,使用了类……在客户端还是需要显式的去 new
对应的运算类对象进行计算,客户端里还是维护了大量的业务逻辑……

继续改进,使用工厂模式——简单工厂模式

简单工厂模式版

一般学过的人,会立即想到该模式
public abstract class Operation { private double num1; private double num2;
public double getNum1() { return num1; } public void setNum1(double num1) { this
.num1 = num1; } public double getNum2() { return num2; } public void setNum2(
double num2) { this.num2 = num2; } public abstract double getResult(); } public
class Add extends Operation { @Override public double getResult() { return this
.getNum1() +this.getNum2(); } } public class Sub extends Operation { @Override
public double getResult() { return this.getNum1() - this.getNum2(); } }  
/////////////// 简单工厂类(也可以使用反射机制) public class OpreationFactory { public static
Operation getOperation(String operation) {if ("+".equals(operation)) { return
new Add(); } else if ("-".equals(operation)) { return new Sub(); } return null;
} }  ////////////// 调用者(客户端) public class Main { public static void
main(String[] args) { System.out.println("******计算器********\n请输入第一个数:");
Scanner scanner= new Scanner(System.in); String num1 = scanner.nextLine();
System.out.println("请输入运算符:"); String operation = scanner.nextLine();
System.out.println("请输入第二个数:"); String num2 = scanner.nextLine();
System.out.println("开始计算。。。。。。"); double result = 0; // 类型转换 double a =
Double.parseDouble(num1);double b = Double.parseDouble(num2); Operation oper =
OpreationFactory.getOperation(operation);// TODO 有空指针异常隐患 oper.setNum1(a);
oper.setNum2(b); result= oper.getResult(); System.out.println(num1 + operation
+ num2 + " = " + result); } }

这样写,客户端(调用者)无需反复修改程序,也不需要关注底层实现,调用者只需要简单了解或者指定一个工厂的接口,然后去调用即可一劳永逸,而底层的修改不会影响调用者的代码结构——实现了解耦。且每个操作符类都各司其职,单一职责,看着还可以


但是这时候面试官说了,给我增加开平方运算,回想之前的简单工厂设计模式,每次增加新的产品都需要去修改原来的工厂代码——这样不符合OCP,那么自然想到了工厂方法模式

工厂方法模式版

只举个加法的例子得了
// 将工厂,又抽象了一层 public interface OpreationFactory { Operation getOperation(); }
////// 具体工厂类,生产不同的产品,比如加减乘除,开平方等 public class AddFactory implements
OpreationFactory { @Overridepublic Operation getOperation() { return new Add();
} }  ///// 抽象的产品实体类 public abstract class Operation { private double num1;
private double num2; public double getNum1() { return num1; } public void
setNum1(double num1) { this.num1 = num1; } public double getNum2() { return
num2; }public void setNum2(double num2) { this.num2 = num2; } public abstract
double getResult(); } public class Add extends Operation { @Override public
double getResult() { return this.getNum1() + this.getNum2(); } }   ///////////
客户端 public class Main { public static void main(String[] args) {
System.out.println("******计算器********\n 请输入第一个数:"); Scanner scanner = new
Scanner(System.in); String num1= scanner.nextLine(); System.out.println(
"请输入运算符:"); String operation = scanner.nextLine(); System.out.println("请输入第二个数:"
); String num2= scanner.nextLine(); System.out.println("开始计算。。。。。。"); double
result = 0; // 类型转换 double a = Double.parseDouble(num1); double b =
Double.parseDouble(num2);// TODO 这里又需要判断了 if ("+".equals(operation)) { // 得到加法工厂
OpreationFactory opreationFactory =new AddFactory(); // 计算 + Operation oper =
opreationFactory.getOperation(); oper.setNum1(a); oper.setNum2(b); result=
oper.getResult(); }// ...... System.out.println(num1 + operation + num2 + " = "
+ result); } }
工厂方法模式虽然避免了每次扩展运算符的时候,都修改工厂类,但是把判断的业务逻辑放到了客户端里,各有缺点吧……不要为了面向对象而面向对象。

改进的工厂方法模式版

不过,还是可以改进的……使用反射动态加载类 + 配置文件/注解 等等,且对重复代码进行提炼和封装……其实框架就这么一步步来的。

代码如下(+和-):
public interface OpreationFactory { Operation getOperation(); } public class
AddFactoryimplements OpreationFactory { @Override public Operation
getOperation() {return new Add(); } } public class SubFactory implements
OpreationFactory { @Overridepublic Operation getOperation() { return new Sub();
} }public abstract class Operation { private double num1; private double num2;
public double getNum1() { return num1; } public void setNum1(double num1) { this
.num1 = num1; } public double getNum2() { return num2; } public void setNum2(
double num2) { this.num2 = num2; } public abstract double getResult(); } public
class Add extends Operation { @Override public double getResult() { return this
.getNum1() +this.getNum2(); } } public class Sub extends Operation { @Override
public double getResult() { return this.getNum1() - this.getNum2(); } }
////////////// 封装了一些操作 public enum Util { MAP { // TODO 写在配置文件里 @Override
public Map<String, String> getMap() { Map<String, String> hashMap = new
HashMap<>(); hashMap.put("+", "compute.object.AddFactory"); hashMap.put("-",
"compute.object.SubFactory"); return hashMap; } public double compute(double a,
double b, OpreationFactory opreationFactory) { Operation oper =
opreationFactory.getOperation(); oper.setNum1(a); oper.setNum2(b);return
oper.getResult(); } };public abstract Map<String, String> getMap(); public
abstract double compute(double a, double b, OpreationFactory opreationFactory);
}//////////// 客户端 public class Main { private static double result = 0; private
static double a; private static double b; public static void main(String[] args)
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
System.out.println("******计算器********\n 请输入第一个数:"); Scanner scanner = new
Scanner(System.in); String num1= scanner.nextLine(); System.out.println(
"请输入运算符:"); String operation = scanner.nextLine(); System.out.println("请输入第二个数:"
); String num2= scanner.nextLine(); System.out.println("开始计算。。。。。。"); a =
Double.parseDouble(num1); b= Double.parseDouble(num2); Class clazz =
Class.forName(MAP.getMap().get(operation)); result= MAP.compute(a, b,
(OpreationFactory) clazz.newInstance()); System.out.println(num1+ operation +
num2 + " = " + result); } }
到这里也差不多了,虽然还有很多问题……关键是思想的掌握 

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信