3 完整解决方案
Sunny公司开发人员决定使用工厂方法模式来设计日志记录器,其基本结构如图3所示:
图3 日志记录器结构图
在图3中,Logger接口充当抽象产品,其子类FileLogger和DatabaseLogger充当具体产品,LoggerFactory接口充当抽象工厂,其子类FileLoggerFactory和DatabaseLoggerFactory充当具体工厂。完整代码如下所示:
//日志记录器接口:抽象产品
interface Logger {
public void writeLog();
}
//数据库日志记录器:具体产品
class DatabaseLogger implements Logger {
public void writeLog() {
System.out.println("数据库日志记录。");
}
}
//文件日志记录器:具体产品
class FileLogger implements Logger {
public void writeLog() {
System.out.println("文件日志记录。");
}
}
//日志记录器工厂接口:抽象工厂
interface LoggerFactory {
public Logger createLogger();
}
//数据库日志记录器工厂类:具体工厂
class DatabaseLoggerFactory implements LoggerFactory {
public Logger createLogger() {
//连接数据库,代码省略
//创建数据库日志记录器对象
Logger logger = new DatabaseLogger();
//初始化数据库日志记录器,代码省略
return logger;
}
}
//文件日志记录器工厂类:具体工厂
class FileLoggerFactory implements LoggerFactory {
public Logger createLogger() {
//创建文件日志记录器对象
Logger logger = new FileLogger();
//创建文件,代码省略
return logger;
}
}
编写如下客户端测试代码:
class Client {
public static void main(String args[]) {
LoggerFactory factory;
Logger logger;
factory = new FileLoggerFactory(); //可引入配置文件实现
logger = factory.createLogger();
logger.writeLog();
}
}
编译并运行程序,输出结果如下:
4 反射与配置文件
为了让系统具有更好的灵活性和可扩展性,Sunny公司开发人员决定对日志记录器客户端代码进行重构,使得可以在不修改任何客户端代码的基础上更换或增加新的日志记录方式。
在客户端代码中将不再使用new关键字来创建工厂对象,而是将具体工厂类的类名存储在配置文件(如XML文件)中,通过读取配置文件获取类名字符串,再使用Java的反射机制,根据类名字符串生成对象。在整个实现过程中需要用到两个技术:Java反射机制与配置文件读取。软件系统的配置文件通常为XML文件,我们可以使用DOM (Document Object Model)、SAX (Simple API for XML)、StAX (Streaming API for XML)等技术来处理XML文件。关于DOM、SAX、StAX等技术的详细学习大家可以参考其他相关资料,在此不予扩展。
|
扩展
关于Java与XML的相关资料,大家可以阅读Tom Myers和Alexander Nakhimovsky所著的《Java XML编程指南》一书或访问developer Works 中国中的“Java XML 技术专题”,参考链接:
http://www.ibm.com/developerworks/cn/xml/theme/x-java.html
|
|
Java反射(Java Reflection)是指在程序运行时获取已知名称的类或已有对象的相关信息的一种机制,包括类的方法、属性、父类等信息,还包括实例的创建和实例类型的判断等。在反射中使用最多的类是Class,Class类的实例表示正在运行的Java应用程序中的类和接口,其forName(String className)方法可以返回与带有给定字符串名的类或接口相关联的 Class对象,再通过Class对象的newInstance()方法创建此对象所表示的类的一个新实例,即通过一个类名字符串得到类的实例。如创建一个字符串类型的对象,其代码如下:
//通过类名生成实例对象并将其返回
Class c=Class.forName("String");
Object obj=c.newInstance();
return obj;
此外,在JDK中还提供了java.lang.reflect包,封装了其他与反射相关的类,此处只用到上述简单的反射代码,在此不予扩展。
Sunny公司开发人员创建了如下XML格式的配置文件config.xml用于存储具体日志记录器工厂类类名:
<!— config.xml -->
<?xml version="1.0"?>
<config>
<className>FileLoggerFactory</className>
</config>
为了读取该配置文件并通过存储在其中的类名字符串反射生成对象,Sunny公司开发人员开发了一个名为XMLUtil的工具类,其详细代码如下所示:
//工具类XMLUtil.java
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;
public class XMLUtil {
//该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
public static Object getBean() {
try {
//创建DOM文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("config.xml"));
//获取包含类名的文本节点
NodeList nl = doc.getElementsByTagName("className");
Node classNode=nl.item(0).getFirstChild();
String cName=classNode.getNodeValue();
//通过类名生成实例对象并将其返回
Class c=Class.forName(cName);
Object obj=c.newInstance();
return obj;
}
catch(Exception e) {
e.printStackTrace();
return null;
}
}
}
有了XMLUtil类后,可以对日志记录器的客户端代码进行修改,不再直接使用new关键字来创建具体的工厂类,而是将具体工厂类的类名存储在XML文件中,再通过XMLUtil类的静态工厂方法getBean()方法进行对象的实例化,代码修改如下:
class Client {
public static void main(String args[]) {
LoggerFactory factory;
Logger logger;
factory = (LoggerFactory)XMLUtil.getBean(); //getBean()的返回类型为Object,需要进行强制类型转换
logger = factory.createLogger();
logger.writeLog();
}
}
引入XMLUtil类和XML配置文件后,如果要增加新的日志记录方式,只需要执行如下几个步骤:
(1) 新的日志记录器需要继承抽象日志记录器Logger;
(2) 对应增加一个新的具体日志记录器工厂,继承抽象日志记录器工厂LoggerFactory,并实现其中的工厂方法createLogger(),设置好初始化参数和环境变量,返回具体日志记录器对象;
(3) 修改配置文件config.xml,将新增的具体日志记录器工厂类的类名字符串替换原有工厂类类名字符串;
(4) 编译新增的具体日志记录器类和具体日志记录器工厂类,运行客户端测试类即可使用新的日志记录方式,而原有类库代码无须做任何修改,完全符合“开闭原则”。
通过上述重构可以使得系统更加灵活,由于很多设计模式都关注系统的可扩展性和灵活性,因此都定义了抽象层,在抽象层中声明业务方法,而将业务方法的实现放在实现层中。
|
思考
有人说:可以在客户端代码中直接通过反射机制来生成产品对象,在定义产品对象时使用抽象类型,同样可以确保系统的灵活性和可扩展性,增加新的具体产品类无须修改源代码,只需要将其作为抽象产品类的子类再修改配置文件即可,根本不需要抽象工厂类和具体工厂类。
试思考这种做法的可行性?如果可行,这种做法是否存在问题?为什么?
|
|
【作者:刘伟 http://blog.csdn.net/lovelion】
分享到:
相关推荐
Factory Method Pattern 工厂三兄弟之工厂方法模式(一) 工厂三兄弟之工厂方法模式(二) 工厂三兄弟之工厂方法模式(三) 工厂三兄弟之工厂方法模式(四) 抽象工厂模式-Abstract Factory Pattern 工厂三兄弟之...
- **工厂三兄弟之简单工厂模式**:简单工厂模式通过一个工厂类负责创建所有实例,简化了客户端的使用方式。 2. **工厂方法模式**:定义了一个用于创建对象的接口,但允许子类决定实例化哪一个类。 - **工厂三兄弟...
中文版的设计模式,包括工厂模式、策略模式等,给打家提供设计上的参考。卷三
《兄弟一体机DCP-7010-7025维修手册》是针对兄弟公司推出的DCP-7010和DCP-7025型号一体机的专业维修指导资料,旨在帮助用户和维修技术人员解决设备在使用过程中可能出现的各种问题。这份手册详细阐述了这两款多功能...
1. **工厂三兄弟之简单工厂模式** - **目的**:通过一个工厂类根据传入的参数决定实例化哪个具体的产品类。 - **优点**:客户端不需要知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可。 - *...
随便说说最近项目中的三层架构吧。讲点实际的东西。我最讨厌空讲道理。... 呵呵,下次再讲讲业务逻辑方面吧,希望给做DELPHI三层的兄弟一个参考。我的QQ: 11718111 Email: wu_yanan2003@yahoo.com.cn
4. **影响**:生产力大幅提高,工业生产模式从手工工场转变为大机器生产,社会结构发生变化,催生了现代工厂制度,推动了全球贸易和殖民扩张,同时也加剧了社会贫富差距。 **第二次科技革命(电力革命)** 1. **...
设计模式部分可能包括了创建型模式(如工厂模式、单例模式、建造者模式等)、结构型模式(如适配器模式、装饰器模式、代理模式等)、行为型模式(如策略模式、观察者模式、迭代器模式等)的定义、特点、使用场景和...
- 子女-兄弟链接法和层次序列链接法都是用来表示层次数据库中实体间关系的方法。具体存储结构示意图需参考教材中的图形表示。 以上内容覆盖了数据库系统概论第一章中的主要知识点,希望能帮助读者更好地理解和掌握...
我的家乡,曾是一片碧波荡漾、绿树成荫的宁静之地。记忆里,春天,燕子兄弟掠过水面,衔泥造窝;夏日,孩子们在河里嬉戏捉鱼;秋风起时,燕子兄弟飞往北方;而冬日,河面结冰,鲤鱼弟弟静卧水底。这样的四季更迭,...
12.3.1 静态工厂方法 231 12.3.2 isNaN方法 232 12.3.3 equals方法 233 12.4 自动打包/解包 -235 12.4.1 自动打包 235 12.4.2 自动解包 236 12.5 特殊的数值计算 237 12.5.1 特大整数的计算 237 ...
6.5.2 模式--函数工厂及其实例 6.6 总结 第7章 对象 7.1 什么是对象 7.2 对象的属性和方法 7.2.1 对象的内置属性 7.2.2 为对象添加和删除属性 7.2.3 反射机制--枚举对象属性 7.3 ...
9-28 7 给对象指定成员函数 自定义工厂方法 9-30 1 课程回顾 9-30 2 javascript的闭包 js变量作用域 9-30 3 仿超级玛丽兄弟游戏制作 9-30 4 构造方法 对象的常用操作 9-30 5 面向对象的封装 继承 多态 9-30 6 面向...
9-28 7 给对象指定成员函数 自定义工厂方法 9-30 1 课程回顾 9-30 2 javascript的闭包 js变量作用域 9-30 3 仿超级玛丽兄弟游戏制作 9-30 4 构造方法 对象的常用操作 9-30 5 面向对象的封装 继承 多态 9-30 6 面向...
9-28 7 给对象指定成员函数 自定义工厂方法 9-30 1 课程回顾 9-30 2 javascript的闭包 js变量作用域 9-30 3 仿超级玛丽兄弟游戏制作 9-30 4 构造方法 对象的常用操作 9-30 5 面向对象的封装 继承 多态 9-30 6 面向...
9-28 7 给对象指定成员函数 自定义工厂方法 9-30 1 课程回顾 9-30 2 javascript的闭包 js变量作用域 9-30 3 仿超级玛丽兄弟游戏制作 9-30 4 构造方法 对象的常用操作 9-30 5 面向对象的封装 继承 多态 9-30 6 面向...
9-28 7 给对象指定成员函数 自定义工厂方法 9-30 1 课程回顾 9-30 2 javascript的闭包 js变量作用域 9-30 3 仿超级玛丽兄弟游戏制作 9-30 4 构造方法 对象的常用操作 9-30 5 面向对象的封装 继承 多态 9-30 6 面向...