上节我们详细介绍了工厂方法模式,解决了复杂对象的创建问题。这次,我们的需求再次升级,我们需要生成具有层级关系的具体产品,并且同一层级具有由多个具体工厂生产的具体产品。对比工厂方法模式,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
抽象工厂模式
抽象工厂(Abstract Factory)模式:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,同样属于对象创建型模式。
抽象工厂模式的结构与工厂方法类似,只不过在原有的基础上拓展了层级概念。
- 抽象工厂(AbstractFactory):提供了产品的抽象类或接口。
- 具体工厂(ConcreteFactory):具体的创建类,实现具体生产产品的方法。
- 抽象产品(Product):产品的抽象创建类。
- 具体产品(ConcreteProduct):将抽象产品进行实现。
实际应用场景
换行符:在不同操作系统中,换行符也各有不同。像在Windows下,换行符是\r\n,而在Linux下则是\n。在这个场景中,操作系统充当一个超级工厂角色,Windows和Linux则是超级工厂下的具体工厂,换行符则是它们生产的具体产品。
数据库:不同的数据库有自己的数据库方言,但它们的基础都是SQL语言。
…
示例
在本文中,与上篇一样,我们还是采用日志工厂进行举例。
在抽象日志工厂中,我们有一个超级工厂,也就是日志工厂。日志工厂下面有两个小工厂,分别是Log4j工厂和Logback工厂,这两个工厂可以生产出不同风格的日志。同时我们日志的产品有:直接输出到控制台的日志,写入文件的日志。两个工厂实现日志的逻辑各不相同。下面我们开始写具体的代码。
首先我们先来创建超级工厂类。
Factory.java
package com.yeliheng.factory.abstractfactory;
/**
* 超级工厂
*/
public interface Factory {
public ConsoleLogger consoleLogger();
public FileLogger fileLogger();
}
超级工厂类中提供了两个接口:分别是控制台日志接口和文件接口。它告诉下面的工厂,只能生产这两种接口规范的产品。具体怎么进行生产,超级工厂并不关心。
然后,我们创建两家工厂,分别是Log4j工厂和Logback工厂。
Log4jFactory.java
package com.yeliheng.factory.abstractfactory;
/**
* Log4j工厂
*/
public class Log4jFactory implements Factory{
@Override
public ConsoleLogger consoleLogger() {
return new Log4jConsoleLogger();
}
@Override
public FileLogger fileLogger() {
return new Log4jFileLogger();
}
}
LogbackFactory.java
package com.yeliheng.factory.abstractfactory;
/**
* Logback工厂
*/
public class LogbackFactory implements Factory{
@Override
public ConsoleLogger consoleLogger() {
return new LogbackConsoleLogger();
}
@Override
public FileLogger fileLogger() {
return new LogbackFileLogger();
}
}
在这两家工厂中,我们实现了超级工厂提供的两个方法,并让其返回自身生产的具体产品类。
我们先来实现抽象的产品类。
ConsoleLogger.java
package com.yeliheng.factory.abstractfactory;
/**
* 抽象产品类: 控制台日志
*/
public interface ConsoleLogger{
//日志级别
void debug(String text);
void info(String text);
void warning(String text);
void error(String text);
}
FileLogger.java
package com.yeliheng.factory.abstractfactory;
/**
* 抽象产品类: 文件日志
*/
public interface FileLogger{
//日志级别
void debug(String text);
void info(String text);
void warning(String text);
void error(String text);
}
这两个抽象产品类都提供了四种不同级别的日志输出,分别是debug,info,warning,error。你会发现它们内容相同。在实际开发中,我们需要根据实际需求对其代码进行修改。这里为了方便演示与理解,仅仅通过名字和输出来区别“输出到控制台的日志”和“输出到文件的日志”,并没有真正使用文件流来将日志保存到磁盘。
现在,我们可以开始实现具体的产品类了。
Log4jConsoleLogger.java
package com.yeliheng.factory.abstractfactory;
/**
* 具体产品类
*/
public class Log4jConsoleLogger implements ConsoleLogger{
@Override
public void debug(String text) {
}
@Override
public void info(String text) {
System.out.println("--[INFO]-- Log4j输出的[控制台]日志: " + text);
}
@Override
public void warning(String text) {
}
@Override
public void error(String text) {
}
}
Log4jFileLogger.java
package com.yeliheng.factory.abstractfactory;
/**
* 具体产品类
*/
public class Log4jFileLogger implements FileLogger{
@Override
public void debug(String text) {
}
@Override
public void info(String text) {
System.out.println("--[INFO]-- Log4j输出的[文件]日志: " + text);
}
@Override
public void warning(String text) {
}
@Override
public void error(String text) {
}
}
在具体产品类中,我们通过输出风格的不同来区别是Log4j工厂还是Logback工厂生产的产品。
LogbackConsoleLogger.java和LogbackFileLogger.java的内容与Log4j的具体产品内容基本相同,仅仅只有输出的不同。完整的代码可前往Github获取。
准备好了工厂和产品,这个时候客户来了。客户要开始使用工厂生产出来的产品了。
我们开始编写Main类,以便于使用我们刚才写好的代码,让工厂开始工作。
Main.java
package com.yeliheng.factory.abstractfactory;
public class Main {
public static void main(String[] args) {
Factory log4jFactory = new Log4jFactory();
ConsoleLogger consoleLogger = log4jFactory.consoleLogger();
consoleLogger.info("测试日志");
FileLogger fileLogger = log4jFactory.fileLogger();
fileLogger.info("文件测试");
Factory logbackFactory = new LogbackFactory();
ConsoleLogger consoleLogger1 = logbackFactory.consoleLogger();
consoleLogger1.info("测试日志");
FileLogger fileLogger1 = logbackFactory.fileLogger();
fileLogger1.info("文件测试");
}
}
在Main方法中,我们分别对两家工厂生产的同一级别的四种产品进行测试。最后我们查看运行结果。
从运行结果可以看出,两家日志工厂都正确地输出了各自生产的日志,这就是抽象工厂模式的具体实现。
抽象工厂模式的优缺点
优点:
- 能够生成复杂的,多个层级的产品结构。
- 隔离了具体类的生成:系统不用关心具体产品如何创建,组合。这在项目开发中能够维持良好的拓展性。
缺点:
- 层级较为复杂带来的就是系统复杂性的提高。
- 开闭原则的倾斜性:增加新的工厂和产品等级结构容易,增加新的同级的产品需要改变工厂代码。
总结
抽象工厂模式在简单工厂和工厂方法的基础上进行升级,最重要的是类层级结构的产生,可以使调用者无需关心什么对象被创建,提高系统的内聚性。
本例完整的源码可参见: Github