项目实例

假设你有一家 pizza 店,你有很多种 pizza,要在系统中显示你所有 pizza 种类。实现这个功能并不难,使用普通方式实现:

public class PizzaStore {
Pizza orderPizza(String type) {
  Pizza pizza = null;
  if (type.equals("cheese")) {
    pizza = new CheesePizza();
  } 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;
  }
}

这种把选择披萨和制作过程全放在一起,如果新推出一款披萨就要修改 OrderPizza 方法,不符合开闭原则。

我们可已经这段变化的代码移到一个专门的类中,这个类只管创建 Pizza,我们将这个类的对象称之为“工厂”;

简单工厂(factory)

根据“将变化抽离并封装“的原则,我们可以将创建 pizza 实例这一块给抽离出来,因为这块后边可能会新增一些别的 pizza 类型,由一个对象来专职创建 pizza 对象。如果有一个 SimplePizzaFactory,那么 orderPizza() 就变成此对象的客户;

当客户下单,pizzaStore 调用 orderPizza() 的时候,就叫比萨工厂做一个,orderPizza() 只关心从工厂得到一个比萨;

public class SimplePizzaFactory {
public PizzaCreatePizza(string pizzaType){
Pizza pizza = null;
if(pizzaType.Equals("cheese"))
pizza = newCheesePizza();
else if (pizzaType.Equals("pepperoni"))
pizza = newPepperoniPizza();
else if(pizzaType.Equals("clam"))
pizza = newCalmPizza();
else if(pizzaType.Equals("veggie"))
pizza = newVeggiePizza();
return pizza;
}
}

接下来看下 Pizza 类:

public class Pizza {
Pizza OrderPizza(stringpizzaType){
Pizza pizza;
SimplePizzaFactory simplePizzaFactory = new SimplePizzaFactory();//生成pizza
pizza =simplePizzaFactory.CreatePizza(pizzaType);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}

这样的话,就可以遵循了把变化的部分抽象成一个类。下次需要变化,只需要对变化的类的部分做修改即可。

下面给出其UML图。

简单工厂的目的,主要负责实现生产对象的细节,根据订单来生产。每一个商店有对应着自己的工厂。在此,可以将 OrderPizza 是工厂的一个客户,当然还可以是其他类似的方法,比如 PizzaShopMenu 也可以是工厂的客户。客户就是为了获取工厂生成的对象。前者是为了卖给 people,后者是为了使用 Pizza 类获取其描述和价格等。这样遵循了找出变化的部分,将其封装到一个类中的原则,从而到达了复用的目的。

简单工厂之所以简单是因为它只根据需要,来生产出来指定的 Pizza。

特点:所有的产品都由一个工厂来生产。

上面的简单工厂并不是一个真正的模式,只是一种编程习惯,这个不能算工厂模式,不过也算是基本满足需求。

接下来我们来看看工厂方法的实现。

工厂方法

背景更新:

假如现在你要开分店,各种加盟商进来后,他们都要开发符合本地口味的 pizza,那就需要各个地方都有一个工厂,也就是每个地方继承 SimpleFactory类,但是每个工厂并不是完全使用你原来的烘培方法。或许,我们可以像 1 中那样利用简单的工厂,对应不同地区的加盟店创建不同的工厂。

这样做导致的另一个问题就是,不同的加盟店披萨的制作流程、方法可能不同,如何才能把加盟店和创建披萨捆绑在一起的同时又保持一定的弹性?

我们可以把 CreatePizza()方法放回到 PizzaStore 中,但是要把它设置为抽象方法,然后为每一个区域加盟店创建一个 PizzaStore 的子类。

如下所示:

对应的代码如下:

public abstract class PizzaStore  {
public PizzaOrderPizza(string pizzaType) {
Pizza pizza =CreatePizza(pizzaType);
pizza.Prepare();
pizza.Bake();
pizza.Cut();
pizza.Box();
return pizza;
} public abstract Pizza CreatePizza(string pizzaType); //把工厂对象移到该方法中,该方法为抽象方法
}

对应于两家分店的代码:

public class NYPizzaStore extends PizzaStore {
  @Override
  protected Pizza createPizza(String type) {
    if (type.equals("cheese")) {
      return new NYCheesePizza();
    }
    return null;
  }
} public class ChicagoPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
if (type.equals("cheese")) {
      return new ChicagoCheesePizza();
    }
    return null;
  }
}

我们需要建立一个 Pizza 实体类:

public abstract class Pizza {
String name; //名称
String dough; //面团类型
String sauce; //酱料
ArrayList<String> toppings = new ArrayList<String>(); //作料 void prepare() {
System.out.println("准备 " + name);
System.out.println("揉面团...");
System.out.println("添加酱料...");
System.out.println("添加作料: ");
for (int i = 0; i < toppings.size(); i++) {
System.out.println(" " + toppings.get(i));
}
}
void bake() {
System.out.println("烘烤25分钟");
}
void cut() {
System.out.println("把Pizza对角切片");
}
void box() {
System.out.println("把Pizza装盒子");
}
public String getName() {
return name;
}
}

然后需要一些具体的子类,下边定义两个子类:纽约风味的芝士披萨(NYStyleCheesePizza)、芝加哥风味的芝士披萨 (ChicageStyleCheesePizza)

public class NYStyleCheesePizza extends Pizza {
public NYStyleCheesePizza() {
name = "NY Style Sauce and Cheese Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Cheese");
}
} public class ChicagoStyleCheesePizza extends Pizza {
public ChicagoStyleCheesePizza() {
name = "Chicago Style Deep Dish Cheese Pizza";
dough = "Extra Thick Crust Dough";
sauce = "Plum Tomato Sauce";
toppings.add("Shredded Mozzarella Cheese");
}
//可以覆盖cut()方法
void cut() {
System.out.println("Cutting the pizza into square slices");
}
}

2.2  总结

所有工厂模式都用来封装对象创建,工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。

创建者(Creator)类

产品类

简单工厂和工厂方法之间的差异?

简单工厂是在一个地方把所有的事都处理完了,然而工厂方法却是创建一个框架,让子类决定要如何实现。简单工厂的做法,可以将对象的创建封装起来,但是简单工厂不具备工厂方法的弹性,因为简单工厂不能变更正在创建的产品。

抽象工厂

创建工厂接口

回到上文的 Pizza 店,现在有新的需求,想要确保每家加盟店使用高质量的材料,打算创建一家生产原料的加工厂,并将原料送到各个加盟店。这个工厂负责创建原料家族中的每一种原料,工厂需要生产面团、酱料、芝士等。先为工厂定义一个接口,该接口负责所有原料:

public interface PizzaIngredientFactory {
Dough CreateDough(); Sauce CreateSauce(); Cheese CreateCheese(); Veggies[] CreateVeggies(); Pepperoni CreatePepperoni(); Clams CreateClam();
}

创建一个原料厂

//具体原料工厂必须实现这个接口
public class NYPizzaIngredientFactory extends PizzaIngredientFactory {
public Dough CreateDough() {
return new ThinCrustDough();
} public Sauce CreateSauce() {
return new MarinaraSauce();
} public Cheese CreateCheese() {
return new ReggianoCheese();
} public Veggies[] CreateVeggies() {
Veggies[] veggies = new Veggies[]{ new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
return veggies;
} public Pepperoni CreatePepperoni() {
return new SlicedPepperoni();
} public Clams CreateClam() {
return new FreshClams();
}
}

重新抽象 Pizza 类

public abstract class Pizza {
public string name;
public Dough dough;
public Sauce sauce;
public Veggies[] veggies;
public Cheese cheese;
public Pepperoni pepperoni;
public Clams clam;
public ArrayList toppings = newArrayList(); public abstract void Prepare();//把Prepare()方法声明成抽象,在这个方法中,我们需要收集Pizza所需的原材料,而这些原材料来自原料工厂。
public void Bake() {
System.Console.WriteLine("Bakefor 25 minutes at 350");
} public void Cut() {
System.Console.WriteLine("Cuttingthe pizza into diagonal slices");
} public void Box() {
System.Console.WriteLine("Placepizza in official PizzaStore box");
} public string GetName() {
return name;
}
}

重新实现 Pizza

public class NYStyleCheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public NYStyleCheesePizza(PizzaIngredientFactoryingredientFactory){
      //制作Pizza需要工厂提供原材料,所以每个pizza类都需要从构造器中得到一个工厂,并将工厂存储在变量中
this.ingredientFactory =ingredientFactory;
name = "NY StyleSauc and Cheese Pizza";
toppings.Add("GratedReggiano Cheese");
} public override void Prepare()
{
System.Console.WriteLine("Preparing" + name);
dough = ingredientFactory.CreateDough();
sauce = ingredientFactory.CreateSauce();
cheese = ingredientFactory.CreateCheese();
} }

重新生产pizza

public class MYPizzaStore extends PizzaStore {
public override Pizza CreatePizza(string type) {
Pizza pizza=null;
PizzaIngredientFactory ingredientFactory= new NYPizzaIngredientFactory();
switch(type) {
case "cheese":
pizza = new NYStyleCheesePizza(ingredientFactory);
break;
case "veggie":
pizza=new NYStyleVeggiePizza(ingredientFactory);
break;
case "clam":
pizza=new NYStyleClamPizza(ingredientFactory);
break;
case "pepperoni":
pizza=new NYStylePepperoniPizza(ingredientFactory);
break;
}
return pizza;
}
}

通过这一系列的操作,我们引入了新类型的工厂,也就是所谓的“抽象工厂”,来创建 pizza 原来家族。通过抽象工厂所提供的接口创建产品家族,利用这个接口书写代码,我们的代码将从实际工厂解耦,以便在不同上下文中实现各式各样的工厂,制造出各种不同的产品。

定义抽象工厂模式

抽象工厂模式提供一个接口,用于创建相关或依赖对象家族,而且不需要致命具体类。

抽象工厂模式允许客户使用抽象的接口来创建一组相关的产品,而不需要知道实际产出的具体产品是什么,客户就从具体的产品中被解耦。

抽象工厂与工厂方法的对比

抽象工厂和工厂方法都是负责创建对象。

抽象工厂是通过对象的组合

定义了一个创建对象的接口,但由于子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。

所以利用工厂方法创建对象时,需要扩展一个类,并覆盖它的工厂方法。

整个工厂方法模式,只不过就是通过子类来创建对象,这种做法,客户只需要知道他们所使用的抽象类型就可以了,而由子类负责决定具体类型。将客户从具体类型中解耦。

工厂方法是继承。

抽象工厂方法是将一群相关的产品集合起来,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

 参考文章

https://blog.csdn.net/xuemoyao/article/details/53437609

https://www.cnblogs.com/lzhp/p/3375041.html

抽象工厂模式详解 —— head first 设计模式的更多相关文章

  1. JAVA 设计模式之 工厂模式详解

    一.简单工厂模式 简单工厂模式(Simple Factory Pattern)是指由一个工厂对象决定创建出哪一种产品类 的实例.属于创建型模式,但它不属于 GOF,23 种设计模式 (参考资料: ht ...

  2. Java设计模式(5:设计模式的分类及工厂模式详解)

    一.设计模式的分类 总的来说,设计模式可以分为三大类:创建型模式.结构型模式.行为型模式,具体如下图: 二.工厂模式 工厂模式分为简单工厂模式.工厂方法模式和抽象工厂模式.其中简单工厂模式并不属于23 ...

  3. JS设计模式——工厂模式详解

    它的领域中同其它模式的不同之处在于它并没有明确要求我们使用一个构造器.取而代之,一个工厂能提供一个创建对象的公共接口,我们可以在其中指定我们希望被创建的工厂对象的类型. 简单工厂模式:使用一个类(通常 ...

  4. java之简单工厂模式详解

    设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于 ...

  5. 设计模式(3)抽象工厂模式(Abstract Factory)

    设计模式(0)简单工厂模式 设计模式(1)单例模式(Singleton) 设计模式(2)工厂方法模式(Factory Method) 源码地址 0 抽象工厂模式简介 0.0 抽象工厂模式定义 抽象工厂 ...

  6. 2.java设计模式-抽象工厂模式

    抽象工厂模式简介: 每一个模式都是针对某一种问题的解决方案.抽象工厂模式与工厂模式最大的区别在于,工厂模式针对的是一个产品等级结构,而抽象工厂模式面对的是多个产品等级结构,即产品族的概念. 这里讲解一 ...

  7. Java开发中的23中设计模式详解(一)工厂方法模式和抽象工厂模式

    一.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接 ...

  8. java开发中的23中设计模式详解--大话设计模式

    设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...

  9. [Python编程实战] 第一章 python的创建型设计模式1.1抽象工厂模式

    注:关乎对象的创建方式的设计模式就是“创建型设计模式”(creational design pattern) 1.1 抽象工厂模式 “抽象工厂模式”(Abstract Factory Pattern) ...

随机推荐

  1. C#LeetCode刷题之#350-两个数组的交集 II(Intersection of Two Arrays II)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4044 访问. 给定两个数组,编写一个函数来计算它们的交集. 输入 ...

  2. 深度优先搜索(dfs)与出题感想

    在3月23号的广度优先搜索(bfs)博客里,我有提到写一篇深搜博客,今天来把这个坑填上. 第一部分:深度优先搜索(dfs) 以上来自百度百科. 简单来说,深度优先搜索算法就是——穷举法,即枚举所有情况 ...

  3. 面试现场:裸面京东JavaT5第一轮电话面试

    人物简介 清辞女朋友,94年资深女程序员一枚,15年非本专业毕业,有过1年测试经验,3年Java开发经验,之前有个1年大公司的工作经历,2年的车联网公司经历,每天打卡式工作,朝九晚七.工作之余没看过一 ...

  4. 远程控制(远控Bin)

    一.概念 控制端:运行在攻击者的电脑中,负责控制其他肉鸡: 被控制端:需要去生成,运行在肉鸡上,木马病毒 二.使用远控Bin工具实现内网远程控制 Step1:打开Kill.exe,点击系统设置,修改监 ...

  5. SparkStreaming架构

    SparkStreaming是一个对实时数据流进行高通量.容错处理的流式处理系统,可以对多种数据源(如Kdfka.Flume.Twitter.Zero和TCP 套接字)进行类似Map.Reduce和J ...

  6. 配置react / antd 按需加载 并且使用less(react v16)

    1.开启项目   并且执行 yarn eject 下载好我们需要的插件(babel-plugin-import   less  less-loader   antd  react-loadable   ...

  7. WARNING: The host 'db01' could not be looked up with /data/mysql/bin/resolveip. This probably means that your libc libraries are not 100 % compatible with this binary MySQL version......

    Linux系统安装MySQL,环境参数: 硬件配置:CPU: Xeon(R) CPU E5-2650 v4 @ 2.20GHz 8核内存:16G硬盘:系统盘200GB 数据盘1TB 操作系统CentO ...

  8. 仿京东BOE官网 JavaScript代码

    let items = document.getElementsByClassName('item'); let points = document.getElementsByClassName('p ...

  9. Federated Learning: Challenges, Methods, and Future Directions

    郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! arXiv:1908.07873v1 [cs.LG] 21 Aug 2019 Abstract 联邦学习包括通过远程设备或孤立的数据中心( ...

  10. 牛客网数据库SQL实战解析(1-10题)

    牛客网SQL刷题地址: https://www.nowcoder.com/ta/sql?page=0 牛客网数据库SQL实战解析(01-10题): https://blog.csdn.net/u010 ...