三个工程都是创建型的模式,主要是为了用来解决对象的实例化问题,但是针对的问题不同。
首先看一个最原始的没有工厂,就是家庭作坊,自给自足,如下的代码,每个披萨定都自己根据参数决定实例化那个披萨。
public class PizzaStore {
//根据传来的参数决定实例化那个对象
public Pizza orderPizza(String type) {
if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("pepperoni")) {
pizza = new PepperoniPizza();
} else if (type.equals("clam")) {
pizza = new ClamPizza();
} else if (type.equals("veggie")) {
pizza = new VeggiePizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
如果有多家披萨店,这样就会有代码冗余,当增加披萨种类的时候每个披萨店类都需要修改,违反开闭原则,复用性也不好。
随着时代的发展人们开始意识到这一的效率比较低,开始出现早期的工厂——简单工厂,看看能解决什么问题,首先看一下简单工厂的类图,在披萨店和披萨之间多了一个简单工厂。
对应的代码如下:
//披萨店,可以有多个,每个都使用简单工厂
public class PizzaStore {
SimplePizzaFactory factory; //关联的体现,定义一个简单工厂类型的成员变量
public PizzaStore(SimplePizzaFactory factory) { //进行初始化
this.factory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = factory.createPizza(type); //调用简单工厂的方法,实现披萨的实例化
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
//简单工厂类
public class SimplePizzaFactory {
public Pizza createPizza(String type) { //实例化披萨的方法,根据传来的字符进行实例化
Pizza pizza = null;
//里面实例化的类都是继承了Pizza类的子类
if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("pepperoni")) {
pizza = new PepperoniPizza();
} else if (type.equals("clam")) {
pizza = new ClamPizza();
} else if (type.equals("veggie")) {
pizza = new VeggiePizza();
}
return pizza;
}
}
改进:将实例化披萨那一部分容易变化的代码提取出来,放在简单工厂类中。
作用:解决了“家庭作坊”的问题,将类的实例化过程抽象出来放在简单工厂中如上图的SimplePizzaFactory中,可以供多个披萨店使用,在没有这个工厂之前每家PizzaStore都需要自己制作披萨,现在所有的都只需要调用简单工厂就可以了。提高代码复用性。
缺点:并没有解决违反开闭原则的问题,如果披萨种类增加还是需要修改简单工厂类。
简单工厂能够解决一些问题,当披萨的子类比较确定的时候使用也是很不错的,但是人们的需求总是在随着时代变化的,所以简单工厂还是不能满足人们的需求,就开始出现另外一种解决办法——工厂方法。
//抽象的披萨店
public abstract class PizzaStore {
abstract Pizza createPizza(String item);
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
System.out.println("--- Making a " + pizza.getName() + " ---");
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
//纽约店
public class NYPizzaStore extends PizzaStore {
Pizza createPizza(String item) {
if (item.equals("cheese")) {
return new NYStyleCheesePizza();
} else if (item.equals("clam")) {
return new NYStyleClamPizza();
} else return null;
}
}
//芝加哥店
public class ChicagoPizzaStore extends PizzaStore {
Pizza createPizza(String item) {
if (item.equals("cheese")) {
return new ChicagoStyleCheesePizza();
} lse if (item.equals("clam")) {
return new ChicagoStyleClamPizza();
}else return null;
}
}
工厂方法定义一个抽象的披萨店,由一个抽象的方法createPizza()在子类继承后进行实现,每个子类的具体实现不同,这样讲类的实例化推迟到子类。
需要注意的是,这里的例子在子类店进行实例化的时候还是用的if语句,这和简单工厂是一样的,当披萨增加种类的时候还是需要修改子类店,这里其实是工厂方法和简单工厂的结合使用,在子类店中可以不使用if语句进行实例化,而是对每种披萨都专门建立一个子店类进行实例化。结合使用可以避免产生太多类。
随着工业化的发展,工厂就会愈来愈全面化不仅仅有生产披萨的工厂,开始有生产披萨原材料的工厂,但是这就会有地域差距,例如同时成产面饼,在纽约要薄一些在芝加哥要厚一些的,还有酱料的差别等。
抽象工厂:解决的是不同“品牌”的一组相同类的实例化问题。
就如上图有一个抽象的原料工厂,在子类中有一个是生成NY(纽约)风味的,还有一个子类是生产Chicago(芝加哥)风味的,相同都是生产奶酪和面团,NY的可能是薄的面团和NY味的奶酪,而芝加哥是自己的风味的。
小结:
简单工厂放在增加产品种类的时候需要修改简单工厂的代码,违反开闭原则
工厂方法将每个类的实例化放在子类里,当增加对应的产品种类时,只需增加对应的工厂方法的子类即可。当二者可以结合使用,极少工厂方法子类的数量。
抽象工厂解决的是一组类的实例化问题而且还是不同“品牌”的。