class="article_content clearfix">
class="htmledit_views">
图 1: Factory Method 模式结构
在class="tags" href="/tags/SheJiMoShi.html" title=设计模式>设计模式中
,Factory Method
也是比较简单的一个
,
但应用非常广泛
,EJB,RMI,COM,CORBA,Swing
中都可以看到此模式的影子
,
它是最重要的模式之一
.
在很多地方我们都会看到
xxxFactory
这样命名的类
,
那么
,
什么是
Factory Method,
为什么要用这个模式
,
如何用
Java
语言来实现该模式
,
这就是本文想要带给大家的内容
.
基本概念
Factory Method
是一种创建性模式
,
它定义了一个创建对象的接口
,
但是却让子类来决定具体实例化哪一个类
.
当一个类无法预料要创建哪种类的对象或是一个类需要由子类来指定创建的对象时我们就需要用到
Factory Method
模式了
.
简单说来
,Factory Method
可以根据不同的条件产生不同的实例
,
当然这些不同的实例通常是属于相同的类型
,
具有共同的父类
.Factory Method
把创建这些实例的具体过程封装起来了
,
简化了客户端的应用
,
也改善了程序的扩展性
,
使得将来可以做最小的改动就可以加入新的待创建的类
.
通常我们将
Factory Method
作为一种标准的创建对象的方法
,
当发现需要更多的灵活性的时候
,
就开始考虑向其它创建型模式转化
简单分析
图
1
是
Factory Method
模式的结构图
,
这里提供了一些术语
,
让我们可以进行更方便的描述
:
图 1: Factory Method 模式结构
1
.
Product:
需要创建的产品的抽象类
.
2 . ConcreteProduct: Product 的子类 , 一系列具体的产品 .
3 . Creator: 抽象创建器接口 , 声明返回 Product 类型对象的 Factory Method.
4 . ConcreteCreator: 具体的创建器 , 重写 Creator 中的 Factory Method, 返回 ConcreteProduct 类型的实例 .
2 . ConcreteProduct: Product 的子类 , 一系列具体的产品 .
3 . Creator: 抽象创建器接口 , 声明返回 Product 类型对象的 Factory Method.
4 . 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
模式
,
进一步加深对它的认识
.
具体实施
先说明一点
,
用
Factory Method
模式创建对象并不一定会让我们的代码更短
,
实事上往往更长
,
我们也使用了更多的类
,
真正的目的在于这样可以灵活的
,
有弹性的创建不确定的对象
.
而且
,
代码的可重用性提高了
,
客户端的应用简化了
,
客户程序的代码会大大减少
,
变的更具可读性
.
标准实现
:
这里我采用
Bruce Eckel
用来描述
OO
思想的经典例子
Shape.
这样大家会比较熟悉一些
.
我完全按照图
1
中所定义的结构写了下面的一段演示代码
.
这段代码的作用是创建不同的
Shape
实例
,
每个实例完成两个操作
:draw
和
erase.
具体的创建过程委托給
ShapeFactory
来完成
.
1.a
首先定义一个抽象类
Shape,
定义两个抽象的方法
.
abstract class Shape {
// 勾画 shape
public abstract void draw();
// 擦去 shape
public abstract void erase();
// 勾画 shape
public abstract void draw();
// 擦去 shape
public abstract void erase();
public String name;
public Shape(String aName){
name = aName;
}
}
public Shape(String aName){
name = aName;
}
}
1.b
定义
Shape
的两个子类
: Circle, Square,
实现
Shape
中定义的抽象方法
//
圆形子类
class Circle extends Shape {
public void draw() {
System.out.println("It will draw a circle.");
}
public void erase() {
System.out.println("It will erase a circle.");
}
// 构造函数
public Circle(String aName){
super(aName);
}
}
class Circle extends Shape {
public void draw() {
System.out.println("It will draw a circle.");
}
public void erase() {
System.out.println("It will erase a circle.");
}
// 构造函数
public Circle(String aName){
super(aName);
}
}
//
方形子类
class Square extends Shape {
public void draw() {
System.out.println("It will draw a square.");
}
public void erase() {
System.out.println("It will erase a square.");
}
// 构造函数
public Square(String aName){
super(aName);
}
}
class Square extends Shape {
public void draw() {
System.out.println("It will draw a square.");
}
public void erase() {
System.out.println("It will erase a square.");
}
// 构造函数
public Square(String aName){
super(aName);
}
}
1.c
定义抽象的创建器
,anOperation
调用
factoryMethod
创建一个对象
,
并对该对象进行一系列操作
.
abstract class ShapeFactory {
protected abstract Shape factoryMethod(String aName);
// 在 anOperation 中定义 Shape 的一系列行为
public void anOperation(String aName){
Shape s = factoryMethod(aName);
System.out.println("The current shape is: " + s.name);
s.draw();
s.erase();
}
}
protected abstract Shape factoryMethod(String aName);
// 在 anOperation 中定义 Shape 的一系列行为
public void anOperation(String aName){
Shape s = factoryMethod(aName);
System.out.println("The current shape is: " + s.name);
s.draw();
s.erase();
}
}
1.d
定义与
circle
和
square
相对应的两个具体创建器
CircleFactory,SquareFactory,
实现父类的
methodFactory
方法
//
定义返回
circle
实例的
CircleFactory
class CircleFactory extends ShapeFactory {
// 重载 factoryMethod 方法 , 返回 Circle 对象
protected Shape factoryMethod(String aName) {
return new Circle(aName + " (created by CircleFactory)");
}
}
// 定义返回 Square 实例的 SquareFactory
class SquareFactory extends ShapeFactory {
// 重载 factoryMethod 方法 , 返回 Square 对象
protected Shape factoryMethod(String aName) {
return new Square(aName + " (created by SquareFactory)");
}
}
class CircleFactory extends ShapeFactory {
// 重载 factoryMethod 方法 , 返回 Circle 对象
protected Shape factoryMethod(String aName) {
return new Circle(aName + " (created by CircleFactory)");
}
}
// 定义返回 Square 实例的 SquareFactory
class SquareFactory extends ShapeFactory {
// 重载 factoryMethod 方法 , 返回 Square 对象
protected Shape factoryMethod(String aName) {
return new Square(aName + " (created by SquareFactory)");
}
}
1.e
测试类
:
请注意这个客户端程序多么简洁
,
既没有罗嗦的条件判断语句
,
也无需关心
ConcreteProduct
和
ConcreteCreator
的细节
(
因为这里我用
anOperation
封装了
Product
里的两个方法
,
所以连
Product
的影子也没看见
,
当然把
Product
里方法的具体调用放到客户程序中也是不错的
).
class Main {
public static void main(String[] args){
ShapeFactory sf1 = new SquareFactory();
ShapeFactory sf2 = new CircleFactory();
sf1.anOperation("Shape one");
sf2.anOperation("Shape two");
}
}
public static void main(String[] args){
ShapeFactory sf1 = new SquareFactory();
ShapeFactory sf2 = new CircleFactory();
sf1.anOperation("Shape one");
sf2.anOperation("Shape two");
}
}
运行结果如下
:
The current shape is: Shape one (created by SquareFactory)
It will draw a square.
It will erase a square.
The current shape is: Shape two (created by CircleFactory)
It will draw a circle.
It will erase a circle.
The current shape is: Shape one (created by SquareFactory)
It will draw a square.
It will erase a square.
The current shape is: Shape two (created by CircleFactory)
It will draw a circle.
It will erase a circle.
参数化的
Factory Method:
这种方式依靠指定的参数作为标志来创建对应的实例
,
这是很常见的一种办法
.
比如
JFC
中的
BorderFactory
就是个很不错的例子
.
以下的这个例子是用字符串作为标记来进行判断的
,
如果参数的类型也不一样
,
那就可以用到过载函数来解决这个问题
,
定义一系列参数和方法体不同的同名函数
,
这里
class="tags" href="/tags/JAVA.html" title=java>java.util.Calendar.getInstance()
又是个极好的例子
.
参数化的创建方式克服了
Factory Method
模式一个最显著的缺陷
,
就是当具体产品比较多时
,
我们不得不也建立一系列与之对应的具体构造器
.
但是在客户端我们必须指定参数来决定要创建哪一个类
.
2.a
我们在第一种方法的基础上进行修改
,
首先自定义一个的异常
,
这样当传入不正确的参数时可以得到更明显的报错信息
.
class NoThisShape extends Exception {
public NoThisShape(String aName) {
super(aName);
}
}
public NoThisShape(String aName) {
super(aName);
}
}
2.b
去掉了
ShapeFactory
的两个子类
,
改为由
ShapeFactory
直接负责实例的创建
. ShapeFactory
自己变成一个具体的创建器
,
直接用参数化的方法实现
factoryMethod
返回多种对象
.
abstract class ShapeFactory {
private static Shape s;
private ShapeFactory() {}
static Shape factoryMethod(String aName, String aType) throws NoThisShape{
if (aType.compareTo("square")==0)
return new Square(aName);
else if (aType.compareTo("circle")==0)
return new Circle(aName);
else throw new NoThisShape(aType);
}
// 在 anOperation 中定义 Shape 的一系列行为
static void anOperation(String aName, String aType) throws NoThisShape{
s = factoryMethod(aName, aType);
System.out.println("The current shape is: " + s.name);
s.draw();
s.erase();
}
}
private static Shape s;
private ShapeFactory() {}
static Shape factoryMethod(String aName, String aType) throws NoThisShape{
if (aType.compareTo("square")==0)
return new Square(aName);
else if (aType.compareTo("circle")==0)
return new Circle(aName);
else throw new NoThisShape(aType);
}
// 在 anOperation 中定义 Shape 的一系列行为
static void anOperation(String aName, String aType) throws NoThisShape{
s = factoryMethod(aName, aType);
System.out.println("The current shape is: " + s.name);
s.draw();
s.erase();
}
}
2.c
测试类
:
这里客户端必须指定参数来决定具体创建哪个类
.
这个例子里的
anOperation
是静态函数
,
可以直接引用
.
class Main {
public static void main(String[] args) throws NoThisShape{
ShapeFactory.anOperation("Shape one","circle");
ShapeFactory.anOperation("Shape two","square");
ShapeFactory.anOperation("Shape three", "delta");
}
}
public static void main(String[] args) throws NoThisShape{
ShapeFactory.anOperation("Shape one","circle");
ShapeFactory.anOperation("Shape two","square");
ShapeFactory.anOperation("Shape three", "delta");
}
}
运行结果如下
:
The current shape is: Shape one
It will draw a circle.
It will erase a circle.
The current shape is: Shape two
It will draw a square.
It will erase a square.
Exception in thread "main" NoThisShape: delta
at ShapeFactory.factoryMethod(ShapeFactory.class="tags" href="/tags/JAVA.html" title=java>java:10)
at ShapeFactory.anOperation(ShapeFactory.class="tags" href="/tags/JAVA.html" title=java>java:15)
at Main.main(Main.class="tags" href="/tags/JAVA.html" title=java>java:5)
The current shape is: Shape one
It will draw a circle.
It will erase a circle.
The current shape is: Shape two
It will draw a square.
It will erase a square.
Exception in thread "main" NoThisShape: delta
at ShapeFactory.factoryMethod(ShapeFactory.class="tags" href="/tags/JAVA.html" title=java>java:10)
at ShapeFactory.anOperation(ShapeFactory.class="tags" href="/tags/JAVA.html" title=java>java:15)
at Main.main(Main.class="tags" href="/tags/JAVA.html" title=java>java:5)
动态装载机制
:
有的时候我们会把
ConcreteProduct
的实例传给创建器作为参数
,
这种情况下
,
如果在创建器里完成创建过程
,
就必须判断参数的具体类型
(
用
instanceof),
然后才能产生相应的实例
,
那么比较好的做法是利用
Java
的动态装载机制来完成这件事
.
比如
:
我们得到一个
Shape
的子类
s,
但不知道具体是那个子类
,
就可以利用
Class
类自带的方法
newInstance()
得到实例
return (Shape)s.getClass().newInstance();
这种方法有兴趣得读者可以自己尝试
,
限于篇幅
,
不写具体代码出来了
.
后话
:
看完这篇文章后
,
相信读者对
Factory Method
模式有一个比较清楚的了解了
.
我想说的是
,
我们不仅应该关心一个具体的模式有什么作用
,
如何去实现这个模式
,
更应该透过现象看本质
,
不但知其然
,
还要知其所以然
.
要通过对模式的学习加深对面向对象思想的理解
,
让自己的认识得到升华
.Factory Method
模式看似简单
,
实则深刻
.
抽象
,
封装
,
继承
,
委托
,
多态
,
针对接口编程等面向对象中的概念都在这里得到了一一的体现
.
只有抓住了它的本质
,
我们才能够不拘于形式的灵活运用
,
而不是为了使用模式而使用模式
.