工厂模式
工厂设计模式是软件开发中经常涉及到的一种设计方法,这里我们从浅入深一步一步介绍工厂模式的三种设计方法,即:简单工厂模式,工厂方法,抽象工厂。
1、需求
为了便于理解,这里引入一个具体的需求,完成披萨店订阅功能,这个披萨项目要便于披萨种类的扩展和维护。具体要求如下:
- 披萨的种类很多(比如GreekPizz、CheessPizz等)
- 披萨的制作有prepare,bake,cut,box四个过程
- 披萨店订购功能
2、传统方式完成
首先我们不考虑工厂模式,看是如何解决这个问题的。
先看类图:

非常直接,只需完成Pizza相关类及OrderPizza订购需要的披萨即可。注意这里OrderPizza就相当于客户端了。
代码如下:
披萨相关类
//披萨抽象类
public abstract class Pizza {
protected String name;
//准备原材料,不同披萨有不同材料
public abstract void prepare();
public void bake(){
System.out.println(name+" baking ");
}
public void cut(){
System.out.println(name+" cutting");
}
public void box(){
System.out.println(name+" boxing");
}
public void setName(String name){
this.name=name;
}
}
//奶酪披萨
public class CheessPizza extends Pizza {
@Override
public void prepare() {
System.out.println("给制作奶酪披萨准备原材料 ");
}
}
//希腊披萨
public class GreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println("给制作希腊披萨准备原材料 ");
}
}
披萨订购类
public class OrderPizza {
//构造器
public OrderPizza(){
Pizza pizza=null;
String orderType;//订购披萨的类型
do {
orderType=getType();
if (orderType.equals("greek")){
pizza=new GreekPizza();
pizza.setName("希腊披萨");
}else if(orderType.equals("cheess")){
pizza=new CheessPizza();
pizza.setName("奶酪披萨");
}else {
break;
}
//输出披萨制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}while (true);
}
//获取客户订购的披萨类型
private String getType() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入披萨类型: ");
String s = null;
s = in.readLine();
return s;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
披萨店订购
public class PizzaStore {
public static void main(String[] args) {
new OrderPizza();
}
}
优缺点分析:
这样做的优点是比较好理解,简单易操作
缺点是违反了设计模式的ocp原则,即对扩展开放,对修改关闭。比如,当新增一个披萨种类时,需要修改OrderPizza这个类。
此外,我们注意到OrderPizza这个类中有多处创建Pizza的地方,那么我们可以考虑把创建Pizza对象封装到一个类中,这样当有新的Pizza种类时,只需修改该类即可。其它地方有创建到Pizza对象的地方都不需要修改。
再细想下,这样做有点多此一举,注意关键这时候你要明白客户端是OrderPizza这个类,其只关心披萨种类,不关心怎样产生这些Pizza。对客户端来说,我只要我想要的Pizza。
由此,就可以引出简单工厂了,我们造个工厂方法,专门生产Pizza,OrderPizza这个类只需调用工厂方法就可以得到想要的Pizza。
3、简单工厂模式
简单工厂模式属于创建型模式,是由一个工厂对象决定创建出哪一种产品类的实例。
对上面的代码进行改进,如下:

创建简单工厂类
public class SimpleFactory {
Pizza pizza=null;
public Pizza createPizza(String orderType){
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName(" 希腊披萨 ");
} else if (orderType.equals("cheese")) {
pizza = new CheessPizza();
pizza.setName(" 奶酪披萨 ");
}
return pizza;
}
}
修改OrderPizza类
public class OrderPizza {
SimpleFactory simpleFactory;
Pizza pizza=null;
public OrderPizza(SimpleFactory simpleFactory){
setFactory(simpleFactory);
}
private void setFactory(SimpleFactory simpleFactory) {
String orderType="";//用户输入
this.simpleFactory=simpleFactory;
do {
orderType=getType();
pizza=this.simpleFactory.createPizza(orderType);
if(pizza!=null){//订购成功
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}else {
System.out.println("订购失败");
break;
}
}while (true);
}
}
4、工厂方法模式
现在项目需求增加了,要求能订不同地方口味的披萨,比如北京的奶酪pizza,北京的希腊pizza,或伦敦的奶酪披萨,伦敦的希腊披萨。这样我们可以将工厂方法抽象出来,让不同的地方实现不同的工厂方法。具体如下:

修改OrderPizza代码为抽象类:
public abstract class OrderPizza {
//定义一个抽象方法,createPizza , 让各个工厂子类自己实现
abstract Pizza createPizza(String orderType);
// 构造器
public OrderPizza() {
Pizza pizza = null;
String orderType; // 订购披萨的类型
do {
orderType = getType();
pizza = createPizza(orderType); //抽象方法,由工厂子类完成
//输出pizza 制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
}
继承OrderPizza类实现各自的工厂:
//北京工厂
public class BJOrderPizza extends OrderPizza {
@Override
Pizza createPizza(String orderType) {
Pizza pizza = null;
if(orderType.equals("cheese")) {
pizza = new BJCheessPizza();
} else if (orderType.equals("greek")) {
pizza = new BJGreekPizza();
}
// TODO Auto-generated method stub
return pizza;
}
}
//伦敦工厂
public class LDOrderPizza extends OrderPizza {
@Override
Pizza createPizza(String orderType) {
Pizza pizza = null;
if(orderType.equals("cheese")) {
pizza = new LDCheessPizza();
} else if (orderType.equals("greek")) {
pizza = new LDGreekPizza();
}
// TODO Auto-generated method stub
return pizza;
}
}
4、抽象工厂模式
现在需求继续改动,不止一家能够订购披萨,而是好多地方都要用到OrderPizza这个类,那么我们再进一步封装,将整个制作各地披萨的工厂都给抽象出来,让不同的OrderPizza来调用即可,注意,这里与工厂方法最大的不同就是,在工厂方法中只有一处用到OrderPizza,而在抽象工厂中则是多处。好了,看类图:

工厂接口:
//一个抽象工厂的抽象层
public interface AbsFactory {
//让下面的工厂子类来 具体实现
public Pizza createPizza(String orderType);
}
//实现工厂接口的北京工厂
public class BJFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("~使用的是抽象工厂模式~");
// TODO Auto-generated method stub
Pizza pizza = null;
if(orderType.equals("cheese")) {
pizza = new BJCheessPizza();
} else if (orderType.equals("GREEK")){
pizza = new BJGreekPizza();
}
return pizza;
}
}
//实现工厂接口的伦敦工厂
public class LDFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("~使用的是抽象工厂模式~");
// TODO Auto-generated method stub
Pizza pizza = null;
if(orderType.equals("cheese")) {
pizza = new LDCheessPizza();
} else if (orderType.equals("greek")){
pizza = new LDGreekPizza();
}
return pizza;
}
}
披萨订购类OrderPizza修改:
public class OrderPizza {
AbsFactory factory;
// 构造器
public OrderPizza(AbsFactory factory) {
setFactory(factory);
}
private void setFactory(AbsFactory factory) {
Pizza pizza = null;
String orderType = ""; // 用户输入
this.factory = factory;
do {
orderType = getType();
// factory 可能是北京的工厂子类,也可能是伦敦的工厂子类
pizza = factory.createPizza(orderType);
if (pizza != null) { // 订购ok
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("订购失败");
break;
}
} while (true);
}
}
5、总结
在上面的过程中,我们通过设计披萨订购这个需求来完成工厂设计模式的改进。
最初时,不考虑工厂模式,客户端直接自己创建披萨进行订购,这在一个客户端的情况下是好使的,如果有多个客户端来订购披萨,那么创建Pizza这个过程就会存在大量重复,因此,我们引入Pizza工厂专门生产Pizza,这样各个客户端都可以直接通过Pizza工厂来创建Pizza即可,这就是简单工厂模式。
进一步时,生产Pizza的工厂不止一个,而是多个,因此我们将工厂给抽象化,让各个子工厂去继承实现即可,注意,这个时候是只有一个客户端来调用的,因此,可以直接对工厂抽象化进行调用,这就是工厂方法。
与简单工厂相比,工厂方法中的工厂是多个的,客户端只有一处调用,每个工厂有自己的实现,因此将工厂抽象,而在简单工厂中只有一个工厂,但是存在多个客户端调用。
最后,工厂不止一个,客户端也不止一个,我们就将整个工厂簇就行抽象化,可以让多个客户端来进行调用,这就是抽象工厂。
通过上面的分析,我们可以看出抽象工厂是对简单工厂和工厂方法的整合。
往期回顾
更多精彩,关注“咋家”

网友评论