内容简介:本文讲述一个披萨的诞生我有一家披萨店,顾客来点了想吃的品种,然后我要准备材料、烘烤、剪切、帮顾客打包。这个过程用代码怎么实现呢?首先定义好准备、烘烤、剪切和打包这些动作
本文讲述一个披萨的诞生
我有一家披萨店,顾客来点了想吃的品种,然后我要准备材料、烘烤、剪切、帮顾客打包。这个过程用代码怎么实现呢?
传统方式
首先定义好准备、烘烤、剪切和打包这些动作
public abstract class Pizza { protected String name; public abstract void prepare(); public void bake() { System.out.println(name+" baking;"); } public void cut() { System.out.println(name+" cutting;"); } public void box() { System.out.println(name+" boxing;"); } public void setname(String name) { this.name=name; } } 复制代码
然后定义一下都可以做哪些披萨种类
public class CheesePizza extend Pizza{ @Override public void prepare(){ super.setname("CheesePizza"); System.out.println("CheesePizza"); } } 复制代码
public class GreekPizza extends Pizza { @Override public void prepare() { super.setname("GreekPizza"); System.out.println("GreekPizza"); } } 复制代码
准备工作做完,就等着顾客来点餐了
public class OrderPizza { public OrderPizza() { String type = null; Pizza pizza = null; do { type = getType(); if ("greek".equals(type)){ pizza = new GreekPizza(); }else if ("cheese".equals(type)){ pizza = new CheesePizza(); }else { break; } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); }while (true); } private String getType(){ String type = null; BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader(System.in)); System.out.println("input pizza type: "); type = br.readLine(); } catch (IOException e) { e.printStackTrace(); } return type; } } 复制代码
整个过程如下:
顾客根据披萨的种类进行点餐,然后根据类型进行创建披萨对象,进行准备、烘焙、剪切、打包
很明显,这样设计是有问题的。如果店里有了新品,或者下架某种类披萨,就要修改OrderPizza类中根据顾客输入来创建对象这段代码,显然是违背上文提到的开放封闭原则。如下代码,现在我们已经知道哪些会改变,哪些不会改变,是时候使用封装了。
//需要修改 if ("greek".equals(type)){ pizza = new GreekPizza(); else if ("cheese".equals(type)){ pizza = new CheesePizza(); }else { break; } //不需要修改 pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); 复制代码
简单披萨工厂
现在最好将创建对象移到orderPizza()之外,但怎么做呢?我们可以把创建披萨的代码移到另一个对象中,由这个新对象专职创建披萨。 我们称这个新对象为“工厂”。
工厂(factory)处理创建对象的细节。一旦有了SimplePizzaFactory,orderPizza()就变成此对象的客户。当需要披萨时,就叫披萨工厂做一个。那些orderPizza()方法需要知道希腊披萨或者蛤蜊披萨的日子一去不复返了。现在orderPizza()方法只关心从工厂得到了一个披萨,而这个披萨实现了Pizza接口,所以它可以调用prepare()、bake()、cut()、box()来分别进行准备、烘烤、切片、装盒。
SimplePizzaFactory是我们的新类,它负责为客户创建披萨
public class SimpleFactoryPizza { public Pizza createPizza(String type){ Pizza pizza = null; if ("greek".equals(type)) { pizza=new GreekPizza(); }else if ("cheese".equals(type)) { pizza=new CheesePizza(); }else if ("beef".equals(type)) { pizza=new BeefPizza(); } return pizza; } } 复制代码
新的order只需构造时传入一个工厂,然后带入订单类型来使用工厂创建披萨,代替之前具体的实例化
public class OrderPizza { SimpleFactoryPizza factory; public OrderPizza(SimpleFactoryPizza factory) { this.factory = factory; } Pizza orderPizza(String type) { Pizza pizza = null; pizza = factory.createPizza(type); if (pizza != null) { pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); } return pizza; } } 复制代码
虽然看似代码同传统方式一样,但是我们已经将变化的代码抽取出来了,在OrderPizza中我们无需再次修改,此时我们已经将变化的和不变化的隔离开来了。
工厂模式
如果披萨店生意越来越好,考虑开几家加盟店。身为加盟公司的经营者,你希望确保加盟店的运营质量,还希望各地的披萨有自己不同的区域特点。在推广SimpleFactoryPizza时,发现加盟店采用的统一的工厂创建的披萨,但是其他部分却开始采用自创的流程,比如烘烤方式,包装方式等等。那么如果建立一个框架,约束关键步骤的同时又能保持一定的弹性呢?
修改给披萨店使用的框架
有一个办法可以让披萨制作活动局限于OrderPizza类,同时让不同的店还拥有自己的特色。
所要做的事情就是把createPizza()方法放回到OrderPizza中,不过得将它设置成抽象方法,然后为每个店铺创建一个OrderPizza的子类。 来看下修改后的OrderPizza:
public abstract class OrderPizza { public Pizza orderPizza(String type){ Pizza pizza; //将创建方法从工厂对象中移回OrderPizza中 pizza = createPizza(type); //没变过 pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } //创建工厂方法改为抽象的,方便子类修改 abstract Pizza createPizza(String type); } 复制代码
现在已经有一个OrderPizza作为父类,让不同区域的店铺来继承OrderPizza,每个子类各自决定制作什么风味的披萨。
允许子类(不同店铺)做决定
OrderPizza已经有一个不错的订单系统,由orderPizza()负责处理订单,而你希望所有加盟店对于订单的处理都能一致。 各个区域披萨店之间的差异在于他们制作披萨的风味(纽约披萨的薄脆、芝加哥披萨的饼厚等),我们现在要让现在createPizza()能够应对这些变化来负责创建正确种类的披萨。做法是让OrderPizza的各个子类负责定义自己的createPizza()方法。所以我们会得到一些OrderPizza具体的子类,每个子类都有自己的披萨变体,而仍然适合OrderPizza框架,并使用调试好的orderPizza()方法。
// 如果加盟店为顾客提供纽约风味的披萨,就使用NyStyleOrderPizza, // 因为此类的createPizza()方法会建立纽约风味的披萨 public class NyStyleOrderPizza extends OrderPizza{ //子类自己定义创建披萨方法 @Override Pizza createPizza(String type) { Pizza pizza = null; if (type.equals("cheese")) { pizza = new NyStyleCheesePizza(); } else if (type.equals("veggie")) { pizza = new NyStyleVeggiePizza(); } return pizza; } } // 类似的,利用芝加哥子类,我们得到了带芝加哥原料的createPizza()实现 public class ChicagoStyleOrderPizza extends OrderPizza{ @Override Pizza createPizza(String type) { Pizza pizza = null; if (type.equals("cheese")) { pizza = new ChicagoCheesePizza(); } else if (type.equals("veggie")) { pizza = new ChicagoVeggiePizza(); } return pizza; } } 复制代码
现在问题来了,OrderPizza的子类终究只是子类,如何能够做决定?在NyStyleOrderPizza类中,并没有看到任何做决定逻辑的代码。 关于这个方面,要从OrderPizza类的orderPizza()方法观点来看,此方法在抽象的OrderPizza内定义,但是只在子类中实现具体类型。 orderPizza()方法对对象做了许多事情(例如:准备、烘烤、切片、装盒),但由于Pizza对象是抽象的,orderPizza()并不知道哪些实际的具体类参与进来了。换句话说,这就是解耦(decouple)! 当orderPizza()调用createPizza()时,某个披萨店子类将负责创建披萨。做哪一种披萨呢?当然是由具体的披萨店决定。 那么,子类是实时做出这样的决定吗?不是,但从orderPizza()的角度看,如果选择在NyStyleOrderPizza订购披萨,就是由这个子类(NyStyleOrderPizza)决定。严格来说,并非由这个子类实际做“决定”,而是由“顾客”决定哪一家风味的披萨店才决定了披萨的风味。
我们来看下如何调用:
public class test { public static void main(String[] args) { OrderPizza orderPizza = new NyStyleOrderPizza(); Pizza pi = orderPizza.orderPizza("cheese"); System.out.println("-------"); Pizza pizza = orderPizza.orderPizza("veggie"); } } 复制代码
执行结果如下:
Preparing NyStyleCheesePizza baking; Preparing NyStyleCheesePizza cutting; Preparing NyStyleCheesePizza boxing; ------- Preparing NyStyleVeggiePizza baking; Preparing NyStyleVeggiePizza cutting; Preparing NyStyleVeggiePizza boxing; 复制代码
本文来源:《head-first设计模式》
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 设计模式——订阅模式(观察者模式)
- 设计模式-简单工厂、工厂方法模式、抽象工厂模式
- java23种设计模式-门面模式(外观模式)
- 设计模式-享元设计模式
- Java 设计模式之工厂方法模式与抽象工厂模式
- JAVA设计模式之模板方法模式和建造者模式
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Effective C++中文版
[美] Scott Meyers / 侯捷 / 华中科技大学出版社 / 2001-9 / 49.80元
Effective C++是世界顶级C++大师Scott Meyers的成名之作,初版于1991年。在国际上,这本书所引起的反响之大,波及整个计算机技术出版领域,余音至今未绝。几乎在所有C++书籍的推荐名单上,这部专著都会位于前三名。作者高超的技术把握力,独特的视角、诙谐轻松的写作风格、独具匠心的内容组织,都受到极大的推崇和仿效。 书中的50条准则,每一条都扼要说明了一个可让你写出更好的C+......一起来看看 《Effective C++中文版》 这本书的介绍吧!