工厂模式进阶用法,如何动态选择对象?


工厂模式进阶用法,如何动态选择对象?

文章插图
前言工厂设计模式可能是最常用的设计模式之一,我想大家在自己的项目中都用到过 。可能你会不屑一顾,但这篇文章不仅仅是关于工厂模式的基本知识,更是讨论如何在运行时动态选择不同的方法进行执行,你们可以看看是不是和你们项目中用的一样?
小菜鸟的问题直接上例子说明,设计一个日志记录的功能,但是支持记录到不同的地方,例如:
  • 内存中
  • 磁盘上的文件
  • 数据库
  • 百度网盘等远程存储服务
面对这么一个需求,你会怎么做呢?我们先来看看小菜鸟的做法吧 。
  1. 小菜鸟创建了一个Logger类
【工厂模式进阶用法,如何动态选择对象?】class Logger {public void log(String message, String loggerMedium) {}}
  1. 小菜鸟想都不想,直接一通if else 。
class Logger {public void log(String message, String loggerMedium) {if (loggerMedium.equals("MEMORY")) {logInMemory(message);} else if (loggerMedium.equals("FILE")) {logOnFile(message);} else if (loggerMedium.equals("DB")) {logToDB(message);} else if (loggerMedium.equals("REMOTE_SERVICE")) {logToRemote(message);}}private void logInMemory(String message) {// Implementation}private void logOnFile(String message) {// Implementation}private void logToDB(String message) {// Implementation}private void logToRemote(String message) {// Implementation}}现在突然说要增加一种存储介质FLASH_DRIVE,就要改了这个类?不拍改错吗?也不符合“开闭原则”,而且随着存储介质变多,类也会变的很大,小菜鸟懵逼了,不知道怎么办?
有没有更好的方法呢?这时候小菜鸟去找你帮忙,你一顿操作,改成了下面这样:
class InMemoryLog {public void logToMemory(String message) {// Implementation}}class FileLog {public void logToFile(String message) {//Implementation}}class DBLog {public void logToDB(String message) {// Implementation}}class RemoteServiceLog {public void logToService(String message) {// Implementation}}class Logger {private InMemoryLog mLog;private FileLog fLog;private DBLog dbLog;private RemoteServiceLog sLog;public Logger() {mLog = new InMemoryLog();fLog = new FileLog();dbLog = new DBLog();sLog = new RemoteServiceLog();}public void log(String message, String loggerMedium) {if (loggerMedium.equals("MEMORY")) {mLog.logToMemory(message);} else if (loggerMedium.equals("FILE")) {fLog.logToFile(message);} else if (loggerMedium.equals("DB")) {dbLog.logToDB(message);} else if (loggerMedium.equals("REMOTE_SERVICE")) {sLog.logToService(message);}}}在这个实现中,你已经将单独的代码分离到它们对应的文件中,但是Logger类与存储介质的具体实现紧密耦合,如FileLog、DBLog等 。随着存储介质的增加,类中将引入更多的实例Logger 。
还有什么更好的办法吗?你想了想,上面的实现都是直接写具体的实现类,是面向实现编程,更合理的做法是面向接口编程,接口意味着协议,契约,是一种更加稳定的方式 。
  1. 定义一个日志操作的接口
public interface LoggingOperation {void log(String message);}
  1. 实现这个接口
class InMemoryLog implements LoggingOperation {public void log(String message) {// Implementation}}class FileLog implements LoggingOperation {public void log(String message) {//Implementation}}class DBLog implements LoggingOperation {public void log(String message) {// Implementation}}class RemoteServiceLog implements LoggingOperation {public void log(String message) {// Implementation}}
  1. 你定义了一个类,据传递的参数,在运行时动态选择具体实现,这就是所谓的工厂类,不过是基础版 。
class LoggerFactory {public static LoggingOperation getInstance(String loggerMedium) {LoggingOperation op = null;switch (loggerMedium) {case "MEMORY":op = new InMemoryLog();break;case "FILE":op = new FileLog();break;case "DB":op = new DBLog();break;case "REMOTE_SERVICE":op = new RemoteServiceLog();break;}return op;}}
  1. 现在你的 Logger类的实现就是下面这个样子了 。
class Logger {public void log(String message, String loggerMedium) {LoggingOperation instance = LoggerFactory.getInstance(loggerMedium);instance.log(message);}}这里的代码变得非常统一,创建实际存储实例的责任已经转移到LoggerFactory,各个存储类只实现它们如何将消息记录到它们的特定介质,最后该类Logger只关心通过LoggerFactory将实际的日志记录委托给具体的实现 。这样,代码就很松耦合了 。你想要添加一个新的存储介质,例如FLASH_DRIVE,只需创建一个实现LoggingOperation接口的新类并将其注册到LoggerFactory中就好了 。这就是工厂模式可以帮助您动态选择实现的方式 。


推荐阅读