设计模式-工厂方法
工厂方法模式
简单工厂的不足
上节的简单工厂,需要拓展时比如修改工厂类,违背了设计模式的开闭原则
简单工厂类直接生成各个子类产品,而工厂方法则有一个抽象工厂类,声明了创建产品的工厂方法,而各个不同的子类产品交由各个不同的具体工厂去完成创建,拓展时,只需要新建一个具体工厂即可,具有更好的灵活性和拓展性
在工厂方法模式中,存在4个角色:
- 抽象产品
- 具体产品
- 抽象工厂
声明了创建产品的抽象方法,返回抽象产品,由具体工厂去实现创建具体产品的方法 - 具体工厂
实现创建产品的抽象方法,创建并返回某一种具体产品
工厂方法模式下的多日志工厂
Log(抽象产品)
抽象Log声明了一个记录日志的方法
package com.example.fxfactory.model;
import java.io.IOException;
public abstract class Log {
public abstract void doLog() throws IOException;
}
ConsoleLog(具体产品)
Log的具体类,控制台日志,它将doLog重写为在控制台打印日志信息
package com.example.fxfactory.model;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ConsoleLog extends Log{
@Override
public void doLog() {
Date date = new Date();
SimpleDateFormat pattern = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String str = pattern.format(date) + " console log";
System.out.println(str);
}
}
FileLog(具体产品)
Log的另一个具体类,文件日志,它将doLog重写为记录日志到filelog.txt文件中去
package com.example.fxfactory.model;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FileLog extends Log {
@Override
public void doLog() throws IOException {
File dir = new File("./log");
if (!dir.exists()) {
dir.mkdirs();
}
File checkFile = new File("./log","filelog.txt");
FileWriter writer = null;
try {
if (!checkFile.exists()) {
checkFile.createNewFile();
}
writer = new FileWriter(checkFile, true);
Date date = new Date();
SimpleDateFormat pattern = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String str = pattern.format(date) + " file log";
writer.append(str);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != writer)
writer.close();
}
}
}
LogFactory(抽象工厂)
抽象的日志工厂,声明了一个创建日志的方法,返回类型为抽象的日志
package com.example.fxfactory.factory;
import com.example.fxfactory.model.Log;
public abstract class LogFactory {
public abstract Log createLog();
}
ConsoleLogFactory(具体工厂)
控制台日志工厂,重写创建日志方法,返回一个控制台日志实例
package com.example.fxfactory.factory;
import com.example.fxfactory.model.ConsoleLog;
import com.example.fxfactory.model.Log;
public class ConsoleLogFactory extends LogFactory {
@Override
public Log createLog() {
return new ConsoleLog();
}
}
FileLogFactory(具体工厂)
文件日志工厂,重写创建日志方法,返回一个文件日志实例
package com.example.fxfactory.factory;
import com.example.fxfactory.model.FileLog;
import com.example.fxfactory.model.Log;
public class FileLogFactory extends LogFactory {
@Override
public Log createLog() {
return new FileLog();
}
}
测试
Main.java
package com.example.fxfactory;
import com.example.fxfactory.factory.ConsoleLogFactory;
import com.example.fxfactory.factory.FileLogFactory;
import com.example.fxfactory.factory.LogFactory;
import com.example.fxfactory.model.Log;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
LogFactory logFactory = new ConsoleLogFactory();
Log consoleLog = logFactory.createLog();
consoleLog.doLog();
logFactory = new FileLogFactory();
Log fileLog = logFactory.createLog();
fileLog.doLog();
}
}
看起来,我们似乎并不需要LogFactory,直接创建2个不同的具体Factory不也行吗?那我们尝试换一种写法:
空工厂
抽象工厂换为一个空工厂,createLog方法返回null,新增一个切换到具体工厂的方法,接收具体工厂名,将自己变为指定的具体工厂
package com.example.fxfactory.factory;
import com.example.fxfactory.model.Log;
public class LogFactory {
public LogFactory siwtch(String logType){
switch (logType){
case "console":{
return new ConsoleLogFactory();
}
case "file":{
return new FileLogFactory();
}
default:{
return this;
}
}
}
public Log createLog(){
return null;
}
}
测试
Main.java
LogFactory logFactory = new LogFactory();
logFactory = logFactory.siwtch("console");
Log consoleLog = logFactory.createLog();
consoleLog.doLog();
logFactory = logFactory.siwtch("file");
Log fileLog = logFactory.createLog();
fileLog.doLog();
现在我们不需要知道具体工厂类了,只需要知道用于指定它们的名字就行。不过要拓展新的具体工厂,还需要维护空工厂的switch方法
总结
工厂方法的优点
★ 客户无须关心生成细节和具体产品的类名
★ 良好的多态性:创建细节完全封装在具体工厂内
★ 易拓展性:无须修改抽象工厂,新增基于抽象工厂的新具体工厂类即可
工厂方法的缺点
★ 新产品和新具体工厂成对,一定程度上增加了系统复杂性
★ 双重抽象(产品和工厂),一定程度上增加了系统的抽象性和理解难度