个人博客:打开链接
工厂方法模式的定义
定义:Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
工厂方法模式的优点
- 良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,降低模块间的耦合。
- 工厂方法模式的扩展性非常优秀。在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。
- 屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不要发生变化。因为产品类的实例化工作是由工厂类负责的,一个产品对象具体由哪一个产品生成是由工厂类决定的。
- 工厂方法模式是典型的解耦框架。高层模块值需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特法则,我不需要的就不要去交流;也符合依赖倒置原则,只依赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类。
女娲造人实例
女娲采集黄土捏成人的形状,然后放到八卦炉中烧制,最后放置在大地上生长,这就是工艺过程。首先对造人的过程分析,该过程涉及三个对象:女娲、八卦炉和三种不同肤色的人(白人、黑人、黄种人)。女娲可以使用场景类Client来表示,八卦炉类似于一个工厂,负责制造生产产品(即人类),三种不同肤色的人,他们都是同一个接口下的不同实现类,都是人嘛,只是肤色、语言不同,对八卦炉来说都是它生产出的产品。
类图比较简单,AbstractHumanFactory是一个抽象类,定义了一个八卦炉具有的整体功能,HumanFactory为实现类,完成具体的任务----创建人类;Human接口是人类的总称,其三个实现类分别为三类人种;NvWa类是一个场景类,负责模拟这个场景,执行相关的任务。UML类图为:
人类总称:
public interface Human{
//每个人种的皮肤都有相应的颜色
public void getColor();
//人类会说话
public void talk();
}
黑色人种:
public class BlackHuman implements Human {
public void getColor() {
System.out.println("黑色人种的皮肤颜色是黑色的!");
}
public void talk() {
System.out.println("黑人会说话,一般人听不懂。");
}
}
黄色人种:
public class YellowHuman implements Human {
public void getColor() {
System.out.println("黄色人种的皮肤颜色是黄色的!");
}
public void talk() {
System.out.println("黄色人种会说话,一般人说的都是双字节。");
}
}
白色人种:
public class WhiteHuman implements Human {
public void getColor() {
System.out.println("白色人种的皮肤颜色是白色的!");
}
public void talk() {
System.out.println("白种人人会说话,一般说的都是单子节。");
}
}
女娲给八卦炉下达的命令应该是“给我生产出一个黄色人种”,而不是“给我生产一个会走、会跑、会说话、皮肤是黄色的人种”,因为这样的命令增加了交流的成本,作为一个生产的管理者,只要知道生产什么就可以了,而不需要事物的具体信息。抽象人类创建工厂,泛型限定了输入参数必须是Class类型、必须是Human的实现类。
抽象人类创建工厂:
public abstract class AbstractHumanFactory {
public abstract <T extends Human> T creatHuman(Class<T> c);
}
人类创建工厂:
public class HumanFactory extends AbstractHumanFactory {
public <T extends Human> T creatHuman(Class<T> c) {
// 定义一个生产的人种
Human human = null;
try{
//生产一个人种
human = (Human)Class.forName(c.getName()).newInstance();
}catch(Exception e){
System.out.println("人类生产错误");
e.printStackTrace();
}
return (T)human;
}
}
女娲类:
public class NvWa {
public static void main(String[] args) {
AbstractHumanFactory YinYanglu = new HumanFactory();
System.out.println("--造出的第一批人是白色人种");
Human whiteHuman = YinYanglu.createHuman(WhiteHuman.class);
whiteHuman.getColor();
whiteHuman.talk();
System.out.println("--造出的第二批人是黑色人种");
Human blackHuman = YinYanglu.createHuman(BlackHuman.class);
blackHuman.getColor();
blackHuman.talk();
System.out.println("--造出的第一批人是黄色人种");
Human yellowHuman = YinYanglu.createHuman(YellowHuman.class);
yellowHuman.getColor();
yellowHuman.talk();
}
}
这就是工厂方法模式,很简单吧?
工厂方法模式的扩展
缩小为简单工厂模式
一个模块仅需要一个工厂类,没有必要把它产生出来,使用静态的方法就可以了。根据这一要求,修改一个类图:
我们在类图中去掉了AbstractHumanFactory抽象类,同时把createHuman方法设置为静态类型,简化了类的创建过程,变更的源码仅仅是HumanFactory和NvWa类:
简单工厂模式中的工厂类:
public class HumanFactory {
public static <T extends Human> T createHuman(Class<T> c){
//定义一个生产出的人种
Human human=null;
try {
//产生一个人种
human = (Human)Class.forName(c.getName()).newInstance();
} catch (Exception e) {
System.out.println("人种生成错误!");
}
return (T)human;
}
}
HumanFactory类仅有两个地方发生变化:去掉继承抽象类,并在createHuman前增加static 关键字;工厂类发生变化,也同时引起了调用者NvWa的变化,代码如下:
public class NvWa {
public static void main(String[] args) {
System.out.println("--------------女娲第一次造人---------------");
Human whiteHuman = HumanFactory.createHuman(WhiteHuman.class);
whiteHuman.getColor();
whiteHuman.talk();
System.out.println("--------------女娲第二次造人---------------");
Human blackHuman = HumanFactory.createHuman(BlackHuman.class);
blackHuman.getColor();
blackHuman.talk();
System.out.println("--------------女娲第三次造人---------------");
Human yellowHuman = HumanFactory.createHuman(YellowHuman.class);
yellowHuman.getColor();
yellowHuman.talk();
}
}
运行结果没有发生变化,但是我们的类图变简单了,而且调用者也比较简单,该模式是 工厂方法模式的弱化,因为简单,所以称为简单工厂模式(Simple Factory Pattern),也叫做 静态工厂模式。在实际项目中,采用该方法的案例还是比较多的,其缺点是工厂类的扩展比 较困难,不符合开闭原则,但它仍然是一个非常实用的设计模式。
升级为多个工厂类
当我们在做一个比较复杂的项目时,经常会遇到初始化一个对象很耗费精力的情况,所有的产品类都放到一个工厂方法中进行初始化会使代码结构不清晰。考虑到需要结构清晰,我们就为每个产品定义一个创造者,然后调用者自己去选择与哪个工厂方法关联。我们还是以女娲造人为例,每个人种都有一个特定的八卦炉,分别造出黑色、黄色和白色人种,修改后的类图为:
多工厂模式的抽象工厂类:
public abstract class AbstractHumanFactory {
public abstract Human createHuman();
}
黑色人种的创建工厂实现:
public class BlackHumanFactory extends AbstractHumanFactory {
public Human createHuman() {
return new BlackHuman();
}
}
黄色人种的创建工厂实现:
public class YellowHumanFactory extends AbstractHumanFactory {
public Human createHuman() {
return new YellowHuman();
}
}
白色人种的创建工厂实现:
public class WhiteHumanFactory extends AbstractHumanFactory {
public Human createHuman() {
return new WhiteHuman();
}
}
场景类NvWa:
public class NvWa {
public static void main(String[] args) {
System.out.println("--------------女娲第一次造人---------------");
Human whiteHuman = (new WhiteHumanFactory()).createHuman();
whiteHuman.getColor();
whiteHuman.talk();
System.out.println("--------------女娲第二次造人---------------");
Human blackHuman = (new BlackHumanFactory()).createHuman();
blackHuman.getColor();
blackHuman.talk();
System.out.println("--------------女娲第三次造人---------------");
Human yellowHuman = (new YellowHumanFactory()).createHuman();
yellowHuman.getColor();
yellowHuman.talk();
}
}
在复杂的应用中一般采用多工厂的方法,然后在增加一个协调类,避免调用者与各个子工厂交流,协调类的作用是封装子工厂类,对高层模块提供统一的访问接口。
替代单例模式
UML类图:
非常简单的类图,Singleton定义了一个private的无参构造函数,目的是不允许通过new的方式创建对象。
单例类:
public class Singleton {
//不允许通过new生产一个对象
private Singleton() {
}
public void doSomething() {
//业务处理
}
}
Singleton保证不能通过正常渠道建立一个对象,所以我们通过反射机制来创建:
public class SingletonFactory {
private static Singleton singleton;
static {
try {
Class cl = Class.forName(Singleton.class.getName());
//获得无参构造
Constructor constructor = cl.getDeclaredConstructor();
//设置无参构造是可访问的
constructor.setAccessible(true);
//产生一个实例对象
singleton = (Singleton)constructor.newInstance();
} catch (Exception e) {
//异常处理
}
}
public static Singleton getSingleton() {
return singleton;
}
}
通过获得类构造器,然后设置访问权限,生成一个对象,然后提供外部访问,保证内存中的对象唯一。
延迟初始化
一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用。延迟初始化是工厂方法模式的一个扩展应用,其通用类图为:
延迟加载的工厂类:
public class ProductFactory {
private static final Map<String,Product> prMap = new HashMap();
public static synchronized Product createProduct(String type) throws Exception {
Product product = null;
//如果Map中已经有这个对象
if(prMap.containsKey(type)) {
product = prMap.get(type);
} else {
if(type.equals("Product1")) {
product = new ConcreteProduct1();
} else {
product = new ConcreteProduct2();
}
//同时把对象放到缓存容器中
prMap.put(type,product);
}
return product;
}
}