Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1504026
  • 博文数量: 3500
  • 博客积分: 6000
  • 博客等级: 准将
  • 技术积分: 43870
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-03 20:31
文章分类

全部博文(3500)

文章存档

2008年(3500)

我的朋友

分类:

2008-05-04 20:52:56

一起学习

当看到"new",就会想到"具体","new"有什么不对劲的?
在技术上,new没有错,毕竟这是java基础部分.针对接口编程,可以隔离掉以后系统可能发生的一大堆改变.为什么呢?如果代码是针对接口而写,你们通过多态,它可以与任何新类实现该接口.但是,当代码使用大量的具体类时,等于是自找麻烦,因为一但加入新的具体类就必须改变代码,也就是说你的代码并非"对修改关闭".别忘了,我们的第一个原则用来处理改变,"找出会变化的方面,把它们从不变的部分分离出来".
假设你有个披萨店,你的代码可能这么写:

Java代码复制代码
  1. pizza orderPizza(){
  2. Pizza piz = new Pizza();
  3. //为了让系统有弹性,我们很希望这是一个抽象类或接口,但如果这样,这些类或接口就无法直接实例化
  4. piz.prepare();
  5. piz.bake();
  6. piz.cut();
  7. piz.box();
  8. return piz;
  9. }

但是你需要更多的披萨类型,所以必须增加一些代码,来决定适合的披萨类型,然后再制造这个披萨:

Java代码复制代码
  1. pizza orderPizza(String type){
  2. //现在把披萨类型传入orderPizza
  3. Pizza piz;
  4. //根据披萨的类型,实例化正确的具体类,注意这里的任何披萨都必须实现Pizza接口
  5. if(type.equals("cheese")){
  6. piz = new CheesePizza();
  7. }else if(type.equals("greek")){
  8. piz = new GreekPizza();
  9. }
  10. //某一天你为了赶上竞争者,你加入了一些流行风味的披萨,greek披萨卖的不好,你要从菜单去掉,随着时间的过去,这里就必须一改再改
  11. else if(type.equals("clam")){
  12. piz = new ClamPizza();
  13. }
  14. else if(type.equals("veggie")){
  15. piz = new VeggaePizza();
  16. }
  17. //一旦我们有了一个披萨,需要做一些准备,然后烘烤.切片.装盒,这里是我们不想改变的地方
  18. piz.prepare();
  19. piz.bake();
  20. piz.cut();
  21. piz.box();
  22. return piz;
  23. }

现在我们知道哪些会改变哪些不会改变,该是使用封装的时候了,现在我们把创建对象的代码抽离,建立一个简单披萨工厂,这个工厂只管如何创建披萨:

Java代码复制代码
  1. public class SimplePizzaFactory {
  2. //内定一个createPizza()方法,所有客户通过这个方法来实例化新对象
  3. public Pizza createPizza(String type) {
  4. Pizza pizza = null;
  5. //移植过来的代码,基本没什么变动
  6. if (type.equals("cheese")) {
  7. pizza = new CheesePizza();
  8. } else if (type.equals("pepperoni")) {
  9. pizza = new PepperoniPizza();
  10. } else if (type.equals("clam")) {
  11. pizza = new ClamPizza();
  12. } else if (type.equals("veggie")) {
  13. pizza = new VeggiePizza();
  14. }
  15. return pizza;
  16. }
  17. }

这么做有什么好处?似乎只是把问题搬到另一个对象罢了,问题依然存在.
别忘了,SimplePizzaFactory可以有很多客户,虽然目前只看到orderPizza()方法是它的客户,然而可能还有其他的客户.所以把创建披萨的代码包装进一个类,当以后实现改变时,只需修改这个类即可.别忘了,我们正要把具体实例化过程从客户的代码中删除.
修改客户代码,重做PizzaStore类:

Java代码复制代码
  1. public class PizzaStore {
  2. //加一个SimplePizzaFactory的引用
  3. SimplePizzaFactory factory;
  4. public PizzaStore(SimplePizzaFactory factory) {
  5. this.factory = factory;
  6. }
  7. public Pizza orderPizza(String type) {
  8. Pizza pizza;
  9. //而orderPizza()方法通过简单传入订单类型来使用工厂创建披萨,
  10. //请注意,我们把new操作符替换成工厂对象的创建方法,这里不在使用具体实例化
  11. pizza = factory.createPizza(type);
  12. pizza.prepare();
  13. pizza.bake();
  14. pizza.cut();
  15. pizza.box();
  16. return pizza;
  17. }
  18. //...other methon
  19. }

简单工厂其实不是一个设计模式,反而比较像是一种编程习惯,接下来继续用披萨店来讲述工厂方法模式.
披萨店经营有成,击败了竞争者,现在大家都希望来加盟你的披萨店,身为经营者,你希望确保加盟店的营运质量,希望这些店都使用你那些经过时间考验的代码.但是区域的差异呢?每家加盟店都可能想要提供不同风味的披萨,比方说纽约.芝加哥.加州,这就受到了开店地点及该地区口味的影响.
如果利用SimplePizzaFactory,写出3种不同的工厂,分别是NYPizzaFactory,ChicagoPizzaFactory,CaliforniaPizzaFactory,那么各个加盟店都有合适的工厂可以使用,这是一种做法:
NYPizzaFactory nyFactory = new NYPizzaFactory();//这里创建的工厂是制造纽约风味的披萨
PizzaStore nyStore = new PizzaStore(nyFactory);//然后建立一个披萨店,将纽约工厂的引用作为参数
nyStore.orderPizza("Veggie"); //制造披萨,会得到纽约风味的披萨
在推广SimplePizzaFactory时,你发现加盟店的确采用你的工厂创建披萨,但是他们自创流程:烘烤的做法有些差异.不要切片.使用其他厂商的盒子.怎么让你的质量多一些质量控制多些弹性呢?
我们再来修改一些披萨店,先声明一个工厂方法:

Java代码复制代码
  1. //现在PizzaStore是抽象的,每个子类都会覆盖createPizza方法,同时使用PizzaStore定义的orderPizza方法,
  2. //甚至可以把orderPizza定义为tinal,以防止被之类覆盖
  3. public abstract class PizzaStore {
  4. /**
  5. *现在把工厂对象移到这个方法中,工厂方法现在是抽象的,所以依赖子类来处理对象的创建
  6. *工厂方法必须返回一个产品,超类中定义的方法,通常使用到工厂方法的返回值
  7. *工厂方法将客户(如orderPizza)和实际创建具体产品的代码分割开来
  8. */
  9. abstract Pizza createPizza(String type);
  10. public Pizza orderPizza(String type) {
  11. //现在createPizza()方法从工厂对象中移回PizzaStore
  12. Pizza pizza = createPizza(type);
  13. pizza.prepare();
  14. pizza.bake();
  15. pizza.cut();
  16. pizza.box();
  17. return pizza;
  18. }
  19. }

好了,让我们把纽约风味的加盟店开起来吧:

Java代码复制代码
  1. //createPizza()返回一个Pizza对象,由子类全权负责该实例化哪一个具体Pizza
  2. public class NYPizzaStore extends PizzaStore {
  3. //必须实现的createPizza方法
  4. Pizza createPizza(String item) {
  5. if (item.equals("cheese")) {
  6. return new NYStyleCheesePizza();
  7. } else if (item.equals("veggie")) {
  8. return new NYStyleVeggiePizza();
  9. } else if (item.equals("clam")) {
  10. return new NYStyleClamPizza();
  11. } else if (item.equals("pepperoni")) {
  12. return new NYStylePepperoniPizza();
  13. } else return null;
  14. }
  15. }

实现一下披萨本身,没披萨开什么加盟店呢:

Java代码复制代码
  1. //从一个抽象披萨类开始,所有的具体披萨都必须派生自这个类
  2. public abstract class Pizza {
  3. String name; //每个披萨都有名称,面团类型,一套佐料
  4. String dough;
  5. String sauce;
  6. ArrayList toppings = new ArrayList();
  7. void prepare() {
  8. System.out.println("Preparing " name);
  9. System.out.println("Tossing dough...");
  10. System.out.println("Adding sauce...");
  11. System.out.println("Adding toppings: ");
  12. //准备工作需要以特定的顺序进行
  13. for (int i = 0; i < toppings.size(); i ) {
  14. System.out.println(" " toppings.get(i));
  15. }
  16. }
  17. void bake() {}
  18. void cut() {}
  19. void box() {}
  20. public String getName() {return name;}
  21. public String toString() {
  22. StringBuffer display = new StringBuffer();
  23. display.append("---- " name " ----\n");
  24. display.append(dough "\n");
  25. display.append(sauce "\n");
  26. for (int i = 0; i < toppings.size(); i ) {
  27. display.append((String )toppings.get(i) "\n");
  28. }
  29. return display.toString();
  30. }
  31. }

现在我们来定义纽约和芝加哥风味的具体子类,并吃些披萨怎么样:

Java代码复制代码
  1. public class NYStyleCheesePizza extends Pizza {
  2. public NYStyleCheesePizza() {
  3. name = "NY Style Sauce and Cheese Pizza";
  4. dough = "Thin Crust Dough";
  5. sauce = "Marinara Sauce";
  6. toppings.add("Grated Reggiano Cheese");
  7. }
  8. }
  9. public class ChicagoStyleCheesePizza extends Pizza {
  10. public ChicagoStyleCheesePizza() {
  11. name = "Chicago Style Deep Dish Cheese Pizza";
  12. dough = "Extra Thick Crust Dough";
  13. sauce = "Plum Tomato Sauce";
  14. toppings.add("Shredded Mozzarella Cheese");
  15. }
  16. //这个芝加哥风味披萨覆盖cut()方法,将披萨切成正方形
  17. void cut() {
  18. System.out.println("Cutting the pizza into square slices");
  19. }
  20. }
  21. public class PizzaTestDrive {
  22. public static void main(String[] args) {
  23. //建立2个不同的店
  24. PizzaStore nyStore = new NYPizzaStore();
  25. PizzaStore chicagoStore = new ChicagoPizzaStore();
  26. //下订单
  27. Pizza pizza = nyStore.orderPizza("cheese");
  28. System.out.println("Ethan ordered a " pizza.getName() "\n");
  29. pizza = chicagoStore.orderPizza("cheese");
  30. System.out.println("Joel ordered a " pizza.getName() "\n");
  31. }
  32. }

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

TAG: 设计模式

下载本文示例代码


设计模式入门学习之工厂模式(工厂方法模式)设计模式入门学习之工厂模式(工厂方法模式)设计模式入门学习之工厂模式(工厂方法模式)设计模式入门学习之工厂模式(工厂方法模式)设计模式入门学习之工厂模式(工厂方法模式)设计模式入门学习之工厂模式(工厂方法模式)设计模式入门学习之工厂模式(工厂方法模式)设计模式入门学习之工厂模式(工厂方法模式)设计模式入门学习之工厂模式(工厂方法模式)设计模式入门学习之工厂模式(工厂方法模式)设计模式入门学习之工厂模式(工厂方法模式)设计模式入门学习之工厂模式(工厂方法模式)
阅读(117) | 评论(0) | 转发(0) |
0

上一篇:SCJP Mock Exam 1

下一篇:Scala和设计模式

给主人留下些什么吧!~~