经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
HeadFirst设计模式读书笔记之工厂模式
来源:cnblogs  作者:被杜撰的风  时间:2018/11/29 9:23:39  对本文有异议

1. 简单工厂

1. 你开了一家披萨店,点披萨的方法可能是这样:

  1. public Pizza orderPizza(String type) {
  2. Pizza pizza;
  3. if (type.equals("芒果披萨")){
  4. pizza = new MangoPizza();
  5. }else if (type.equals("核桃披萨")){
  6. pizza = new WalnutPizza();
  7. }else if (type.equals("橙皮披萨")){
  8. pizza = new FlavedoPizza();
  9. }else {
  10. pizza = new StarPizza();
  11. }
  12. pizza.prepare();
  13. pizza.bake();
  14. pizza.cut();
  15. pizza.box();
  16. return pizza;
  17. }

可以看到,每当你想增加一种披萨类型,就要修改代码,添加一种if else条件.当有多个系统存在orderPizza的需求时,每个系统都要同时修改他们的代码.因此,需要将这种实例化具体对象的代码封装起来.

  1. public class PizzaStore {
  2. SimplePizzaFactory factory;
  3. public PizzaStore(SimplePizzaFactory factory){
  4. this.factory = factory;
  5. }
  6. public Pizza orderPizza(String type) {
  7. Pizza pizza;
  8. pizza = factory.createPizza(type);
  9. pizza.prepare();
  10. pizza.bake();
  11. pizza.cut();
  12. pizza.box();
  13. return pizza;
  14. }
  15. }
  1. public class SimplePizzaFactory {
  2. public Pizza createPizza(String type){
  3. Pizza pizza = null;
  4. if (type.equals("芒果披萨")){
  5. pizza = new MangoPizza();
  6. }else if (type.equals("核桃披萨")){
  7. pizza = new WalnutPizza();
  8. }else if (type.equals("橙皮披萨")){
  9. pizza = new FlavedoPizza();
  10. }else {
  11. pizza = new StarPizza();
  12. }
  13. return pizza;
  14. }
  15. }

这就是简单工厂方法,他不算一种设计模式,而是一种编程习惯.其要点有二:

1. 将产品定义为抽象类,可以被覆盖

2. 将具体的产品放到工厂类中来实例化,由工厂创建并返回给客户,这样便使得产品的创建逻辑可以解耦合,并增加了复用性

2. 工厂方法

1. 假设你现在有了很多加盟店,每种加盟店根据地区的差异有自己的工厂,但加盟店虽然采用工厂方法创建披萨,但其他部分却有所不同:烘烤的做法,不切片,使用杂七杂八的盒子...

这时,你想把创建披萨的方法与每家披萨店绑定在一起,让每家PizzaStore中都使用createPizza()创建pizza,并且这些店还可以拥有一定的制作披萨的自主权.因此,我们需要把PizzaStore和createPizza()定义成抽象的

  1. /**
  2. * 抽象的PizzaStore
  3. */
  4. public abstract class PizzaStore {
  5. public Pizza orderPizza(String type) {
  6. Pizza pizza;
  7. // 使用自己的方法做披萨
  8. pizza = createPizza(type);
  9. pizza.prepare();
  10. pizza.bake();
  11. pizza.cut();
  12. pizza.box();
  13. return pizza;
  14. }
  15. // 将做披萨的工厂方法放到PizzaStore中,工厂方法是抽象的,所以依赖子类来处理对象的创建
  16. public abstract Pizza createPizza(String type);
  17. }

2. 现在由PizzaStore的子类来具体卖披萨

  1. public class BeijingPizzaStore extends PizzaStore {
  2. @Override
  3. public Pizza createPizza(String item) {
  4. if (item.equals("橙皮披萨")){
  5. return new FlavedoPizza();
  6. }else {
  7. return new PekingDuckPizza();
  8. }
  9. }
  10. }
  1. public class ChengduPizza extends PizzaStore {
  2. @Override
  3. public Pizza createPizza(String type) {
  4. if (type.equals("芒果披萨")){
  5. return new MangoPizza();
  6. }else {
  7. return new StarPizza();
  8. }
  9. }
  10. }

现在,orderPizza()与具体的Pizza实现了一定的解耦合,决定最终Pizza对象的变成了某个具体的pizza店,而pizza店则由顾客来决定

3. 一些对工厂方法的理解:

1. 工厂方法是抽象的,所以依赖子类来创建对象,这样便将超类的代码与具体创建产品的代码分割开了

2. 工厂方法必须返回一个产品,超类通常中会用到这个产品

3. 工厂方法将产品与创建者抽象为接口,使他们成为平级的存在,具体的产品创建则使用子类来交互

4. 工厂方法将产品实例化的操作推迟到子类,子类的选择则由创建者来决定

5. 简单工厂方法更趋向组合,工厂方法则利用抽象和继承

6. 简单工厂封装了对象的创建,一个工厂处理所有的产品,但对同一个产品不可变更其做法,工厂方法则更有弹性,子类通过重写可以更改产品的创建

3. 依赖倒置原则

下面是一个简单粗暴的披萨店:

  1. /**
  2. * 依赖具体对象的PizzaStore
  3. */
  4. public class PizzaStore {
  5. public Pizza createPizza(String style, String type){
  6. Pizza pizza = null;
  7. if (style.equals("一元档")){
  8. if (type.equals("芒果披萨")){
  9. pizza = new MangoPizza();
  10. }else if (type.equals("核桃披萨")){
  11. pizza = new WalnutPizza();
  12. }
  13. }else if (style.equals("二元档")){
  14. if (type.equals("北京烤鸭披萨")){
  15. pizza = new PekingDuckPizza();
  16. }else if (type.equals("橙皮披萨")){
  17. pizza = new FlavedoPizza();
  18. }
  19. }else {
  20. System.out.println("披萨卖完了");
  21. }
  22. pizza.prepare();
  23. pizza.bake();
  24. pizza.cut();
  25. pizza.box();
  26. return pizza;
  27. }
  28. }

可以看到,这个披萨店依赖于每一个具体的披萨对象,每增加一个披萨就要修改一次代码,增加一个依赖,可以说是非常不好用了

从图中看到,高层组件直接依赖于低层组件而依赖倒置原则的的内容就是:

要依赖抽象,不要依赖具体类
这个原则说明不管高层还是低层组件都不应该依赖具体类,而应该依赖于抽象.如图所示:

与之前相比高层组件PizzaStore不在直接依赖于具体的Pizza对象,而是依赖于Pizza接口,而具体的Pizza产品则由被高层组件依赖颠倒成了依赖Pizza接口,这就是一种依赖倒置.总体来看,PizzaStore和Pizza都抽象化了,不用再依赖于具体的类型,符合依赖倒置的设计原则.
有三个指导方针可以让你更好地遵守依赖倒置原则:

  • 变量不可以持有具体类的引用
  • 不要让类派生自具体类
  • 不要覆盖基类中已覆盖的方法

4. 抽象工厂方法:创建产品家族

与普通的工厂方法比较,抽象工厂方法要显得重型得多: 工厂方法只创建一种产品,抽象工厂方法则创建一个家族的产品

1. 典型做法是创建一个工厂的接口,这个接口负责创建所有的产品家族成员,如创建一个生产调料家族的工厂:

  1. /**
  2. * 原料工厂的接口,实现这样的接口,可以随意组合产出各式各样的调料
  3. * 每个原料都是一个类
  4. */
  5. public interface SeasoningFactory {
  6. Chili createChili();
  7. DouBan createDouBan();
  8. Pepper createPepper();
  9. Salt createSalt();
  10. }
  1. /**
  2. * 郫县调料生产厂
  3. */
  4. public class PiXianSeasoningFactory implements SeasoningFactory {
  5. /**
  6. * 虎皮青椒
  7. * @return
  8. */
  9. @Override
  10. public Chili createChili() {
  11. return new HuPiQingJiao();
  12. }
  13. /**
  14. * 郫县豆瓣
  15. * @return
  16. */
  17. @Override
  18. public DouBan createDouBan() {
  19. return new PiXianDouBan();
  20. }
  21. /**
  22. * 花椒油
  23. * @return
  24. */
  25. @Override
  26. public Pepper createPepper() {
  27. return new HuaJiaoYou();
  28. }
  29. /**
  30. * 食用盐
  31. * @return
  32. */
  33. @Override
  34. public Salt createSalt() {
  35. return new ShiYongYan();
  36. }
  37. }
  1. /**
  2. * 灌县调料生产厂
  3. */
  4. public class GuanXianSeasoningFactory implements SeasoningFactory {
  5. /**
  6. * 虎皮青椒
  7. * @return
  8. */
  9. @Override
  10. public Chili createChili() {
  11. return new HuPiQingJiao();
  12. }
  13. /**
  14. * 老干妈豆瓣酱
  15. * @return
  16. */
  17. @Override
  18. public DouBan createDouBan() {
  19. return new LaoGanMaDouBanJiang();
  20. }
  21. /**
  22. * 花椒油
  23. * @return
  24. */
  25. @Override
  26. public Pepper createPepper() {
  27. return new HuaJiaoYou();
  28. }
  29. /**
  30. * 食用盐
  31. * @return
  32. */
  33. @Override
  34. public Salt createSalt() {
  35. return new ShiYongYan();
  36. }
  37. }

下面是抽象工厂方法的类图:

从类图中可以看出来,抽象工厂方法有一个缺点,就是当添加了一个新产品接口时,要去抽象工厂接口中添加一个方法,这会造成一些麻烦

2. 工厂方法与抽象工厂的一些对比

相同点:

  • 都是负责创建对象
  • 都能使程序解耦,将程序从创建大量具体对象的泥潭中拉出来

不同点:

  • 工厂方法用子类来创建具体对象,利用抽象和继承.抽象工厂包含了一大堆接口,利用组合的思想来创建对象
  • 工厂方法一般用来创建一种产品,抽象工厂则创建一族产品
  • 工厂方法功能简单,使用轻便.抽象工厂功能强大,使用时会创建大量的类

    3. 下面使用抽象工厂生产的原料来制作披萨

  1. /**
  2. * 原料工厂的接口,实现这样的接口,可以随意组合产出各式各样的调料
  3. * 每个原料都是一个类
  4. */
  5. public interface SeasoningFactory {
  6. /**
  7. * 辣椒
  8. * @return
  9. */
  10. Chili createChili();
  11. /**
  12. * 豆瓣
  13. * @return
  14. */
  15. DouBan createDouBan();
  16. /**
  17. * 花椒
  18. * @return
  19. */
  20. Pepper createPepper();
  21. /**
  22. * 盐
  23. * @return
  24. */
  25. Salt createSalt();
  26. }
  1. /**
  2. * 豆瓣
  3. */
  4. public abstract class DouBan {
  5. public abstract String sayMyName();
  6. }
  1. public class PiXianDouBan extends DouBan {
  2. @Override
  3. public String sayMyName() {
  4. return "郫县豆瓣";
  5. }
  6. }
  1. public class LaoGanMaDouBanJiang extends DouBan {
  2. @Override
  3. public String sayMyName() {
  4. return "老干妈豆瓣酱";
  5. }
  6. }
  1. /**
  2. * 郫县调料生产厂
  3. */
  4. public class PiXianSeasoningFactory implements SeasoningFactory {
  5. /**
  6. * 虎皮青椒
  7. * @return
  8. */
  9. @Override
  10. public Chili createChili() {
  11. return new HuPiQingJiao();
  12. }
  13. /**
  14. * 郫县豆瓣
  15. * @return
  16. */
  17. @Override
  18. public DouBan createDouBan() {
  19. return new PiXianDouBan();
  20. }
  21. /**
  22. * 花椒油
  23. * @return
  24. */
  25. @Override
  26. public Pepper createPepper() {
  27. return new HuaJiaoYou();
  28. }
  29. /**
  30. * 食用盐
  31. * @return
  32. */
  33. @Override
  34. public Salt createSalt() {
  35. return new ShiYongYan();
  36. }
  37. }
  1. /**
  2. * 灌县调料生产厂
  3. */
  4. public class GuanXianSeasoningFactory implements SeasoningFactory {
  5. /**
  6. * 虎皮青椒
  7. * @return
  8. */
  9. @Override
  10. public Chili createChili() {
  11. return new HuPiQingJiao();
  12. }
  13. /**
  14. * 老干妈豆瓣酱
  15. * @return
  16. */
  17. @Override
  18. public DouBan createDouBan() {
  19. return new LaoGanMaDouBanJiang();
  20. }
  21. /**
  22. * 花椒油
  23. * @return
  24. */
  25. @Override
  26. public Pepper createPepper() {
  27. return new HuaJiaoYou();
  28. }
  29. /**
  30. * 食用盐
  31. * @return
  32. */
  33. @Override
  34. public Salt createSalt() {
  35. return new ShiYongYan();
  36. }
  37. }
  1. /**
  2. * 每一个披萨都有一组抽象工厂生产的原料
  3. *
  4. */
  5. public abstract class Pizza {
  6. protected String name;
  7. /**
  8. * 辣椒
  9. */
  10. protected Chili chili;
  11. /**
  12. * 豆瓣
  13. */
  14. protected DouBan douBan;
  15. /**
  16. * 花椒
  17. */
  18. protected Pepper pepper;
  19. /**
  20. * 盐
  21. */
  22. protected Salt salt;
  23. /**
  24. * 在这个方法中,收集抽象工厂生产的原料
  25. */
  26. public abstract void prepare();
  27. public void bake(){
  28. System.out.println("烘烤...");
  29. }
  30. public void cut(){
  31. System.out.println("切片...");
  32. }
  33. public void box() {
  34. System.out.println("包装...");
  35. }
  36. public String getName() {
  37. return name;
  38. }
  39. public void setName(String name) {
  40. this.name = name;
  41. }
  42. @Override
  43. public String toString(){
  44. return "披萨制作完毕:"+name
  45. +"\n生产原料:"+chili.sayMyName()
  46. +"\t"+douBan.sayMyName()
  47. +"\t"+pepper.sayMyName()
  48. +"\t"+salt.sayMyName();
  49. }
  50. }
  1. public class ChengduPizza extends Pizza{
  2. // 调料工厂
  3. SeasoningFactory seasoningFactory;
  4. public ChengduPizza(SeasoningFactory seasoningFactory) {
  5. this.name = "旧成都披萨";
  6. this.seasoningFactory = seasoningFactory;
  7. }
  8. @Override
  9. public void prepare() {
  10. System.out.println("开始准备 "+getName()+":");
  11. chili = seasoningFactory.createChili();
  12. douBan = seasoningFactory.createDouBan();
  13. pepper = seasoningFactory.createPepper();
  14. salt = seasoningFactory.createSalt();
  15. System.out.println("以下原料准备完毕:"+chili.sayMyName()
  16. +","+douBan.sayMyName()+","+pepper.sayMyName()+","+salt.sayMyName());
  17. }
  18. }
  1. public abstract class PizzaStore {
  2. public abstract Pizza createPizza(String type);
  3. }
  1. public class StarBoundPizzaStore extends PizzaStore {
  2. @Override
  3. public Pizza createPizza(String type) {
  4. Pizza pizza;
  5. SeasoningFactory factoryA = new PiXianSeasoningFactory();
  6. SeasoningFactory factoryB = new GuanXianSeasoningFactory();
  7. if (type.equals("旧成都披萨")){
  8. pizza = new ChengduPizza(factoryA);;
  9. }else if (type.equals("新北京披萨")){
  10. pizza = new PekingPizza(factoryB);
  11. }else {
  12. pizza = new ChengduPizza(factoryB);
  13. }
  14. pizza.prepare();
  15. pizza.bake();
  16. pizza.cut();
  17. pizza.box();
  18. return pizza;
  19. }
  20. public static void main(String[] args) {
  21. PizzaStore pizzaStore = new StarBoundPizzaStore();
  22. Pizza pizza = pizzaStore.createPizza("旧成都披萨");
  23. System.out.println(pizza.toString());
  24. Pizza pizza1 = pizzaStore.createPizza("新北京披萨");
  25. System.out.println(pizza1.toString());
  26. }
  27. }
  1. 结果:
  2. 开始准备 旧成都披萨:
  3. 以下原料准备完毕:虎皮青椒,郫县豆瓣,花椒油,食用盐
  4. 烘烤...
  5. 切片...
  6. 包装...
  7. 披萨制作完毕:旧成都披萨
  8. 生产原料:虎皮青椒 郫县豆瓣 花椒油 食用盐
  9. 开始准备 新北京披萨:
  10. 以下原料准备完毕:虎皮青椒,老干妈豆瓣酱,花椒油,食用盐
  11. 烘烤...
  12. 切片...
  13. 包装...
  14. 披萨制作完毕:新北京披萨
  15. 生产原料:虎皮青椒 老干妈豆瓣酱 花椒油 食用盐
 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号