`
aine_pan
  • 浏览: 44851 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

设计模式 创建模式之工厂家族

 
阅读更多
学习设计模式是程序员到设计人员的必经之路,灵活运用设计模式可以使我们的工作事半功倍,甚至一劳永逸。
今天学习了设计模式里面的工厂家族:简单工厂模式,工厂方法模式和抽象工厂模式。
参考资料:Java design.pdf 是一本老书了,作者:阎宏
这三个模式刚好是这本书最先介绍的三种模式,经过阅读,我觉得有必要把我的想法写出来。
这里的客户端调用是模拟可变的,就是说如果我们从实例化一个类改成实例化另一个类(即生产一种产品改成生产另一种产品),我们的代码的修改量是作为评价框架优劣的主要因素(追求灵活性,可扩展性)。
简单工厂
用一个工厂来统一生成一类商品的实例,当系统修改时,不需要修改具体的实现类,只需要更新工厂类。(不会贴图,类图就先不贴了,下同)
需求:我们需要两个产品,产品间的选择是不定的。
产品A
package com.aine.patter.product.simple;

public class ProductA{

	public void doSome() {
		System.out.println("I'm A, I can do A.");
	}

}

产品B
package com.aine.patter.product.simple;

public class ProductB{

	public void doSome() {
		System.out.println("I'm B, I can do B.");
	}

}

Client调用
public static void main(String [] args){
		System.out.println("I'm Simple Client begin.");
		
		//version 1 _S
		ProductA proA = new ProductA();
		proA.doSome();
		
		ProductB proB = new ProductB();
		proB.doSome();
		//version 1 _E
		
		System.out.println("I'm Simple Client end.");
	}

最后运行的结果是:
I'm Simple Client begin.
I'm A, I can do A.
I'm B, I can do B.
I'm Simple Client end.

这样应该是最基本的写法,也是最初级程序员的写法,我们来看看这样写的弊端:
1 客户端程序直接了解了产品类A,B,如果需要增加产品,客户端就必须重新认识,并且修改(将前两行mark掉,打开后面的代码)。
2 不满足依赖倒转原则。
运用简单工厂方法改造
抽象出产品接口
package com.aine.patter.product.simple;

public interface IProduct {
	public void doSome();
}

让所有产品实现接口:
package com.aine.patter.product.simple;

public class ProductA implements IProduct{

	@Override
	public void doSome() {
		System.out.println("I'm A, I can do A.");
	}

}

package com.aine.patter.product.simple;

public class ProductB implements IProduct{

	@Override
	public void doSome() {
		System.out.println("I'm B, I can do B.");
	}

}

新建工厂类,来分发请求:
package com.aine.patter.factory;

import com.aine.patter.product.simple.IProduct;
import com.aine.patter.product.simple.ProductA;
import com.aine.patter.product.simple.ProductB;

public class SimpleFactory {
	public static IProduct createProduct(String name) throws Exception{
		if("A".equalsIgnoreCase(name)){
			return new ProductA();
		}else if ("B".equalsIgnoreCase(name)){
			return new ProductB();
		}else{
			throw new Exception("No such product.");
		}
	}
}

客户端修改成:
package com.aine.patter.client;

import com.aine.patter.factory.SimpleFactory;
import com.aine.patter.product.simple.IProduct;

public class SimpleClinet {
	public static void main(String [] args){
		System.out.println("I'm Simple Client begin.");
		
		//version 1 _S
//		ProductA proA = new ProductA();
//		proA.doSome();
//		
//		ProductB proB = new ProductB();
//		proB.doSome();
		//version 1 _E
		
		//version 2 _S
		try {
			IProduct product = SimpleFactory.createProduct("A");
			product.doSome();
		} catch (Exception e) {
			e.printStackTrace();
		}
		//version 2 _E
		
		System.out.println("I'm Simple Client end.");
	}
}

执行的结果是:
I'm Simple Client begin.
I'm A, I can do A.
I'm Simple Client end.

分析:
优点:如此一来,将产品类的逻辑从客户端剥离到factory中了,如果以后需要实例化B而不是A的话,只需要在客户端选B 类型的product就可以了,这样大大减少了修改量。客户端使用了里氏代换原则,整个结构也满足了依赖倒转原则。
缺点:
1 如果要增加新的product,还是需要修改factory类里面的if判断,这样还是不符合开闭原则。
2 factory类需要学习所有的实现子类,职责太集中,如果factory出问题了,整个系统都出问题了。
3 factory中使用的是静态方法,无法通过继承等方式重构扩展。

演变
网上有帖子使用反射的原理来拓展factory,我觉得是简单工厂的演变,而不是工厂方法。
package com.aine.patter.factory;

import com.aine.patter.product.simple.IProduct;

public class SimpleFactory {
	public static IProduct createProduct() throws Exception {
		String className = "ProductA";// get from parameter
		String classPath = "com.aine.patter.product.simple." + className;
		IProduct product;
		try {
			product = (IProduct) Class.forName(classPath).newInstance();
		} catch (Exception e) {
			e.printStackTrace();
			product = null;
		}
		return product;
	}
}

这样是大大的灵活了工厂的实例化方法,并且解耦了工厂类和产品类,但是这个还是属于简单工厂模式。
工厂方法
针对简单工厂的遗留的缺点,我们使用工厂方法来解决。
缺点:
1 如果要增加新的product,还是需要修改factory类,这样还是不符合开闭原则。
2 factory类需要学习所有的实现子类,职责太集中,如果factory出问题了,整个系统都出问题了。
3 factory中使用的是静态方法,无法通过继承等方式重构扩展。
方案:
工厂的职责是什么?答曰:生产产品。那么我们能否将简单工厂中的if判断抽离,将所有的工厂抽象出来呢?答案是肯定的。
所以第一步,抽象一个工厂接口
package com.aine.patter.factory.method;

import com.aine.patter.product.simple.IProduct;

public interface IFactory {
	public IProduct createProduct();
}

然后新建每个商品的工厂类
package com.aine.patter.factory.method;

import com.aine.patter.product.simple.IProduct;
import com.aine.patter.product.simple.ProductA;

public class FactoryA implements IFactory{

	@Override
	public IProduct createProduct() {
		return new ProductA();
	}

}

package com.aine.patter.factory.method;

import com.aine.patter.product.simple.IProduct;
import com.aine.patter.product.simple.ProductB;

public class FactoryB implements IFactory{

	@Override
	public IProduct createProduct() {
		return new ProductB();
	}

}

客户端调用:
package com.aine.patter.client;

import com.aine.patter.factory.method.FactoryA;
import com.aine.patter.factory.method.IFactory;
import com.aine.patter.product.simple.IProduct;

public class MethodClient {
	public static void main(String [] args){
		System.out.println("I'm Method Client begin.");
		try {
			IFactory factory = new FactoryA();
			IProduct product = factory.createProduct();
			product.doSome();
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("I'm Method Client end.");
	}
}

执行结果:
I'm Method Client begin.
I'm A, I can do A.
I'm Method Client end.

分析:
1 如果要建造ProductB只需要在客户端将工厂改成new FactoryB();
如果要新建ProductC需要增加工厂FactoryC,并在工厂中生产ProductC,客户端调用C工厂。
如此一来就满足了开闭原则。
2 简单工厂中的factory类 被分解成多个子类,实例化的工作被延迟到子类,分担了责任。
3 整个框架是面向接口编程的,符合了依赖倒转原则,是系统灵活多变,易于扩展。
Java中的应用
说几个我们熟悉的例子
1 EJB
package com.aine.patter.client;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class TestEJB {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			Context ctx = new InitialContext();
			EJBHome home = (EJBHome)ctx.lookup("EJBName");
			EJB ejb = home.create("aine");
			ejb.doSome();
		} catch (NamingException e) {
			e.printStackTrace();
		}
	}

}

2 JMS,Spring等等,代码就不贴了,大家应该都很熟悉这样的用法。
缺点:
如此完美的工厂方法还是有缺点的,那就是如果产品等级结构复杂了,工厂方法就无能为力了。
说的有点抽象,比方说我的例子里面的Product都是可以抽象出IProduct接口的,如果生产中需要构造不同的抽象接口的产品怎么办?举个不太准确的例子,我们之前的工厂是生产电脑的,很显然电脑是一个抽象的产品,因为根据不同厂家的实现不同,就实例化成不同的电脑了,比如联想电脑,华硕电脑,以及惠普电脑。如果单纯这样的区分我们使用工厂方法就可以handle了,因为这都是属于同一个产品等级结构的,换言之,都是统一的划分的。但是如果现在按照电脑卖给不同的人来划分不同的功能,卖给个人的电脑是PC,卖给公司是服务器,卖给商务人员是笔记本等等,这样的划分跟厂家实现的划分是不同的等级结构的,这样就变成了一个二维的分类组合:
联想华硕惠普
PC联想PC华硕PC惠普PC
服务器联想服务器华硕服务器惠普服务器
终端联想终端华硕终端惠普终端

这样的复杂的结构使用工厂方式就已经不能满足了,下面我们使用抽象工厂模式来解决这样的问题。
抽象工厂
讲抽象工厂之前必须要明确一个概念:产品族
产品族就是位于不同产品等级结构中,功能项关联的产品组成的家族。
比如上面的table,横向的是产品等级结构,通过不同厂商实现分厂了各个厂商的品牌等级,纵向就是产品族了,PC是一个产品族类型,因为它是将一类产品按照功能组合在一起的,同样服务器也是一个产品族类型。
抽象工厂就是用来生产这样复杂结构的产品的,具体的描述我就不写了,大家都能看到,我们来看实现:
1 根据产品族抽象产品类型(就以之前电脑的例子来说)。根据产品功能,我们抽象出2个产品族(当然可以更多):PC机,服务器
package com.aine.patter.product.abst;

public interface IPCComputer {
	public void doPC();
}

package com.aine.patter.product.abst;

public interface IServiceComputer {
	public void doService();
}

2 根据产品族,定义工厂方法:这就好像知道了产品的种类,我们必须有对应的生产方法,这个方法是抽象方法
package com.aine.patter.factory.abst;

import com.aine.patter.product.abst.IPCComputer;
import com.aine.patter.product.abst.IServiceComputer;

public interface ICreateProduct {
	public IPCComputer createPC();
	
	public IServiceComputer createService();
}

这里一个产品族对应一个生产方法,可以理解吧?
3 将产品的子类补齐,因为是组合关系,我们就以联想和惠普为例:
package com.aine.patter.product.abst;

public class LenovoPC implements IPCComputer{

	@Override
	public void doPC() {
		System.out.println("I'm Lenovo PC, I can do PC.");
	}
	
}

package com.aine.patter.product.abst;

public class LenovoService implements IServiceComputer{

	@Override
	public void doService() {
		System.out.println("I'm Lenovo Service, I can do Service.");
	}
	
}


package com.aine.patter.product.abst;

public class HuipuPC implements IPCComputer{

	@Override
	public void doPC() {
		System.out.println("I'm Huipu PC, I can do PC.");
	}
	
}

package com.aine.patter.product.abst;

public class HuipuService implements IServiceComputer{

	@Override
	public void doService() {
		System.out.println("I'm Huipu Service, I can do Service.");
	}
	
}

3 和工厂方法一样,我们新建每个产品等级的工厂类
package com.aine.patter.factory.abst;

import com.aine.patter.product.abst.IPCComputer;
import com.aine.patter.product.abst.IServiceComputer;
import com.aine.patter.product.abst.LenovoPC;
import com.aine.patter.product.abst.LenovoService;

public class LenovoFactory implements ICreateProduct{

	@Override
	public IPCComputer createPC() {
		return new LenovoPC();
	}

	@Override
	public IServiceComputer createService() {
		return new LenovoService();
	}

}

package com.aine.patter.factory.abst;

import com.aine.patter.product.abst.HuipuPC;
import com.aine.patter.product.abst.HuipuService;
import com.aine.patter.product.abst.IPCComputer;
import com.aine.patter.product.abst.IServiceComputer;

public class HuipuFactory implements ICreateProduct{

	@Override
	public IPCComputer createPC() {
		return new HuipuPC();
	}

	@Override
	public IServiceComputer createService() {
		return new HuipuService();
	}

}

4 客户端代码就和工厂方法一样了:
package com.aine.patter.client;

import com.aine.patter.factory.abst.ICreateProduct;
import com.aine.patter.factory.abst.LenovoFactory;
import com.aine.patter.product.abst.IPCComputer;
import com.aine.patter.product.abst.IServiceComputer;

public class AbstractClient {
	public static void main(String [] args){
		System.out.println("I'm Abstract Client begin.");
		try {
			ICreateProduct factory = new LenovoFactory();
			IPCComputer pc = factory.createPC();
			pc.doPC();
			
			IServiceComputer service = factory.createService();
			service.doService();
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("I'm Abstract Client end.");
	}
}

5 执行结果:
I'm Abstract Client begin.
I'm Lenovo PC, I can do PC.
I'm Lenovo Service, I can do Service.
I'm Abstract Client end.

如果需要生产惠普的机器,我们只要将客户端改成 new HuipuFactory();其他的都不用改,是不是将修改限制在最少了?
6 分析:
关于开闭原则:
当我们尝试增加一个产品等级机构的时候,我们只需要增加工厂,然后增加产品等级的实现类,这样是符合开闭原则的(同工厂方法)。
如果我们尝试增加一个产品族,我们不仅需要增加产品的抽象类,还要修改抽象工厂的工厂方法,因为对于一种新功能的产品,我们要提供相应的生产方法,进而这样就导致每个厂商都要提供相应的实现方法了。这是不满足开闭原则的。
这里我的理解跟书上写的是反的,不知道是谁的对,个人理解,希望大家一起讨论下。

总结
至此,大家是不是跟我一样对工厂家族有了一个新的认识啊?现在再回过头来看自己写的代码,我发现有好多地方可以重构下。
那我就不浪费时间了,赶紧去改了。
记录以备查阅。
分享到:
评论

相关推荐

    设计模式资料之工厂设计模式

    在给定的“设计模式资料之工厂设计模式”中,我们主要关注的是工厂设计模式,这是一种创建型设计模式,它提供了创建对象的最佳方式。在这个资料中,可能会详细解释工厂模式的概念、目的以及如何在实际编程中应用。 ...

    三种设计模式(简单工厂_工厂方法_抽象工厂)

    简单工厂模式是一种创建型设计模式,它提供了一个静态方法来创建对象,而无需暴露创建逻辑。这个静态方法根据输入参数来决定返回哪个类的实例。这种方式将对象的创建与使用分离,使得代码更加简洁,易于理解和使用。...

    设计模式之工厂

    "工厂模式"是其中一种常用的设计模式,它提供了一种创建对象的最佳方式。在这个主题下,我们将深入探讨三种主要的工厂模式:简单工厂、工厂方法和抽象工厂。 1. **简单工厂模式**: 简单工厂模式是最基础的形式,...

    设计模式之蝉

    还有“工厂模式”(Factory pattern),它是创建型设计模式之一,用于创建对象而不暴露创建逻辑给客户端,并且通过使用一个共同的接口来指向新创建的对象。这种模式在创建对象时提供了更高的灵活性和可扩展性。在...

    设计模式 - 抽象工厂模式

    抽象工厂模式是一种创建型设计模式,它提供了一种方式来创建一组相关或相互依赖的对象,而不需要指定具体的类。该模式允许客户端使用抽象的接口来创建一组相关的产品,而不需要关系实际产出的具体产品是什么。 在...

    设计模式之java工厂模式

    "设计模式之java工厂模式"是关于如何优雅地创建对象的一种经典设计模式,它属于创建者模式类别。创建者模式主要关注对象的创建,而工厂模式则在其中扮演着重要角色,因为它提供了一种抽象的方式来创建对象,从而使...

    工厂模式:简单工厂模式、工厂方法模式、抽象工厂模式

    工厂模式是一种常用的软件设计模式,它是创建型设计模式的一种,主要解决对象的创建问题,将对象的创建过程封装起来,使得创建过程独立于使用过程。这样可以提高代码的可复用性和灵活性,同时降低了系统的耦合度。...

    c#设计模式-工厂模式

    工厂模式是一种常用的设计模式,它的主要目的是通过抽象出产品创建过程,使得客户端代码不再直接new对象,而是通过工厂来获取对象,从而降低了代码之间的耦合度,提高了系统的可扩展性。工厂模式分为简单工厂模式、...

    设计模式之工厂模式Java实现和类设计图

    当面临多种产品系列时,抽象工厂模式可以提供一个接口,用于创建相关或依赖对象的家族,而无需明确指定具体类。这样可以确保在更换产品族时,不会影响使用产品的代码。 在Java中实现这些模式时,我们需要定义产品...

    设计模式之三种工厂模式

    工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。本篇将详细讲解三种工厂模式:简单工厂模式、工厂方法模式和抽象工厂模式,以及它们在实际开发中的应用。 1. 简单工厂模式(Simple Factory ...

    工厂模式-设计模式

    工厂模式是软件设计模式中的一种基础模式,它在对象创建方面提供了一种抽象化的方式,使得代码在实例化具体类时更加灵活。这个压缩包文件可能是关于如何在实际项目中运用工厂模式的教学材料,特别适合那些正在学习...

    工厂设计模式简单实例

    工厂设计模式是面向对象编程中的一种经典设计模式,它的主要目的是为了解耦对象的创建与使用,使得系统在不关心具体实现的情况下能够灵活地创建所需的对象。在这个名为"工厂设计模式简单实例"的资料中,我们可以期待...

    C#简单登录演示抽象工厂设计模式

    抽象工厂设计模式是其中一种创建型设计模式,它提供了一种创建对象家族的方法,这些对象来自同一个接口但属于不同的类。在这个“C#简单登录演示抽象工厂设计模式”的案例中,我们将深入探讨如何在C#中应用这个模式来...

    设计模式(简单工厂,工厂,抽象工厂)简单例子.rar

    简单工厂模式为初学者提供了快速入门的设计模式之一,因为它结构简单,易于理解。而工厂方法模式和抽象工厂模式则更加复杂,更加适合大型项目的开发,因为它们能够在变化中保持系统的稳定,降低维护成本。掌握并合理...

    设计模式之工厂模式详细

    无论是简单工厂、工厂方法还是抽象工厂,它们都在不同场景下提供了创建对象的优雅方式,是每个开发者都应该掌握的设计模式之一。在学习和实践中,理解每种模式的原理、优缺点以及适用场景,能够提升我们的编程素养,...

    Java设计模式之禅

    《Java设计模式之禅》是一本深入浅出讲解设计模式的书籍,书中不仅包含23种经典设计模式的案例,还详细介绍了设计模式背后的思想和原则,适合初学者以及对设计模式有一定了解的程序员阅读。本书旨在帮助读者理解如何...

    设计模式:简单工厂、方法工厂、抽象工厂、单例、原型、委派、模板、代理、策略

    3. **抽象工厂**:抽象工厂模式属于创建型设计模式,提供一个接口来创建相关或依赖对象的家族,而无需指定具体类。它允许更换产品族,同时保持相同的接口。 4. **单例**:单例模式确保一个类只有一个实例,并提供一...

    二十三种设计模式Java版之工厂模式

    在软件工程领域,设计模式是解决常见问题的...总的来说,工厂模式是Java开发中的重要设计模式之一,它提高了代码的可读性、可维护性和可扩展性。理解并熟练运用工厂模式,对于提升软件开发的效率和质量有着显著的作用。

    java 简单工厂模式 源代码

    从设计模式的类型上来说,简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂...

Global site tag (gtag.js) - Google Analytics