FactoryMethod是一种创建性模式,它定义了一个创建对象的接口,但是却让子类来决定具体实例化哪一个类.当一个类无法预料要创建哪种类的对象或是一个类需要由子类来指定创建的对象时我们就需要用到Factory Method 模式了.简单说来,Factory Method可以根据不同的条件产生不同的实例,当然这些不同的实例通常是属于相同的类型,具有共同的父类.Factory Method把创建这些实例的具体过程封装起来了,简化了客户端的应用,也改善了程序的扩展性,使得将来可以做最小的改动就可以加入新的待创建的类. 通常我们将Factory Method作为一种标准的创建对象的方法,当发现需要更多的灵活性的时候,就开始考虑向其它创建型模式转化
图1是Factory Method 模式的结构图,这里提供了一些术语,让我们可以进行更方便的描述:
- Product: 需要创建的产品的抽象类.
- ConcreteProduct: Product的子类,一系列具体的产品.
- Creator: 抽象创建器接口,声明返回Product类型对象的Factory Method.
- ConcreteCreator: 具体的创建器,重写Creator中的Factory Method,返回ConcreteProduct类型的实例.
由此可以清楚的看出这样的平行对应关系: Product <====> Creator ; ConreteProduct <====> ConreteCreator
抽象产品对应抽象创建器,具体产品对应具体创建器.这样做的好处是什么呢?为什么我们不直接用具体的产品和具体的创建器完成需求呢?实际上我们也可以这样做.但通过Factory Method模式来完成,客户(client)只需引用抽象的Product和Creater,对具体的ConcreteProduct和ConcreteCreator可以毫不关心,这样做我们可以获得额外的好处:
- 首先客户端可以统一从抽象创建器获取产生的实例,Creator的作用将client和产品创建过程分离开来,客户不用操心返回的是那一个具体的产品,也不用关心这些产品是如何创建的.同时,ConcreteProduct也被隐藏在Product后面,ConreteProduct继承了Product的所有属性,并实现了Product中定义的抽象方法,按照Java中的对象造型(cast)原则,通过ConcreteCreator产生的ConcreteProduct可以自动的上溯造型成Product.这样一来,实质内容不同的ConcreteProduct就可以在形式上统一为Product,通过Creator提供给client来访问.
- 其次,当我们添加一个新的ConcreteCreator时,由于Creator所提供的接口不变,客户端程序不会有丝毫的改动,不会带来动一发而牵全身的灾难, 这就是良好封装性的体现.但如果直接用ConcreteProduct和ConcreteCreator两个类是无论如何也做不到这点的. 优良的面向对象设计鼓励使用封装(encapsulation)和委托(delegation),而Factory Method模式就是使用了封装和委托的典型例子,这里封装是通过抽象创建器Creator来体现的,而委托则是通过抽象创建器把创建对象的责任完全交给具体创建器ConcreteCreator来体现的.
现在,请再回头看看基本概念中的那段话,开始也许觉得生涩难懂,现在是不是已经明朗化了很多.
工厂方法模式的引进
工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。下面是工厂方法模式的缩略类图:
在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做。这个核心工厂则变为抽象工厂角色,仅负责给出具工厂子类必须实现的接口,而不接触哪一产品创建的细节。
这种抽象的结果,使这种工厂方法模式可以用来允许系统不修改具体工厂角色的情况下引进新产品,这一特点无疑使得工厂模式具有超过简单工厂模式的优越性。
工厂方法模式的结构
在工厂方法模式中,一般都有一个平行的等级结构,也就是说工厂和产品是对应的的。抽象工厂对应抽象产品,具体工厂对应具体产品。简单的示意图如下:
从上图可以看出,工厂方法模式的系统涉及到了以下角色:
抽象工厂角色:与应用程序无关,任何在模式中创建对象的工厂必须实现这个接口。
具体工厂角色:实现了抽象工厂接口的具体Java类,含有与引用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。
抽象产品角色:工厂方法所创建产品对象的超类型,也就是产品对象的共同父类或共同拥有的接口。
具体产品角色:这个角色实现了抽象产品角色所声名的接口。工厂方法所创建的每个具体产品对象都是某个具体产品角色的实例。
先说明一点,用Factory Method模式创建对象并不一定会让我们的代码更短,实事上往往更长,我们也使用了更多的类,真正的目的在于这样可以灵活的,有弹性的创建不确定的对象.而且,代码的可重用性提高了,客户端的应用简化了,客户程序的代码会大大减少,变的更具可读性.
工厂方法模式在农场系统中的实现
背景
在简单工厂模式中,有个全能的园丁,控制所有作物的种植、生长和收获。现在农场规模变大了,管理更加专业化了。过去全能的园丁没有了,每一种作物都有专门的园丁管理,形成了规模化和专业化生产。
系统设计机构图
实现源码
水果产品接口Fruit.java
package com.lavasoft.patterns.factorymethod.ybms;
public interface Fruit {
/**
* 种植
*/
void plant();
/**
* 生长
*/
void grow();
/**
* 收获
*/
void harvest();
}
具体产品苹果Apple.java
package com.lavasoft.patterns.factorymethod.ybms;
public class Apple implements Fruit {
private int treeAge;
/**
* 种植
*/
public void plant() {
System.out.println("Apple has been planted.");
}
/**
* 生长
*/
public void grow() {
System.out.println("Apple is growing...");
}
/**
* 收获
*/
public void harvest() {
System.out.println("Apple has been harvested.");
}
/**
* @return 返回树龄
*/
public int getTreeAge() {
return treeAge;
}
/**
* 设置树龄
*/
public void setTreeAge(int treeAge) {
this.treeAge = treeAge;
}
}
Grape.java
package com.lavasoft.patterns.factorymethod.ybms;
public class Grape implements Fruit {
private boolean seedless; //是否有籽
/**
* 种植
*/
public void plant() {
System.out.println("Grape has been planted.");
}
/**
* 生长
*/
public void grow() {
System.out.println("Grape is growing...");
}
/**
* 收获
*/
public void harvest() {
System.out.println("Grape has been harvested.");
}
/**
* @return 是否有籽
*/
public boolean getSeedless() {
return seedless;
}
/**
* 有无籽的赋值方法
*/
public void setSeedless(boolean seedless) {
this.seedless = seedless;
}
/**
* 辅助方法
*/
public static void log(String msg) {
System.out.println(msg);
}
}
具体产品草莓:Strawberry.java
package com.lavasoft.patterns.factorymethod.ybms;
public class Strawberry implements Fruit {
/**
* 生长
*/
public void grow() {
System.out.println("Strawberry is growing...");
}
/**
* 收获
*/
public void harvest() {
System.out.println("Strawberry has been harvested.");
}
/**
* 种植
*/
public void plant() {
System.out.println("Strawberry has been planted.");
}
/**
* 辅助方法
*/
public static void log(String msg) {
System.out.println(msg);
}
}
水果工厂接口:FruitGardener.java
package com.lavasoft.patterns.factorymethod.ybms;
public interface FruitGardener {
/**
* 工厂方法
*
* @return 水果
*/
public Fruit factory();
}
苹果工厂:AppleGardener.java
package com.lavasoft.patterns.factorymethod.ybms;
public class AppleGardener implements FruitGardener {
/**
* 工厂方法
*
* @return 苹果
*/
public Fruit factory() {
Fruit f = new Apple();
System.out.println("水果工厂(AppletGardener)成功创建一个水果:苹果!");
return f;
}
}
葡萄工厂:GrapeGardener.java
package com.lavasoft.patterns.factorymethod.ybms;
public class GrapeGardener implements FruitGardener {
/**
* 工厂方法
*
* @return 葡萄
*/
public Fruit factory() {
Fruit f = new Grape();
System.out.println("水果工厂(GrapeGardener)成功创建一个水果:葡萄!");
return f;
}
}
草莓工厂:StrawberryGardener.java
package com.lavasoft.patterns.factorymethod.ybms;
public class StrawberryGardener implements FruitGardener {
/**
* 工厂方法
*
* @return 草莓
*/
public Fruit factory() {
Fruit f = new Strawberry();
System.out.println("水果工厂(StrawberryGardener)成功创建一个水果:草莓!");
return f;
}
}
测试类(客户端):TestApp.java
package com.lavasoft.patterns.factorymethod.ybms;
public class TestApp {
private FruitGardener f1, f2, f3;
private Fruit p1, p2, p3;
private void test() {
//实力化水果工厂
f1 = new AppleGardener();
f2 = new GrapeGardener();
f3 = new StrawberryGardener();
//从水果工厂生产水果
p1 = f1.factory();
p2 = f2.factory();
p3 = f3.factory();
}
public static void main(String args[]) {
TestApp test = new TestApp();
test.test();
}
}
测试运行结果
水果工厂(AppletGardener)成功创建一个水果:苹果!
水果工厂(GrapeGardener)成功创建一个水果:葡萄!
水果工厂(StrawberryGardener)成功创建一个水果:草莓!
Process finished with exit code 0
关于工厂方法模式的实现
在下面我简单说说一些实现方式,不做详细分析了。
1.使用java接口或者java抽象类
2.使用多个工厂方法
3.产品循环使用
4.多态性的丧失和模式的退化,主要体现在工厂方法创建对象、工厂方法返回的类型、工厂等级结构三个方面。
女娲举绳造人
女娲举绳造人是工厂方法模式的一个实例。下面仅仅给出设计图如下,具体编码请您自己实现。