《Head First设计模式》第四章笔记 工厂模式_public veggies[] createate();

之前我们一直在使用new操作符,但是实例化这种行为并不应该总是公开的进行,而且初始化经常会造成耦合问题,工厂模式将摆脱这种复杂的依赖,本次内容包括简单工厂,工厂方法和抽象工厂三种情况。

1 2 3 4 5 6Duck duck; if``(a){ duck=``new Duck1(); }``else``{ duck=``new Duck2(); }

如上面代码中使用new实例化一个类时,使用的是实现,而不是接口,代码捆绑着具体类会导致代码更脆弱缺乏弹性,后续的维护、修改等操作容易出错。

使用new操作符会造成的问题:

1.如果你针对接口编程,你可以利用多态,实现该接口并没有太大问题
2.但是如果代码中使用了很多具体类,一旦加入新的类,就必须改变代码,这就违反了开放-关闭原则。

项目举例

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

使用普通方式实现:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17public 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; } }

但是如果新上市一种pizza或者下架一种pizza,你就需要修改这段代码,这样就没有做到对修改关闭。

简单工厂:

于是我们使用之前学到的知识,找到变化的代码和不变的代码,进行封装了:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29public class SimpleFactory { public Pizza createPizza(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(); } return pizza; } }   public class PizzaStore { SimpleFactory simpleFactory; public PizzaStore(SimpleFactory simpleFactory) { this``.simpleFactory = simpleFactory; } Pizza orderPizza(String type) { Pizza pizza =``null``; pizza = simpleFactory.createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }

这么做的好处看起来好像只是把原来的代码放进到另外一个类中,但是当以后出现变化时,自需要修改这个类,就可以,并且这样做的好处还在于可能现在只有客人点餐才会用到这段代码,如果可以点外卖的话,仍然可以用这段代码,代码得到了复用

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

工厂方法模式:

背景更新:假如现在你要开分店,各种加盟商进来后,他们都要开发符合本地口味的pizza,那就需要各个地方都有一个工厂,也就是每个地方继承SimpleFactory类,但是每个工厂并不是完全使用你原来的烘培方法。

工厂方法模式类图如下:

对应代码·:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32public abstract class PizzaStore { Pizza orderPizza(String type) { Pizza pizza =``null``; pizza = createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } protected abstract Pizza createPizza(String type); }   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``; } }

现在订购这些订单时,自需要实例化各自风格的工厂,就算都是芝士披萨,也是来自不同商店口味的。

总结:
  • 工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的是哪一个,工厂方法让类把实例化推迟到子类,工厂方法模式的优点在于帮助产品从使用中分离,从使用中解耦。
  • 和简单工厂的区别,两者类似,但是在于createPizza方法,工厂方法让每个上篇自行负责,而简单工厂使用的则是PizzaStore对象。
  • 简单工厂把所有的事情在一个地方干完了,然而工厂方法则是写了一个框架,让子类具体实现,PizzaStore作为工厂的orderPizza方法提供了一个一般的框架,以便创建披萨,其具体依赖工厂的方法来创建具体的披萨。
  • 两者都实现了不让创建对象的代码到处乱跑。

看看对象依赖情况,原来的PizzaStore依赖于所有的Pizza对象,当这些对象发生改变时,可能会影响到PizzaStore,PizzaStore时依赖具体类的,PizzaStore就是高层组件,Pizza各种对象就是低层组件,但是设计原则有一条是:依赖抽象,不要依赖具体类
而使用工厂方法之后就不在出现这种依赖很多具体类的情况了。

现在背景有变,有些加盟店,使用低价原料来增加利润,你必须采取一些手段,以免毁掉你的披萨店品牌。
你打算建造一家成产原料的工厂,并将原料运送到各家加盟店,那么剩下最后一个问题,不同的区域原料是不一样的,对于两个加盟店给出了两组不同的原料。

类图:

建造原料工厂
我们要建造一个工厂来生产原料,这个工厂负责创建原料家族中的每一种原料。

1 2 3 4 5 6 7 8public interface PizzaIngredientFactory { public Dough createDough(); public Sauce createSauce(); public Cheese createCheese(); public Veggies[] createVeggies(); public Pepperoni createPepperoni(); public Clams createClams(); }

要做的事情是:
1.为每个区域建造一个工厂,你需要创建一个继承自PizzaIngredientFactory的子类来实现每一个创建方法。
2.实现一组原料类供工厂使用,例如RegianoCheese,RedPeppers,ThickCrustDough.这些类可以在何时的区域间共享。
3.然后你仍然需要将这一切组织起来,将新的原料工厂整合进旧的PizzaStore代码中。

创建纽约的原料工厂

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26class NYPizzaIngredientFactory``implements PizzaIngredientFactory{ @Override public Dough createDough() { return null``; } @Override public Sauce createSauce() { return null``; } @Override public Cheese createCheese() { return null``; } @Override public Veggies[] createVeggies() { return new Veggies[``0``]; } @Override public Pepperoni createPepperoni() { return null``; } @Override public Clams createClams() { return null``; } }

重做披萨

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25abstract class Pizza{ String name; Dough dough; Sauce sauce; Veggies veggies[]; Cheese cheese; Pepperoni pepperoni; Clams clams; abstract void prepare(); void cut(){ System.out.println(``"Cutting the pizza into diagonal slices"``); } void box(){ System.out.println(``"Place pizza in official PizzaStore box"``); } void setName(String name){ this``.name=name; } public String getName(){ return name; } public void toString2(){ //这里打印披萨的代码 } }

继续重做披萨

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20public class CheesePizza``extends Pizza { //这里组合了一个PizzaIngredientFactory对象的引用,用于提供不同的原料 PizzaIngredientFactory ingredientFactory;   /** * 通过传入一个PizzaIngredientFactory原料工厂,我们可以在制作Pizza的时候动态的产生所需要的原料 * @param ingredientFactory */   public CheesePizza(PizzaIngredientFactory ingredientFactory) { this``.ingredientFactory = ingredientFactory; }   void prepare() { System.out.println(``"Preparing " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); } }

再回到披萨店

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29public class NYPizzaStore``extends PizzaStore {   protected Pizza createPizza(String item) { Pizza pizza =``null``; PizzaIngredientFactory ingredientFactory =``new NYPizzaIngredientFactory();   if (item.equals(``"cheese"``)) {   pizza =``new CheesePizza(ingredientFactory); pizza.setName(``"New York Style Cheese Pizza"``);   }``else if (item.equals(``"veggie"``)) {   pizza =``new VeggiePizza(ingredientFactory); pizza.setName(``"New York Style Veggie Pizza"``);   }``else if (item.equals(``"clam"``)) {   pizza =``new ClamPizza(ingredientFactory); pizza.setName(``"New York Style Clam Pizza"``);   }``else if (item.equals(``"pepperoni"``)) { pizza =``new PepperoniPizza(ingredientFactory); pizza.setName(``"New York Style Pepperoni Pizza"``);   } return pizza; } }

我们做了些什么?
我们引入新类型的工厂,也就是所谓的抽象工厂,来创建披萨原料家族。
通过抽象工厂所提供的接口可以创建产品的家族,利用这个接口书写代码,我们的代码将从实际工厂解耦,以便在不同上下文中实现各式各样的工厂,制造出各种不同的产品。

定义抽象工厂模式

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

工厂方法与抽象工厂

抽象工厂的每个方法都实际上看起来像工厂方法,方法声明成抽象,子类方法去覆盖这些方法去创建对象。
抽象工厂的任务是定义一个负责创造一组产品的接口,每个方法就是创建一种产品,这就是工厂方法,所以在抽象工厂中利用工厂方法来实现生产方法是自然的。

对比:

  • 两者都是用来创建对象,但是工厂方法使用的是继承,而抽象工厂使用的是对象的组合。
  • 比如工厂方法创建pizza是各种PizzaStore继承后覆盖createPizza()方法实现的,让客户从具体类型中解耦。
  • 但是对于抽象工厂,提供了一个用来创建一个产品家族的抽象类型,这个类型的子类定义了产品被产生的方法,要想使用这个工厂必须实例化它,然后将它传入一些针对抽象类型所写的代码中。
  • 比如PizzaIngredientFactory这个接口定义了一堆原料的做法,而对于纽约的原料加工厂先实现这些接口,然后纽约的商店实例化这个原料加工厂,就保证了不同的商店使用到各自不同的原料,把客户从所使用的实际产品中解耦。
  • 一群还是一个,一群产品集合用抽象工厂,具体哪些类中可以确定一个抽象类,子类继承实现使用工厂方法

设计原则:依赖倒置

依赖倒置原则:要依赖抽象,不要依赖具体类。高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。

用依赖倒置原则重构代码

下面是一些指导方针来避免违反依赖倒置原则:

  • 变量不可以持有具体类的引用。如果使用new,就会持有具体类的引用,你可以改成工厂来避免
  • 不要让类派生自具体类,如果派生自具体类,你就会依赖具体类,请派生自抽象(接口或者抽象类)
  • 不要覆盖基类中已经实现的方法,如果覆盖,那么基类说明就不是一个真正适合被继承的抽象,基类中已经实现的方法,应该有所有的子类共享

要尽量达到这些原则,但不是随时都要遵守

总结

工厂:封装对象的创建,处理创建对象的细节

静态工厂:利用静态方法定义一个简单的工厂。优点:不需要创建工厂类的实例化。缺点:不能通过继承改变创建方法行为。

简单工厂:简单工厂并不是一种设计模式,因为只是简单的把创建对象的代码封装起来

工厂模式:在父类定义了一个创建对象的接口,通过让子类决定创建的对象是什么,来达到让对象创建的过程封装的目的。工厂方法让类把实例化推迟到子类

抽象工厂:提供一个接口,用于创建相关或依赖对象的家族,而不需要指明具体的类

  • 工厂方法用来处理对象的创建,并将这样的行为封装在子类中。这样客户程序中超类的代码就和子类对象的创建部分解耦了
  • 简单工厂vs工厂模式:简单工厂把全部的事情在一个地方做完了,而工厂模式是一个创建框架,让子类决定如何实现
  • 抽象工厂vs工厂模式
    • 抽象工厂的方法经常以工厂方法的方式实现,抽象工厂的任务是定义一个负责创建一组产品的接口
    • 工厂方法使用继承,抽象工厂使用组合
    • 工厂方法只是用来创建一种产品,而抽象工厂创建的是一个产品家族
    • 使用工厂模式意味着需要扩展一个类并覆盖它的工厂方法。抽象工厂提供了一个创建产品家族的抽象类型,类型的子类定义了产品生产的方式

最后

🍅 硬核资料:关注即可领取PPT模板、简历模板、行业经典书籍PDF。
🍅 技术互助:技术群大佬指点迷津,你的问题可能不是问题,求资源在群里喊一声。
🍅 面试题库:由技术群里的小伙伴们共同投稿,热乎的大厂面试真题,持续更新中。
🍅 知识体系:含编程语言、算法、大数据生态圈组件(Mysql、Hive、Spark、Flink)、数据仓库、Python、前端等等。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里无偿获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 12
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值