这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战
原料供应问题
工厂方法模式为我们提供了一种利用工厂获得不同类产品的方法,解决了分店时工厂压力过大的问题。现在,我们需要为不同的 PizzaStore 提供不同的原料。若果我们继续使用工厂方法模式那么我们大概会这样写。
abstract class Dough {
abstract String name;
}
class ThickCrustDough extends Dough {
// ...
}
class ThinCrustDough extends Dough {
// ...
}
abstract class DoughFactory extends IngredientFactory {
Dough createDough(String type) {
if(...){
return new ThickCrustDough();
} else if(...){
return new //...
}
}
}
abstract class SauceFactory extends IngredientFactory {
//...
}
//...
当我们的原料变得越来越多,利用工厂方法模式编写就会变得复杂,我们需要定义多种原料的工厂,并且它们与原料工厂有着复杂的继承关系,这使得整个程序的耦合性大大的提高,复杂的继承关系不利于后面代码的维护,同时也违反了一个重要的原则 —— 对扩展开放、对修改关闭。为了解决这个问题,我们需要用组合来替代复杂的继承。
我们发现,每个地方都提供符合当地特色的原料家族来供应当地的 Pizza 店,我们可以对应建造不同区域的 IngredientFactory 来提供不同的原料家族。这就是抽象工厂模式——提供一个接口,用于创建相关或依赖对象的家族,而不需要指明具体类。这个工厂类时抽象的,因此命名为抽象工厂模式。
首先是一个 Pizza 的具体类来规定 Pizza 的组成和操作。
public abstract class Pizza {
private String name;
public Dough dough;
public Sauce sauce;
Veggies veggies[];
Cheese cheese;
Clams clams;
abstract void prepare();
void bake() {
System.out.println("bake");
}
void cut() {
System.out.println("cut");
}
void box() {
System.out.println("box");
}
void setName(String name) {
this.name = name;
}
String getName() {
return this.name;
}
}
然后我们定义原料
abstract class Dough {
abstract String getName();
}
abstract class Cheese {
abstract String getName();
}
abstract class Sauce {
abstract String getName();
}
abstract class Clams {
abstract String getName();
}
abstract class Veggies {
}
//具体原料
class ThickCrustDough extends Dough {
@Override
String getName() {
return "ThickCrustDough";
}
}
class ThinCrustDough extends Dough {
@Override
String getName() {
return "ThinCrustDough";
}
}
class MarinaraSauce extends Sauce {
@Override
String getName() {
return "MarinaraSauce";
}
}
class PlumTomatoSauce extends Sauce {
@Override
String getName() {
return "PlumTomatoSauce";
}
}
class MozzarellaCheese extends Cheese {
String getName() {
return "MozzarellaCheese";
}
}
class FeshClams extends Clams {
@Override
String getName() {
return "FleshClams";
}
}
class FrozenClams extends Clams {
@Override
String getName() {
return "FrozenClams";
}
}
class Onion extends Veggies {
//...
}
class Garlic extends Veggies {
//...
}
然后定义原料工厂的抽象类,作为调用工厂的统一接口。
public interface IngredientFactory {
Dough createDough();
Sauce createSauce();
Cheese createCheese();
Veggies[] createVeggies();
Clams createClams();
}
然后是各个地方工厂的具体类
//Chicago原料工厂
public class ChicagoIngredientFactory implements IngredientFactory {
@Override
public Dough createDough() {
return new ThickCrustDough();
}
@Override
public Sauce createSauce() {
return new PlumTomatoSauce();
}
@Override
public Cheese createCheese() {
return new MozzarellaCheese();
}
@Override
public Veggies[] createVeggies() {
return new Veggies[]{new Garlic(), new Mushroom()};
}
@Override
public Clams createClams() {
return new FrozenClams();
}
}
//NewYork原料工厂
public class NewYorkIngredientFactory implements IngredientFactory {
@Override
public Dough createDough() {
return new ThinCrustDough();
}
@Override
public Sauce createSauce() {
return new MarinaraSauce();
}
@Override
public Cheese createCheese() {
return new ReggianoCheese();
}
@Override
public Veggies[] createVeggies() {
return new Veggies[]{new Garlic(), new Onion(), new Mushroom()};
}
public Clams createClams() {
return new FeshClams();
}
}
PizzaStore 的抽象类,用来处理 pizza 的订单和获得信息
abstract class PizzaStore {
void orderPizza(String type) {
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
System.out.println(this.getInfo(pizza));
}
private String getInfo(Pizza pizza) {
return "Ingredients are " + pizza.dough.getName() + ", " + pizza.sauce.getName();
}
protected abstract Pizza createPizza(String type);
}
下面是 PizzaStore 的具体类,制作不同种类 Pizza 的具体执行者,并获得从和构造器中的原料工厂得到的原料。
//ChicagoPizzaStore
public class ChicagoPizzaStore extends PizzaStore {
private Pizza pizza = null;
@Override
protected Pizza createPizza(String item) {
IngredientFactory pizzaIngredientFactory = new ChicagoIngredientFactory();
if (item.equals("cheese")) {
pizza = new CheesePizza(pizzaIngredientFactory);
pizza.setName("Chicago style cheese Pizza");
} else if (item.equals("clam")) {
pizza = new ClamPizza(pizzaIngredientFactory);
pizza.setName("Chicago style clam Pizza");
}
return pizza;
}
}
//NewYorkPizzaStore
class NewYorkPizzaStore extends PizzaStore {
private Pizza pizza = null;
protected Pizza createPizza(String item) {
IngredientFactory pizzaIngredientFactory = new NewYorkIngredientFactory();
if (item.equals("cheese")) {
pizza = new CheesePizza(pizzaIngredientFactory);
pizza.setName("New York Style Cheese Pizza");
} else if (item.equals("clam")) {
pizza = new ClamPizza(pizzaIngredientFactory);
pizza.setName("New York Style Clam Pizza");
}
return pizza;
}
}
测试我们的 PizzaStore
public class AbstractFactoryTest {
public static void main(String[] args) {
NewYorkPizzaStore newYorkPizzaStore = new NewYorkPizzaStore();
ChicagoPizzaStore chicagoPizzaStore = new ChicagoPizzaStore();
newYorkPizzaStore.orderPizza("cheese");
System.out.println("----next-----");
newYorkPizzaStore.orderPizza("clam");
System.out.println("----next-----");
chicagoPizzaStore.orderPizza("cheese");
System.out.println("----next-----");
chicagoPizzaStore.orderPizza("clam");
System.out.println("----next-----");
}
}
得到最终结果
模式结构
AbstractFactory:抽象工厂,创建产品类调用的统一接口
ConcreteFactory:具体工厂,负责提供需要的具体对象
AbstractProduct:抽象产品
Product:具体产品
模式分析
优点
相比于工厂方法利用一个抽象接口创建产品,并用具体类决定实例化那一个具体类,抽象工厂模式则提供一个抽象接口来创建一个产品家族,而每个具体工厂负责创建具体产品方法。另外与工厂方法的继承不同的是抽象工厂模式更多的通过组合的方式创建一组对象的集合。
抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。
当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
缺点
在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。
抽象工厂模式的应用情况
- 系统中有多于一个的产品族,而每次只使用其中某一产品族。
- 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
- 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
结
工厂模式在作为一种创建型模式的主要功能就是在一个高内聚低耦合的前提下提供不同的实例化对象,这使得代码结构更加层次化,便于后期的扩展,在实际情况中根据需要选择不同的工厂模式。