参考文章1:http://wxg6203.iteye.com/blog/740229
参考文章2:http://blog.csdn.net/zhengzhb/article/details/7348707
学习设计模式系列之“简单工厂模式”
简单工厂模式(simple factory pattern)是类的创建模式,又叫静态工厂方法(static factory method)模式。简单工厂模式就是由一个工厂类根据传入的参数决定创建哪一种的产品类。
工厂模式的角色
简单工厂模式共有4个角色
- 工厂类角色:是具体产品类角色直接调用者。
- 抽象产品角色:接口或抽象类,负责具体产品角色的定义,及与客户端的交互。
- 具体产品角色:被工厂类创建的对象,也是客户端实际操作对象。
- 客户端:调用工厂类产生实例,并调用实例的方法进行相应工作。
Java代码实现
抽象产品角色
抽象产品角色是简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
/**
* 动物类接口
*/
public interface AnimalInterface
{
// 动物的动作
public void act();
}
具体产品角色
具体产品角色是简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。
狗
/**
* 狗
*/
public class Dog implements AnimalInterface
{
@Override
public void act()
{
System.out.println("我是一只狗,汪~……");
}
}
猫
/**
* 猫
*/
public class Cat implements AnimalInterface
{
@Override
public void act()
{
System.out.println("我是一只猫,喵~……");
}
}
羊
/**
* 羊
*/
public class Sheep implements AnimalInterface
{
@Override
public void act()
{
System.out.println("我是一只羊,咩~……");
}
}
牛
/**
* 牛
*/
public class Bull implements AnimalInterface
{
@Override
public void act()
{
System.out.println("我是一头牛,哞~……");
}
}
工厂类角色
工厂类角色是简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
实现
**
* 创建动物对象
*/
public class AnimalFactory
{
/**
* 根据字符串创建对象
* 缺点:扩展需增加判断
* @param key
* @return
*/
public static AnimalInterface getAnimalByKey(String key)
{
// 注意 "dog".equals(key) "dog"放在前面的用法的好处,避免key为null的判断
if ("dog".equals(key))
{
return new Dog();
}
else if ("cat".equals(key))
{
return new Cat();
}
else if ("sheep".equals(key))
{
return new Sheep();
}
else if ("Bull".equals(key))
{
return new Bull();
}
return null;
}
/**
* 使用java的反射,根据类名创建对象
* 缺点:类名太长
* @param className
* @return
*/
public static AnimalInterface getAnimalByClass(String className)
{
try
{
// 使用反射,动态加载类,创建类对象
AnimalInterface animal = (AnimalInterface)Class.forName(className).newInstance();
return animal;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
/**
* 根据类名的映射来缩写完整类名
* @param classKey 类名的简写
* @return
*/
public static AnimalInterface getAnimalByClassKey(String classKey)
{
// 读properties文件,获取完整类名
PropertiesReader reader = new PropertiesReader();
Map<String, String> animalMap = reader.readProperties("input.properties");
String className = animalMap.get(classKey);
// 使用反射,动态加载类,创建类对象
try
{
AnimalInterface animal = (AnimalInterface)Class.forName(className).newInstance();
return animal;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
}
Properties文件
dog=com.factory.dp.Dog
cat=com.factory.dp.Cat
sheep=com.factory.dp.Sheep
bull=com.factory.dp.Bull
读取Properties文件
/**
* 读取properties文件的内容
*/
public class PropertiesReader
{
/**
* 根据properties文件名读取文件内容
* @return 类简写和类的映射
*/
public Map<String, String> readProperties(String file)
{
// 将文件内容读进map
Map<String, String> animalMap = new HashMap<String, String>();
// 创建Properties对象
Properties p = new Properties();
InputStream in = getClass().getResourceAsStream(file);
try
{
// 加载输入流
p.load(in);
Enumeration e = p.propertyNames();
while (e.hasMoreElements())
{
String key = (String)e.nextElement();
String className = p.getProperty(key);
animalMap.put(key, className);
}
}
catch (IOException e)
{
e.printStackTrace();
}
return animalMap;
}
}
客户端
public class Client
{
/**
* 客户端调用
*/
public static void main(String[] args)
{
// 使用key调用
AnimalInterface a1 = AnimalFactory.getAnimalByKey("dog");
a1.act();
AnimalInterface a2 = AnimalFactory.getAnimalByKey("cat");
a2.act();
// 使用类名调用
AnimalInterface a3 = AnimalFactory.getAnimalByClass("com.factory.dp.Bull");
a3.act();
AnimalInterface a4 = AnimalFactory.getAnimalByClass("com.factory.dp.Sheep");
a4.act();
// 使用类名的映射来调用
AnimalInterface a5 = AnimalFactory.getAnimalByClassKey("sheep");
a5.act();
AnimalInterface a6 = AnimalFactory.getAnimalByClassKey("bull");
a6.act();
}
}
优点
工厂类是整个模式的关键.包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象.通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了。而不必管这些对象究竟如何创建及如何组织的.明确了各自的职责和权利,有利于整个软件体系结构的优化。
缺点
由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。
当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利
这些缺点在工厂方法模式中得到了一定的克服。
使用场景
- 工厂类负责创建的对象比较少;
- 客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心;
- 由于简单工厂很容易违反高内聚责任分配原则,因此一般只在很简单的情况下应用。
PS
在工厂类角色创建具体类对象时,使用三种方法:
- 使用key调用
- 使用完整类名
- 使用完整类名的简写映射
其中使用反射和Properties文件,对于第一种方法恰恰正是上文中缺点所说的,第二种方法和第三种则进行了改进。