设计模式(四):工厂方法模式(解析设计原则)

我的原文:设计模式(四):工厂方法模式(解析设计原则)

一、概述

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

二、解决问题

   通常我们需要一个对象的时候,会想到使用new来创建对象

      Tea tea = new MilkTea(); //使用了接口,代码更有弹性,体现设计原则“对接口编程,而不是对实现编程”

  当我们需要多个对象的时候,”对接口编程“的原则似乎还能派上用场   

Tea tea;
if("milk".equals(type)){
	tea = new MilkTea();
}else if("coffee".equals(type)){
	tea = new CoffeeTea();
}else if("lemon".equals(type)){
	tea = new LemonTea();
}

   这里也体现了“对接口编程”的好处,运行时决定要实例化哪个对象,让系统具备了弹性,但对于扩展性方面,我们就不敢恭维了。看以上代码,当我们要新增对象或者要扩展时,不得不打开这份代码进行检查和修改。通常这样的修改过的代码会造成部分系统更难维护和更新,而且也容易犯错。

  为了有良好的扩展性,我们想到了另外一个设计原则把变化的代码从不变化的代码中分离出来

  假设我们要开一家烧饼店,我们每天会做各种口味的烧饼出售,做烧饼的程序包括准备原材料、和面、烘烤、切片、装盒。

  

//烧饼店
public class ShaobingStore {
        public Shaobing orderShaobing(String type){
                Shaobing shaobing = null;
                if("onion".equals(type)){
                         //洋葱烧饼
                         shaobing = new OnionShaobing();
                }else if("sour".equals(type)){
                         //酸菜烧饼
                         shaobing = new SourShaobing();
                }else if("beef".equals(type)){
                         //牛肉烧饼
                         shaobing = new BeefShaobing();
                }
                //以上代码会发生改变,当洋葱烧饼不再出售时,我们会把创建洋葱的代码删除,我们可能会新增新口味的烧饼  
                 else if("pork".equals(type)){
                         //牛肉烧饼
                         shaobing = new PorkShaobing();
                }       
                //对于制作烧饼的程序中,以下这些步骤是不变的
                if(shaobing != null){
                        shaobing.prepare();
            shaobing.cut();
            shaobing.bake();  
                        shaobing.box();
                }
                return shaobing;
        }                      
}

  对于上面代码,我们使用”分离变化“的原则,把创建烧饼代码封装到一个类中,我们把它叫做工厂类,里面专门一个方法用来创建烧饼,如下所示:

package factorymethod.pattern;

public class SimpleShaobingFactory {
	public Shaobing createShaobing(String type){
		Shaobing shaobing = null;
		if("onion".equals(type)){
			//洋葱烧饼
			shaobing = new OnionShaobing();
		}else if("sour".equals(type)){
			//酸菜烧饼
			shaobing = new SourShaobing();
		}else if("beef".equals(type)){
			//牛肉烧饼
			shaobing = new BeefShaobing();
		}

		return shaobing;
	}
}

  改进后的烧饼店如下:

package factorymethod.pattern;

//烧饼店
public class ShaobingStore {

  public Shaobing orderShaobing(String type){
	  Shaobing shaobing = null;
	  
	  shaobing = factory.createShaobing(type);

	  //对于制作烧饼的程序中,以下这些步骤是不变的
	  if(shaobing != null){   	 
		  shaobing.prepare();
		  shaobing.cut();
		  shaobing.bake();
		  shaobing.box();
	   }
	     
	   return shaobing;
  }  

  SimpleShaobingFactory factory;

  public ShaobingStore(SimpleShaobingFactory factory){
     this.factory = factory;
  }

}

如上图所示,不管以后烧饼的口味怎么变,烧饼店的代码都不用变了,要扩展或者修改烧饼,我们只要更改创建烧饼的工厂类,也就是SimpleShaobingFactory 类,这就解开了烧饼店和烧饼的耦合,体现了对扩展开放,对修改关闭的设计原则。

  其实上面的改进方案使用了一个没有被真正冠名的设计模式简单工厂模式,其类图如下所示:

 从上面的类图来看,如果我们的烧饼店开在不同的地方,不同地方对洋葱烧饼,酸菜烧饼要求的口味不一样,北方人喜欢放辣椒,南方人喜欢清淡的,我们的烧饼店该怎么开呢?这就是工厂方法模式要帮我们解决的问题,工厂方法模式让类把实例化推迟到子类,让子类决定实例化的类是哪一个,将产品的“实现”从“使用”中解耦出来,让系统同时具备了弹性和扩展性。简单工厂不够弹性,不能改变正在创建的产品(同一种类型的只有一个,拿洋葱烧饼来说,全国各地的口味一样,没有辣与不辣的区分了)

三、结构类图

     

四、成员角色

   抽象创建者(Creator):定义了创建对象模板,实现了所有操纵产品的方法,除了工厂方法。具体创建者必须继承该类,实现工厂方法。

  具体创建者(ConcreteCreator):继承抽象创建者,实现工厂方法,负责创建产品对象。

  抽象产品(Product):定义了产品的共用资源,提供给子类继承使用,某些方法可以做成抽象方法,强制子类实现。

  具体产品(ConcreteProduct):继承自抽象产品,实现父类的抽象方法,也可以覆盖父类的方法,从而产生各种各类的产品。

五、应用实例  

  下面还是以开烧饼店为例,介绍如何在广州和长沙开烧饼店,卖适合当地风味的烧饼,而且烧饼的种类和名称一样。

  首先抽象烧饼店,也就是Creator

package factorymethod.pattern;

public abstract class ShaobingStore {
	public Shaobing orderShaobing(String type){
		Shaobing shaobing = createShaobing(type);
		
		shaobing.prepare();
		shaobing.cut();
		shaobing.bake();
		shaobing.box();
		
		return shaobing;
	}
	
	//未实现的工厂方法
	public abstract Shaobing createShaobing(String type);
		
}

  第二步,创建抽象烧饼,也就是Product

package factorymethod.pattern;

public abstract class Shaobing {
	//烧饼名称
	public String name;
	//烧饼用的配料
	public String sauce;
	//面团
	public String dough;
	
	public void prepare(){
		System.out.println("Prepareing " + name);
		//和面
		System.out.println("Kneading dough...");
		//加配料
		System.out.println("加配料:" + sauce);
	}
	
	//烤烧饼
	public void bake(){
		System.out.println("Bake for 25 minutes at 350C");
	}
	
	//切面团
	public void cut(){
		System.out.println("Cutting the dough into fit slices");
	}
	
	//打包
	public void box(){
		System.out.println("Place shaobing into official box");
	}
}

  第三步、创建广州风味的烧饼(加番茄酱的洋葱烧饼和牛肉烧饼),对应ConcreteProduct

package factorymethod.pattern;

public class GZOnionShaobing extends Shaobing{
	public GZOnionShaobing(){
		name = "广州的洋葱烧饼";
		//配料
		sauce = "番茄酱";
	}
}

  

package factorymethod.pattern;

public class GZBeefShaobing extends Shaobing{
	public GZBeefShaobing(){
		name = "广州的牛肉烧饼";
		//配料
		sauce = "番茄酱";
	}
}

  第四步、创建长沙风味的烧饼(加辣椒酱的洋葱烧饼和牛肉烧饼),对应ConcreteProduct

package factorymethod.pattern;

public class CSOnionShaobing extends Shaobing{
	public CSOnionShaobing(){
		name = "长沙洋葱烧饼";
		//配料
		sauce = "辣椒酱";
	}
}

  

package factorymethod.pattern;

public class CSBeefShaobing extends Shaobing{
	public CSBeefShaobing(){
		name = "长沙牛肉烧饼";
		//配料
		sauce = "辣椒酱 ";
	}
}

  第五步、创建广州烧饼店,对应ConcreteCreator

package factorymethod.pattern;

//广州烧饼店
public class GZShaobingStore extends ShaobingStore{

	@Override
	public Shaobing createShaobing(String type) {
		Shaobing shaobing = null;
		if("onion".equals(type)){
			shaobing = new GZOnionShaobing();
		}else if("beef".equals(type)){
			shaobing = new GZBeefShaobing();
		}
		
		return shaobing;
	}
}

  第六步、创建长沙烧饼店,对应ConcreteCreator

package factorymethod.pattern;

//长沙烧饼店
public class CSShaobingStore extends ShaobingStore{

	@Override
	public Shaobing createShaobing(String type) {
		Shaobing shaobing = null;
		if("onion".equals(type)){
			shaobing = new CSOnionShaobing();
		}else if("beef".equals(type)){
			shaobing = new CSBeefShaobing();
		}
		
		return shaobing;
	}

}

  第七步、测试售出名字相同但风味不一样的烧饼

package factorymethod.pattern;

public class TestShaobingStore {
	public static void main(String[] args){
		//在广州开一个烧饼店
		ShaobingStore gzStore = new GZShaobingStore();
		//售出一个洋葱烧饼
		gzStore.orderShaobing("onion");
		System.out.println("----------------------");
		//在长沙开一个烧饼店
		ShaobingStore csStore = new CSShaobingStore();
		//售出一个洋葱烧饼
		csStore.orderShaobing("onion");
	}
}

  运行结果:

六、工厂方法特有的设计原则

  如果我们之间在烧饼店中直接实例化一个烧饼,这种设计师依赖具体类的,类图如下:

  

这种依赖具体类设计,扩展性、弹性、维护性都比较差。如果将实例化的代码独立出来,使用工厂方法,我们将不再依赖具体类了,请看如下类图:

这就是我们要讲的依赖倒置原则:要依赖抽象,不要依赖具体类。用依赖倒置原则设计的系统,使得对象的实现从使用中解耦,对象的使用是在Creator,实现却在ConcreteCreator中,Creator只有Product的引用,Creator与ConcreteProduct松耦合,这种设计很强的扩展性、弹性和可维护性。

  设计中使用依赖倒置原则方法:

  1、变量不可以持有具体类的引用(就是不能使用new,使用工厂方法)

  2、不要让类派生自具体类(继承抽象或者实现接口)

  3、不要覆盖基类中已实现的方法

七、优点和缺点

  1、优点

  (1)、符合“开闭”原则,具有很强的的扩展性、弹性和可维护性。扩展时只要添加一个ConcreteCreator,而无须修改原有的ConcreteCreator,因此维护性也好。解决了简单工厂对修改开放的问题。

  (2)、使用了依赖倒置原则,依赖抽象而不是具体,使用(客户)和实现(具体类)松耦合。

  (3)、客户只需要知道所需产品的具体工厂,而无须知道具体工厂的创建产品的过程,甚至不需要知道具体产品的类名。

  2、缺点

  (1)、一个具体产品对应一个类,当具体产品过多时会使系统类的数目过多,增加系统复杂度。

  (1)、每增加一个产品时,都需要一个具体类和一个具体创建者,使得类的个数成倍增加,导致系统类数目过多,复杂性增加。

  (2)、对简单工厂,增加功能修改的是工厂类;对工厂方法,增加功能修改的是客户端。

八、使用场合

  1、当需要一个对象时,我们不需要知道该对象所对应的具体类,只要知道哪个具体工厂可以生成该对象,实例化这个具体工厂即可创建该对象。

  2、类的数目不固定,随时有新的子类增加进来,或者是还不知道将来需要实例化哪些具体类。

  3、定义一个创建对象接口,由子类决定要实例化的类是哪一个;客户端可以动态地指定工厂子类创建具体产品。

  

  

posted @ 2016-07-27 00:10 jenkinschan 阅读(...) 评论(...) 编辑 收藏

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
设计模式解析(第2版)》以作者自身学习、使用模式和多年来为软件开发人员(包括面向对象技术老兵和新手)讲授模式的经验为基础撰写而成。首先概述了模式的基础知识,以及面向对象分析和设计在当代软件开发中的重要性,随后使用易懂的示例代码阐明了12个最常用的模式,包括它们的基础概念、优点、权衡取舍、实现技术以及需要避免的缺陷,使读者能够理解模式背后的基本原则和动机,理解为什么它们会这样运作。  第1章 面向对象范型 2   第2章 UML 21   第二部分 传统面向对象设计的局限   第3章 对代码灵活性要求很高的问题 33   第4章 标准的面向对象解决方案 41   第三部分 设计模式   第5章 设计模式简介 53   第6章 Facade模式 65   第7章 Adapter模式 72   第8章 开拓视野 82   第9章 Strategy模式 98   第10章 Bridge模式 113   第11章 Abstract Factory模式 138   第12章 专家设计之道 154   第13章 用模式解决CAD/CAM问题 162   第五部分 迈向新的设计方式   第14章 设计模式原则与策略 180   第15章 共性与可变性分析 189   第16章 分析矩阵 196   第17章 Decorator模式 207   第六部分 其他重要模式   第18章 Observer模式 220   第19章 Template Method模式 231   第七部分 各种工厂模式   第20章 来自设计模式的教益:各种工厂模式 242   第21章 Singleton模式和Double-Checked Locking模式 249   第22章 Object Pool模式 257   第23章 Factory Method模式 267   第24章 工厂模式的总结 272   第八部分 终点与起点   第25章 设计模式回顾:总结与新起点 276
### 回答1: Java设计模式是一套经过总结和整理的编程思想,用于解决软件开发中常见的问题。《Java设计模式:23种设计模式全面解析(超级详细)》是一本详细介绍这23种设计模式的书籍,它可以帮助读者深入理解每种设计模式的原理和用法。 这本书分为23个章节,每个章节介绍一种设计模式。每个章节都从问题的背景出发,描述该设计模式的目标和应用场景。然后,通过实例代码和图形示意来展示如何使用该设计模式解决问题。 书中详细介绍了创建型设计模式、结构型设计模式和行为型设计模式。在创建型设计模式方面,包括单例模式、工厂模式、抽象工厂模式、建造者模式和原型模式等。结构型设计模式包括适配器模式、装饰器模式、代理模式、组合模式和桥接模式等。行为型设计模式包括策略模式、观察者模式、迭代器模式、命令模式和模板方法模式等。 每个设计模式都有其特定的使用场景和适用性,读者可以根据自己的需求选择合适的设计模式来解决问题。通过学习这本书,读者可以深入理解设计模式的原理和思想,并且能够应用到实际的软件开发中。 总的来说,《Java设计模式:23种设计模式全面解析(超级详细)》是一本对于Java开发者来说非常有价值的书籍。它通过详细的示例和解释,帮助读者深入理解23种设计模式的使用方法,使读者能够灵活地应用设计模式解决实际开发中遇到的问题。无论是初学者还是有一定经验的开发者,都可以从这本书中受益匪浅。 ### 回答2: 《Java设计模式:23种设计模式全面解析(超级详细)》是一本介绍Java设计模式的全面解析书籍。设计模式是软件工程领域中的一种解决问题的思维方式和经验总结,能够提供可复用的解决方案。这本书详细介绍了23种经典的设计模式,包括创建型、结构型和行为型三种类型的模式。 在创建型设计模式方面,书中包含了单例模式、原型模式工厂方法模式、抽象工厂模式、建造者模式。这些模式都是用来创建对象的,通过不同的实现方式能够灵活地创建对象,并且符合原则,如单一职责原则、开闭原则等。 在结构型设计模式方面,书中介绍了适配器模式、装饰器模式、代理模式、外观模式等。这些模式通过组合不同的类和对象,来解决不同类间关系的问题,增加了程序的可扩展性和灵活性。 在行为型设计模式方面,书中讲解了观察者模式、模板方法模式、策略模式、命令模式等。这些模式着重于对象之间的通信和协作,通过定义不同的行为和规则,让对象能够更好地进行交互,降低了对象间的耦合度。 此外,书中还介绍了其他几种分类的设计模式,如迭代器模式、访问者模式、备忘录模式等,这些模式在特定的应用场景中发挥着重要作用。 总的来说,《Java设计模式:23种设计模式全面解析(超级详细)》是一本详细介绍了Java设计模式的书籍,对于想深入了解和应用设计模式的Java开发人员来说,是一本很好的资料,能够帮助他们理解,并在实际项目中应用这些经典的设计模式。 ### 回答3: Java设计模式是一套被广泛应用于软件设计的规范和经验总结。设计模式可以提供可重用和可维护的代码,能够帮助开发人员解决常见的软件设计问题。 Java设计模式一共有23种,分为创建型模式、结构型模式和行为型模式三个类别。 创建型模式包括单例模式、工厂模式、抽象工厂模式、建造者模式和原型模式。单例模式确保一个类只有一个实例,工厂模式将对象的创建委托给工厂类,抽象工厂模式允许客户端使用抽象接口来创建一组相关对象,建造者模式通过一步步构建复杂对象,原型模式通过克隆已有对象来创建新对象。 结构型模式包括适配器模式、桥接模式、组合模式、装饰器模式、外观模式、享元模式和代理模式。适配器模式将一个类的接口转换成客户端期望的接口,桥接模式将抽象和实现分离,组合模式将对象组合成树形结构以表示“部分-整体”的层次结构,装饰器模式动态地给对象添加职责,外观模式为多个子系统提供一个统一的接口,享元模式共享对象以减少内存的使用,代理模式为其他对象提供一个代理以控制对这个对象的访问。 行为型模式包括模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式和访问者模式。模板方法模式定义了一个操作中的算法骨架,而将一些步骤延迟到子类中实现,命令模式将请求封装成对象,迭代器模式提供一种方法来顺序访问聚合对象的元素,观察者模式定义对象之间的一对多依赖关系,中介者模式定义了一个封装一组对象交互的对象,备忘录模式在不破坏封装的前提下捕获一个对象的内部状态,解释器模式为语言创建解释器,状态模式允许一个对象在其内部状态改变时改变其行为,策略模式定义了一系列算法,职责链模式将请求的发送者和接收者解耦,访问者模式将算法与数据结构分离开来。 这些设计模式在实际的软件开发中有着广泛的应用,对于提高代码的可重用性、可维护性和可扩展性都具有很好的作用。了解和熟练运用这些设计模式,对于Java开发人员来说是非常重要的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值