设计模式之【工厂模式】

工厂模式概念:

实例化对象,用工厂方法取代new操作;

工厂模式包含工厂方法模式和抽象方法模式。

抽象工厂模式是工厂方法模式的扩展;


工厂模式的意图

定义一个工厂来创建对象,可是让子类来决定哪些类须要被实例化;

工厂方法把实例化的工作推迟到子类中去实现。


什么情况下适合工厂模式?

有一组类似的对象须要创建;

在编码时不能预见须要创建哪种类的实例;

系统须要考虑扩展性。不应依赖于产品类实例怎样被创建、组合和表达的细节;


项目需求:

在软件系统中常常面临着“对象”的创建工作,因为需求的变化,这个对象肯能随之也会发生变化,但它却拥有比較稳定的接口。

为此我们须要提供一种封装机制来隔离出这个易变对象的变化。从而保持系统中其它依赖该对象的对象不随着需求变化而变化。


解决之道:

1.尽量松耦合。一个对象的依赖对象的变化与本身无关;

2.详细产品与client剥离,责任切割;


工厂模式方法类图:



抽象工厂模式类图:



抽象工厂模式举例:如今有一个抽象硬件工厂,以下有多个详细工厂。生产一系列类似的产品族。如图所看到的:



应用场景举例:

比方如今计算机里面有一个游戏,游戏里面的角色分为男性、女性、儿童,他们分别须要穿戴不同的服饰,那么这个时候我们通过工厂模式来完毕获取不同的服饰对象。


首先定义一个服饰接口:

/**
 * 人物服饰接口
 * 
 * @author bear
 *
 */
public interface ClothingInterface {

	/**
	 * 为人物画上服饰
	 */
	public abstract void draw();
}

然后定义三个服饰类,它们都是ClothingInterface的实现类:

/**
 * 男性人物的服饰
 * 
 * @author bear
 *
 */
public class MensWear implements ClothingInterface {

	/**
	 * 在UI上为男性人物加入上服饰
	 */
	@Override
	public void draw() {
		// TODO Auto-generated method stub
		System.out.println("**************男装************");
	}

}


/**
 * 女性人物的服饰
 * 
 * @author bear
 *
 */
public class WomansWear implements ClothingInterface {

	/**
	 * 在UI上为女性人物加入上服饰
	 */
	@Override
	public void draw() {
		// TODO Auto-generated method stub
		System.out.println("**************女装************");
	}

}


/**
 * 儿童人物的服饰
 * 
 * @author bear
 *
 */
public class ChildrensWear implements ClothingInterface {

	/**
	 * 在UI上为儿童人物加入上服饰
	 */
	@Override
	public void draw() {
		// TODO Auto-generated method stub
		System.out.println("**************童装************");
	}

}


接着,我们这里来定义服饰工厂类:

import java.util.Map;

/**
 * 服饰工厂
 * 
 * @author bear
 *
 */
public class ClothingFactory {
	
	//存储关键词和类的全路径键值对
	private Map<String, String> classNameMap;
	
	//工厂构造方法,在方法中初始化map
	public ClothingFactory(){
		PropertiesReader pr = new PropertiesReader();
		classNameMap = pr.getProperties();
	}

	/**
	 * 工厂生产方法,依据关键词来生产对象
	 * 缺点:每次新添加一种类别,都须要在这里添加代码
	 * 
	 * @param key 传入须要的服饰类别
	 * @return 返回客户须要的服饰
	 */
	
	/*
	 * 
	public ClothingInterface newInstance(int key){
	 
		switch (key) {
		case Clothing.Men:
			return new MensWear();
		case Clothing.Women:
			return new WomansWear();
		case Clothing.Children:
			return new ChildrensWear();
		default:
			return null;
		}
	}
	* 
	*/
	
	/**
	 * 工厂生产方法,依据关键词来索取类名。利用反射来生产对象
	 * 
	 * @param key
	 * @return
	 */
	public ClothingInterface newInstance(String key){
		try {
			//获取到要实例化的类的全路径
			String className = classNameMap.get(key);
			//通过反射的方法来生成新的实例
			ClothingInterface clothing = (ClothingInterface)Class.forName(className).newInstance();
			//返回新实例
			return clothing;
		} catch (Exception e) {
			// TODO: handle exception
			return null;
		}
		
	} 
	
}

在构造方法中。通过读取属性。来初始化map。属性读取类的定义例如以下:

/**
 * 属性文件读取类
 * 
 * @author bear
 *
 */
public class PropertiesReader {

	/**
	 * 读取属性文件里的键值对到map中,然后返回
	 * 
	 * @return
	 */
	public Map<String, String> getProperties(){
		Properties props = new Properties();
		Map<String, String> map = new HashMap<String, String>();
		try {
			InputStream is = this.getClass().getResourceAsStream("Type.properties");
			props.load(is);
			Enumeration en = props.propertyNames();
			while (en.hasMoreElements()) {
				String key = (String) en.nextElement();
				String property = props.getProperty(key);
				map.put(key, property);
			}
		} catch (Exception e) {
			// TODO: handle exception
		}
		return map;
	}
}

这里的属性文件名叫做Type.properties,它的内容是这种:

men=com.bear.factorypattern.MensWear
women=com.bear.factorypattern.WomansWear
children=com.bear.factorypattern.ChildrensWear

然后我们在Clothing这种一个实体类中定义几个key的常量值:

/**
 * 服饰类
 * 
 * @author bear
 *
 */
public class Clothing {
	
	public static final String Men = "men";
	public static final String Women = "women";
	public static final String Children = "children";
}


最后,我们在client的Main方法中,来通过工厂获取到我们须要的实例:

public class UITest {

	public static void main(String[] args) {
		ClothingFactory factory = new ClothingFactory();
		ClothingInterface clothing01 = factory.newInstance(Clothing.Men);
		clothing01.draw();
		ClothingInterface clothing02 = factory.newInstance(Clothing.Women);
		clothing02.draw();
		ClothingInterface clothing03 = factory.newInstance(Clothing.Children);
		clothing03.draw();
	}
}


打印结果例如以下:

**************男装************
**************女装************
**************童装************

总结:在以上的演示样例中,假设我们要新加入一种服饰。须要的操作是:

(1).新建服饰类。该类须要实现ClothingInterface接口。

(2).在properties属性文件里添加映射关系;

(3).在Clothing类中加入常量key。


工厂模式演示样例代码下载链接:

FactoryPatternDemo


抽象工厂模式实际上就是有一个新的接口。那么这个接口中包括了一系列的相关的接口产品簇,抽象工厂专门负责生产产品簇,而非单个产品。


常见工厂模式的应用:

(1).JDBC

是一种用于运行SQL语句的Java API,能够为多种关系数据库提供统一訪问,它由一组用Java语言编写的类和接口组成。



(2)Spring BeanFactory

BeanFactory,作为Spring基础的IOC容器,是Spring的一个Bean工厂。

假设但从工厂模式的角度思考,它就是用来“生产Bean”,然后提供给client。

Bean的实例化过程:

a.调用Bean的默认构造方法,或指定的构造方法,生成Bean实例(暂称instance1);

b.假设Bean的配置文件里注入了Bean属性值。则在instance1基础上进行属性注入形成instance2。这样的注入是覆盖性的;

c.假设Bean实现了InitializingBean接口,则调用afterPropertiesSet()方法,来改变或操作instance2。得到instance3;

d.假设Bean的配置文件里指定了init-method="init"属性,则会调用指定的初始化方法。则在instance3的基础上调用初始化方法init(),将对象终于初始化为instance4,当然,这个初始化的名字是随意的;



工厂模式的作用:

(1)系统能够在不改动详细工厂角色的情况下引进新的产品。

(2)client不必关心对象创建,明白了职责;

(3)更好的理解面向对象的原则是面向接口编程,而不是面向实现编程;


工厂模式的应用场景:

(1)一个系统应当不依赖于产品类实例被创建、组成、表示的细节。

(2)这个系统的产品至少有一个产品簇;

(3)同属于同一产品簇的产品是设计成在一起使用的。

(4)不同的产品以一系列的接口的面貌出现,从而使系统不依赖于接口实现的细节;


posted @ 2017-06-04 12:07  jzdwajue  阅读(120)  评论(0编辑  收藏  举报