先编一个这么久不写的理由
上周我终于鼓起勇气翻开了 headfirst 设计模式这本书, 看看自己下一个设计模式要写个啥, 然后, 我终于知道我为啥这么久都没写设计模式了, headfirst 的这个抽象工厂模式, 额, 我看了好几次, 都不太理解.
在我的印象中, 简单工厂, 工厂方法, 抽象工厂, 这三个东西应该是层层递进的, 然后我带着这个思路去看, emmmm, 真的没看懂, 还好最近又补了一遍《大话设计模式》, 揣着刚刚温习了的新知识, 然后又上了 headfirst 这条船, 我感觉我这次应该是看懂了.
所以不写并不是因为忙, 也不是因为懒, 什么上分和吃鸡, 我根本都没有听说过, 和一个 996 的码农说这些, 表示很扎心.
看完了工厂模式以后, 发现后面居然是单例模式, 兄弟, 你是认真的吗?(泪流满面)
抽象工厂举例分析
首先, 我去看了一下这本书配套的源代码, 嗯, 抽象工厂的类, 感觉太多太多了, 带上测试类一共是 35 个, 这要是贴出来, 可以水很大的篇幅了, 但是我是那种人吗? 当然不是!
碰到这种情况当然是直接给地址, 有需要的小伙伴可以自己去看
GitHub 地址: https://github.com/bethrobson/Head-First-Design-Patterns
工厂模式递进体系分析
先捋一捋书中描述的抽象工厂是个什么意思, 要说清楚这个, 就必须先说一下它整个工厂模式的叙述的递进关系, 具体如下:
1, 先拿一个贼简单的创建 Pizza 的简单工厂类 PizzaStore, 来忽悠我, 这章很简单, 来学学吧, 这章源代码只有 8 个类
2, 兴冲冲的看完了第一部分, 当然是一鼓作气的看第二部分, 第二部分总结起来大概是讲这么一个事情:
1)我们公司在纽约的那个旗舰店 (PizzaStore) 生意不错, 现在我们要开一个分店, 名字叫芝加哥披萨店(ChicagoPizzaStore)
2)分店开了以后, 原来的先进经验不能丢啊, 所以 PizzaStore 被抽象出来了, 搞成了一个抽象类, 作为基础技术储备
3)PizzaStore 变成了公司的基础部门, 专门指导披萨制作的流程
4)旗舰店没办法啊, 一方面自己的名字被占用了, 一方面规范管理的风也吹来了, 就改名字叫纽约披萨店(NYPizzaStore), 旗下的所有披萨也顺势把名字也改了
5)新开起来的芝加哥披萨店, 倚靠了公司的技术实力, copy 了一份旗舰店的菜单, 但是也要照顾本地人的口味啊, 所以就因地制宜, 开发了自己的产品
整个事情就是这样, 涉及到的类有: pizza 抽象类(1 个),PizzaStore 抽象类(1 个), 实体披萨店(2 个), 实体店的各类 Pizza(8 个), 测试类(1 个)
总的来说, 这一部分的递进关系很棒, 也能很好的体现, 简单工厂和工厂方法最大的不同, 工厂方法可以对工厂类做到: 开闭原则
3, 工厂方法扯完了, 就来看一下, 抽象工厂嘛, 这一波操作我当时着实没有看懂, 就一直搁置了, 为什么当时没看懂呢?
对于正常的抽象工厂的讲解套路来说应该是这个样子的: 新增加一个产品线(抽象类, 实现类), 重新规划一下 Factory 的接口和实现类(Factory 里新加一个接口, 实现类新增实现)
它没有这样搞, 它直接根据披萨原料, 重新抽象了一套抽象工厂模式的体系出来, 不得不说, 这个比刚才说的那个新增产品线的例子漂亮太多了, 原因有以下几点:
1)直接新增一个产品线的这种操作, 虽然更容易理解, 但是会给人一个误导: 我在原来 Factory 新增接口的这个操作, 是符合设计模式, 符合抽象工厂模式的
2)抽象工厂的缺点是什么? 缺点就是, Factory 的接口, 在实际使用的时候, 几乎是无法新增产品的, 修改太多了. 所以它选择了使用原料这套新体系来讲解, 更加符合实际
当然, 它的例子也不是没有缺点, 个人感觉它的缺点有以下几点:
1)产生了类太多了, 35 个(上面有说过), 对新手不友好, 看到这个量级, 稍微往后一翻又是一个单例模式, 想着这个东西又不怎么能用上, 有求生欲望的, 都不会在放弃的边缘疯狂试探......
2)对它本身的工厂方法模式的体系(PizzaStore 体系), 也有很大量的修改, 集中体现在各类 Pizza 实现类的缩减(取消了按店铺名称来创建的各类披萨, 转而使用了简单工厂模式里面的名字),Pizza 的方法实现也有很多修改, 主要是为了支持原料体系, 当然, 虽然产生了很多的修改, 但是对外部提供的接口是没有影响的, 换句话说, 虽然实现有了翻天覆地的变化, 但是顾客还是无感知的, 这个从测试代码就能看出来:
工厂方法模式测试类:
- public class PizzaTestDrive {
- public static void main(String[] args) {
- PizzaStore nyStore = new NYPizzaStore();
- PizzaStore chicagoStore = new ChicagoPizzaStore();
- Pizza pizza = nyStore.orderPizza("cheese");
- System.out.println("Ethan ordered a" + pizza.getName() + "\n");
- pizza = chicagoStore.orderPizza("cheese");
- System.out.println("Joel ordered a" + pizza.getName() + "\n");
- pizza = nyStore.orderPizza("clam");
- System.out.println("Ethan ordered a" + pizza.getName() + "\n");
- pizza = chicagoStore.orderPizza("clam");
- System.out.println("Joel ordered a" + pizza.getName() + "\n");
- pizza = nyStore.orderPizza("pepperoni");
- System.out.println("Ethan ordered a" + pizza.getName() + "\n");
- pizza = chicagoStore.orderPizza("pepperoni");
- System.out.println("Joel ordered a" + pizza.getName() + "\n");
- pizza = nyStore.orderPizza("veggie");
- System.out.println("Ethan ordered a" + pizza.getName() + "\n");
- pizza = chicagoStore.orderPizza("veggie");
- System.out.println("Joel ordered a" + pizza.getName() + "\n");
- }
- }
- View Code
抽象工厂测试类:
- public class PizzaTestDrive {
- public static void main(String[] args) {
- PizzaStore nyStore = new NYPizzaStore();
- PizzaStore chicagoStore = new ChicagoPizzaStore();
- Pizza pizza = nyStore.orderPizza("cheese");
- System.out.println("Ethan ordered a" + pizza + "\n");
- pizza = chicagoStore.orderPizza("cheese");
- System.out.println("Joel ordered a" + pizza + "\n");
- pizza = nyStore.orderPizza("clam");
- System.out.println("Ethan ordered a" + pizza + "\n");
- pizza = chicagoStore.orderPizza("clam");
- System.out.println("Joel ordered a" + pizza + "\n");
- pizza = nyStore.orderPizza("pepperoni");
- System.out.println("Ethan ordered a" + pizza + "\n");
- pizza = chicagoStore.orderPizza("pepperoni");
- System.out.println("Joel ordered a" + pizza + "\n");
- pizza = nyStore.orderPizza("veggie");
- System.out.println("Ethan ordered a" + pizza + "\n");
- pizza = chicagoStore.orderPizza("veggie");
- System.out.println("Joel ordered a" + pizza + "\n");
- }
- }
- View Code
是一个非常赞的地方
抽象工厂实现过程
首先, 我们从原料的工厂类入手:
- public interface PizzaIngredientFactory {
- Dough createDough();
- Sauce createSauce();
- Cheese createCheese();
- Veggies[] createVeggies();
- Pepperoni createPepperoni();
- Clams createClam();
- }
一共有 6 种原料可以创建, 这个也是它源代码类多的原因之一, 由于大多数代码都是相似的, 所以这里也就不一一的列举了只列举流程相关的实现
先看酱油 (Sauce) 相关的接口和实现:
- public interface Sauce {
- String toString();
- }
- PlumTomatoSauce:
- public class PlumTomatoSauce implements Sauce {
- public String toString() {
- return "Tomato sauce with plum tomatoes";
- }
- }
- View Code
- MarinaraSauce:
- public class MarinaraSauce implements Sauce {
- public String toString() {
- return "Marinara Sauce";
- }
- }
- View Code
然后看看面团的相关接口和实现:
- public interface Dough {
- String toString();
- }
- ThickCrustDough:
- public class ThickCrustDough implements Dough {
- public String toString() {
- return "ThickCrust style extra thick crust dough";
- }
- }
- View Code
- ThinCrustDough:
- public class ThinCrustDough implements Dough {
- public String toString() {
- return "Thin Crust Dough";
- }
- }
- View Code
以上, 是抽象工厂的基础: 产品族的工厂类, 产品接口(或者是抽象类), 产品的具体实现类请忽略它只是为了方便理解放到一起的
列举了这个, 再来看看具体的原料工厂:
ChicagoPizzaIngredientFactory:
- public class ChicagoPizzaIngredientFactory
- implements PizzaIngredientFactory
- {
- // 我是面团, 我是这里
- public Dough createDough() {
- return new ThickCrustDough();
- }
- // 我是酱油, 我在这里
- public Sauce createSauce() {
- return new PlumTomatoSauce();
- }
- public Cheese createCheese() {
- return new MozzarellaCheese();
- }
- public Veggies[] createVeggies() {
- Veggies veggies[] = { new BlackOlives(),
- new Spinach(),
- new Eggplant() };
- return veggies;
- }
- public Pepperoni createPepperoni() {
- return new SlicedPepperoni();
- }
- public Clams createClam() {
- return new FrozenClams();
- }
- }
- View Code
- NYPizzaIngredientFactory:
- public class NYPizzaIngredientFactory implements 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 Garlic(), new Onion(), new Mushroom(), new RedPepper() };
- return veggies;
- }
- public Pepperoni createPepperoni() {
- return new SlicedPepperoni();
- }
- public Clams createClam() {
- return new FreshClams();
- }
- }
- View Code
实现也是很简单的, 这里为了不占篇幅就隐藏了, 有需要的自己点开看, 接下来看一个这个原料体系的建立, 需要修改的体现:
1, 各类 Pizza 的实现类, 需要持有 PizzaIngredientFactory 对象, 并且 prepare 方法会有修改
- public class ClamPizza extends Pizza {
- PizzaIngredientFactory ingredientFactory;
- public ClamPizza(PizzaIngredientFactory ingredientFactory) {
- this.ingredientFactory = ingredientFactory;
- }
- void prepare() {
- System.out.println("Preparing" + name);
- dough = ingredientFactory.createDough();
- sauce = ingredientFactory.createSauce();
- cheese = ingredientFactory.createCheese();
- clam = ingredientFactory.createClam();
- }
- }
2, 具体的 pizza 工厂的修改:
- public class ChicagoPizzaStore extends PizzaStore {
- protected Pizza createPizza(String item) {
- Pizza pizza = null;
- // 创建各自的原料工厂
- PizzaIngredientFactory ingredientFactory =
- new ChicagoPizzaIngredientFactory();
- if (item.equals("cheese")) {
- // 创建具体 pizza 的时候传入原料工厂
- pizza = new CheesePizza(ingredientFactory);
- pizza.setName("Chicago Style Cheese Pizza");
- } else if (item.equals("veggie")) {
- pizza = new VeggiePizza(ingredientFactory);
- pizza.setName("Chicago Style Veggie Pizza");
- } else if (item.equals("clam")) {
- pizza = new ClamPizza(ingredientFactory);
- pizza.setName("Chicago Style Clam Pizza");
- } else if (item.equals("pepperoni")) {
- pizza = new PepperoniPizza(ingredientFactory);
- pizza.setName("Chicago Style Pepperoni Pizza");
- }
- return pizza;
- }
- }
以上就是抽象工厂类的实现套路, 以及在 head first 中, 对原工厂方法模式具体影响的叙述, 贴代码是不可能贴代码的, 这辈子都不可能贴代码的
来源: https://www.cnblogs.com/skyseavae/p/9261119.html