在 Java 中应用设计模式 - Factory Method
基本概念
FactoryMethod是一种创建性模式,它定义了一个创建对象的接口,但是却让子类来决定具体实例化哪一个类.当一个类无法预料要创建哪种类的对象或是一个类需要由子类来指定创建的对象时我们就需要用到Factory Method 模式了.简单说来,Factory Method可以根据不同的条件产生不同的实例,当然这些不同的实例通常是属于相同的类型,具有共同的父类.Factory Method把创建这些实例的具体过程封装起来了,简化了客户端的应用,也改善了程序的扩展性,使得将来可以做最小的改动就可以加入新的待创建的类. 通常我们将Factory Method作为一种标准的创建对象的方法,当发现需要更多的灵活性的时候,就开始考虑向其它创建型模式转化
简单分析
图1是Factory Method 模式的结构图,这里提供了一些术语,让我们可以进行更方便的描述:
- Product: 需要创建的产品的抽象类.
- ConcreteProduct: Product的子类,一系列具体的产品.
- Creator: 抽象创建器接口,声明返回Product类型对象的Factory Method.
- ConcreteCreator: 具体的创建器,重写Creator中的Factory Method,返回ConcreteProduct类型的实例.
图1: Factory Method 模式结构
由此可以清楚的看出这样的平行对应关系: 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.具体的创建过程委托�oShapeFactory来完成.
1.a 首先定义一个抽象类Shape,定义两个抽象的方法.
12345678910abstract class Shape {
// 勾画shape
public abstract void draw();
// 擦去 shape
public abstract void erase();
public String name;
public Shape(String aName){
name = aName;
}
}
1.b 定义 Shape的两个子类: Circle, Square,实现Shape中定义的抽象方法
1234567891011121314151617181920212223242526// 圆形子类
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);
}
}
1.c 定义抽象的创建器,anOperation调用factoryMethod创建一个对象,并对该对象进行一系列操作.
12345678910abstract 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();
}
}
1.d 定义与circle和square相对应的两个具体创建器CircleFactory,SquareFactory,实现父类的methodFactory方法
123456789101112131415// 定义返回 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)");
}
}
1.e 测试类:请注意这个客户端程序多么简洁,既没有罗嗦的条件判断语句,也无需关心ConcreteProduct和ConcreteCreator的细节(因为这里我用anOperation封装了Product里的两个方法,所以连Product的影子也没看见,当然把Product里方法的具体调用放到客户程序中也是不错的).
12345678class Main {
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.
- 参数化的Factory Method: 这种方式依靠指定的参数作为标志来创建对应的实例,这是很常见的一种办法.比如JFC中的BorderFactory就是个很不错的例子. 以下的这个例子是用字符串作为标记来进行判断的,如果参数的类型也不一样,那就可以用到过载函数来解决这个问题,定义一系列参数和方法体不同的同名函数,这里java.util.Calendar.getInstance()又是个极好的例子.参数化的创建方式克服了Factory Method模式一个最显著的缺陷,就是当具体产品比较多时,我们不得不也建立一系列与之对应的具体构造器. 但是在客户端我们必须指定参数来决定要创建哪一个类.
2.a 我们在第一种方法的基础上进行修改,首先自定义一个的异常,这样当传入不正确的参数时可以得到更明显的报错信息.
12345class NoThisShape extends Exception {
public NoThisShape(String aName) {
super(aName);
}
}
2.b去掉了ShapeFactory的两个子类,改为由ShapeFactory直接负责实例的创建. ShapeFactory自己变成一个具体的创建器,直接用参数化的方法实现factoryMethod返回多种对象.
1234567891011121314151617181920abstract 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();
}
}
2.c 测试类:这里客户端必须指定参数来决定具体创建哪个类.这个例子里的anOperation是静态函数,可以直接引用.
1234567class Main {
public static void main(String[] args) throws NoThisShape{
ShapeFactory.anOperation("Shape one","circle");
ShapeFactory.anOperation("Shape two","square");
ShapeFactory.anOperation("Shape three", "delta");
}
}
运行结果如下:
12345678910The 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.java:10)
at ShapeFactory.anOperation(ShapeFactory.java:15)
at Main.main(Main.java:5)
- 动态装载机制:
有的时候我们会把ConcreteProduct的实例传给创建器作为参数,这种情况下,如果在创建器里完成创建过程,就必须判断参数的具体类型(用instanceof),然后才能产生相应的实例,那么比较好的做法是利用Java的动态装载机制来完成这件事.比如:
我们得到一个Shape的子类s,但不知道具体是那个子类,就可以利用Class类自带的方法newInstance()得到实例
return (Shape)s.getClass().newInstance();
这种方法有兴趣得读者可以自己尝试,限于篇幅,不写具体代码出来了.
后话:
看完这篇文章后,相信读者对Factory Method模式有一个比较清楚的了解了.我想说的是,我们不仅应该关心一个具体的模式有什么作用,如何去实现这个模式,更应该透过现象看本质,不但知其然,还要知其所以然.要通过对模式的学习加深对面向对象思想的理解,让自己的认识得到升华.Factory Method模式看似简单,实则深刻.抽象,封装,继承,委托,多态,针对接口编程等面向对象中的概念都在这里得到了一一的体现.只有抓住了它的本质,我们才能够不拘于形式的灵活运用,而不是为了使用模式而使用模式.
相关主题
- Thinking in Pattern with Java ---- Bruce Eckel
- The Factory Method Design Pattern by Gopalan Suresh Raj----http://gsraj.tripod.com/design/creational/factory/factory.html
- SENG609_40 FACTORY PATTERNS PAPER----http://sern.ucalgary.ca/~kjfu/courses/SENG60904/paper.html
- Factory Method Pattern---- http:// www.ugolandini.net/FactoryMethodPattern.html
- Design Patterns in Java ---- Bob Tarr
- Design Patterns ---- Gang of Four
- Dynamic Class Loading in Java---- http:// www.pramodx.20m.com/dynamic_class_loading_in_java.htm
在 Java 中应用设计模式 - Factory Method相关推荐
- java复习系列[5] - Java 中的设计模式
文章目录 Java中的设计模式 设计原则 迭代器模式 + Iterator 适配器模式 + HandlerAdapter 代理模式 + AOP 单例模式 + Spring Bean 模板方法模式 + ...
- JAVA 中的设计模式(一)
JAVA 中的设计模式(一) 1.1.创建型模式(5种) 1.1.1.单列模式:在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在.这样的模式有几个好处: 1.某些类创建比较频繁, ...
- Java 中的设计模式详细介绍
设计模式 第 2 页 目 录 1. 策略模式[Strategy Pattern] ........................................................ ...
- java中策略设计模式_Java中的设计模式(五):策略模式
策略设计模式是行为设计模式之一.当我们为特定任务使用多个算法时,使用策略模式,客户端决定在运行时使用的实际实现. 策略模式的最佳示例之一是Collections.sort()采用Comparator参 ...
- Java中的设计模式:“代理模式”的理解
代理模式定义: 为其他对象提供一种代理以控制对这个对象的访问.在面向对象中,有时候直接访问一些对象比较麻烦,所以代理模式就是在这个对象上加上一个访问该对象的访问层.类似于很多明星的事务实际都是交给经纪 ...
- enclosing type java_两个问题,1.java中什么是函数的signature,2.java中什么是enclosing method...
展开全部 方法的签名可以唯一的确定这个函数 方法签名由方法名称和一个参数列表32313133353236313431303231363533e4b893e5b19e31333335333662(方法的 ...
- Java 中的设计模式
1.设计模式概念 1.1 什么地方可以用到设计模式 面向对象(OO)=>功能模块[设计模式+算法(数据结构)]=>框架[使用多种设计模式]=>架构[服务器集群] 复制代码 1.2 使 ...
- 【Java】Java中的设计模式的介绍以及代码详解
一.什么是设计模式? 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案.这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误 总结出来的. 对问题行之有效地解决方式,是一种设计 ...
- java中的设计模式
背景:讲设计模式的时候,最好能够结合源码来进行理解,逼格更高 策略模式 泛型接口比较实用的使用场景就是用作策略模式的公共策略,比如 Java 解惑:Comparable 和 Comparator 的区 ...
最新文章
- 来自mooon的最简单的日志类CSimpleLogger
- 机器学习(2)--感知机
- 我长了一条日本制的尾巴:智能配合身体运动,增强平衡感,把我变成“改造人”| SIGGRAPH...
- U3D prefab
- 45.Android 第三方开源库收集整理(转)
- studiolibrary安装_DAZ Studio 3D扩展包安装使用教程
- Windows 平台下Git 服务器搭建
- hadoop搭建和指令
- matlab如何测两点的角度_matlab求两直线的夹角
- 机器学习 | 实战(一)Decision_tree_红酒数据集
- ferguson博弈_组合博弈游戏
- echarts 饼图悬浮频繁闪动
- CSU-ACM2019寒假训练1-E - 可能简单题
- java 开源 cms系统_基于Java的开源CMS系统选择(转)
- 1003. Universal Travel Sites (35)
- c语言scanf(%d%d,ab)解释与EOF的使用
- 提供最全面最详细的ESP32从零开始搭建一个物联网平台教程(从最基本的配网和内建WEB服务器开始到自已搭建一个MQTT服务器)
- juicer.js的使用
- 160W有桥PFC+LLC电源设计
- 逃课去吃鸡?!!!,strcpy
热门文章
- Java Annotations: Explored Explained--转载
- Java 编程的动态性, 第4部分: 用 Javassist 进行类转换--转载
- MySql中把一个表的数据插入到另一个表中的实现代码--转
- 转载--redis密码管理
- 【ETL】ETL讲解(很详细!!!)
- 【采用】【风控系统】风控中心—京东基于Spark的风控系统架构实践和技术细节
- 供应链金融与区块链技术-可以研读
- Spring Cloud Alibaba - 07 Ribbon 应用篇及内置的负载均衡算法
- 实战SSM_O2O商铺_17【商铺编辑】Dao层开发
- java web 播放音频_使用Java ME以流形式播放Web服务器上的音乐文件