1.为啥需要工厂模式(why)
在我平常的工作中我们往往会写下如下的代码。
public Pizza orderPizza(String type) {
Pizza pizza = new Pizza();
//以下是变化的部分
if (type.equals("chesss")) {
pizza = new ChessPizza();
} else if (type.equals("clam")) {
pizza = new CalmPizza();
}
pizza.bake();
return pizza;
}
以上代码的问题在于我们要增加种类的时候,需要打开这段代码进行修改。这样造成系统难以维护,也更容易犯错。
我们对拓展开放,对修改关闭。通常这种情况下,应该抽象出变化的部分。
2.如何实现工厂模式。
- 我们首先实现一个简单工厂模式
- 抽象出变化的部分,就是工厂
public class SimplePIizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza = null;
if (type.equals("ChessPizza")) {
pizza = new ChessPizza();
} else if (type.equals("CalmPizza")) {
pizza = new CalmPizza();
}
return pizza;
}
}
- 调用的时候只需要用简单工厂生成就行
public class PizzaStroe {
SimplePIizzaFactory simplePIizzaFactory;
public PizzaStroe(SimplePIizzaFactory simplePIizzaFactory) {
this.simplePIizzaFactory = simplePIizzaFactory;
}
public Pizza orderPizza(String type) {
Pizza pizza =simplePIizzaFactory.createPizza(type);
pizza.bake();
return pizza;
}
}
上面这样做的好处在于抽象出变化的部分,供其他部分调用 。另外,当我们需要修改的时候,只需要修改工厂类,其他的部分就不需要变动。
- 工厂模式(真正意义上的工厂模式)
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
- 定义一个基本的工厂基类和它的子工厂类
//定义抽象的Pizza类
public abstract class Pizza {
public String name;
public ArrayList<String> ingredent=new ArrayList<>();
void prepare(){
System.out.println("Prepare: "+name);
for(int i=0;i<ingredent.size();i++){
System.out.print(ingredent.get(i)+" ");
}
}
void bake(){
System.out.println("Pizza is baking");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ArrayList<String> getIngredent() {
return ingredent;
}
public void setIngredent(ArrayList<String> ingredent) {
this.ingredent = ingredent;
}
}
public abstract class PizzaStroe {
public Pizza orderPizza(String type) {
//不用管子类是什么
Pizza pizza = createPizza(type);
pizza.bake();
return pizza;
}
//定义抽象方法,由子类来实现
public abstract Pizza createPizza(String type);
}
//武汉的工厂类,生产出武汉style的Pizza
public class WuhanPizzaStore extends PizzaStroe {
@Override
public Pizza createPizza(String type) {
Pizza pizza = null;
if (type.equals("CalmPizza")) {
pizza = new CalmPizza();
pizza.ingredent.add("WuhanStyle");
} else if (type.equals("ChessPizza")) {
pizza = new ChessPizza();
pizza.ingredent.add("WuhanStyle");
}
return pizza;
}
}
//北京的工厂类,生产出北京style的Pizza
public class BeijingPizzaStore extends PizzaStroe {
@Override
public Pizza createPizza(String type) {
Pizza pizza = null;
if (type.equals("CalmPizza")) {
pizza = new CalmPizza();
pizza.ingredent.add("BeijingStyle");
} else if (type.equals("ChessPizza")) {
pizza = new ChessPizza();
pizza.ingredent.add("BeijingStyle");
}
return pizza;
}
}
- 当我们需要Pizza的时候,只需要用那个工厂类就可以构造出那种风格的Pizza
public class PizzaTest {
public static void main(String[] args) {
PizzaStroe beijingPizzaStore=new BeijingPizzaStore();
PizzaStroe wuhanPizzaStore=new WuhanPizzaStore();
Pizza pizza1=beijingPizzaStore.orderPizza("CalmPizza");
Pizza pizza2=wuhanPizzaStore.orderPizza("CalmPizza");
System.out.println(pizza1.getName());
System.out.println(pizza2.getName());
}
}
来看下类图的关系
简单工厂与工厂方法的区别:简单工厂把全部的事情在一个地方都处理完了,然而工厂方法却是创建一个框架,让子类决定如何实现。
简单工厂的做法,可以将对象的创建封装起来,但是简单工厂不具备工厂方法的弹性,因为简单工厂不能变更正在创建的产品。
- 抽象工厂模式
首先给出定义
抽象工厂模式 提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类
- 首先我们定义抽象接口
public interface PropertyFactory {
public String getName();
public ArrayList<String> getIngredent();
}
在实际的场合中,抽象接口里面的抽象方法都是返回某个想要创建的抽象类,我这里比较简单,直接返回字符串和集合。之所以称为抽象工厂,我们从这里就可以看出来。通过抽象的方法,返回你希望得到的类的基类。
- 我们构造抽象工厂类的实现类
public class CalmPropertyFactory implements PropertyFactory {
@Override
public String getName() {
return "NewCalmPizza";
}
@Override
public ArrayList<String> getIngredent() {
ArrayList<String> arrayList=new ArrayList<>();
arrayList.add("Newcalm");
return arrayList;
}
}
public class ChessPropertyFactory implements PropertyFactory {
@Override
public String getName() {
return "NewChessPizza";
}
@Override
public ArrayList<String> getIngredent() {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("Newchess");
return arrayList;
}
}
那么此时的Pizza的子类应该如此构造
public class CalmPizza extends Pizza {
PropertyFactory propertyFactory;
public CalmPizza(PropertyFactory propertyFactory) {
this.propertyFactory=propertyFactory;
name=propertyFactory.getName();
ingredent=propertyFactory.getIngredent();
}
}
直接用对应的原料工厂来构造。
来看下类图。
- 个人总结(conclusion)
通过以上的描述,我们很容易明白工厂模式,也很容易区分简单工厂和工厂模式的区别(即构造类是在一个工厂类里面统一构造,还是通过子类的工厂方法来构造)。但是令人迷惑的是工厂模式与抽象工厂模式有什么区别。区别如下:
抽象工厂的每个方法实际上看起来都像是工厂方法,每个方法都被声明成抽象。而子类的方法来覆盖这些方法来创建某些对象。注意到,这里的子类是想要构造类的成分,就像我上面的写的name和ingredent成员。Pizza的子类只需要依赖依赖这些成分工厂类,例如ChessPropertyFactory ,就能构造出对象。重点突出抽象,是因为它有一个总的抽象接口,实现它的子类负责构造对象的成分,总而构成对象。