一、工厂方法模式概述
工厂方法模式是为了弥补简单工厂模式的不足并且继承它的优点而延生出的一种设计模式,属于GoF中的一种。它能更好的符合开闭原则的要求
属于创建型设计模式,此模式的核心精神是封装类中变化的部分,提取其中个性化善变的部分为独立类,通过依赖注入以达到解耦、复用和方便后期维护拓展的目的。
它的核心结构有四个角色,分别是抽象工厂;具体工厂;抽象产品;具体产品
定义:定义了一个用于创建对象的接口,但是让子类决定将哪一个类实例化。即让类的实例化延迟到子类
二、工厂方法模式的结构与实现
1. 结构:
1)Product(抽象产品):定义产品的接口,是工厂方法模式所创建对象的公共父类(获取计算结果集)
2)ConcreteProduct(具体产品):实现了抽象产品的接口,某类型的具体产品由专门的工厂创建(对应具体的加减乘除操作)
3)Factory(抽象工厂):它声明了工厂方法,用于返回一个产品。工厂方法模式的核心,所有创建对象的工厂必须实现该接口(创建计算器运算方式的工厂)
4)ConcreteFactory(具体工厂):抽象工厂类的子类,实现了抽象工厂中声明的工厂方法,返回一个具体产品类的实例(对应具体的加减乘除)
2. 实现:
日志记录器
代码如下:
/** * @author *** * @title: LoggerFactory * @projectName design-pattern * @description 日志记录器工厂接口,充当抽象工厂 * @date 2021/7/7 11:15 */ public interface LoggerFactory { /** * 抽象工厂方法 * @return */ Logger CreateLogger(); }
/** * @author *** * @title: FileLoggerFactory * @projectName design-pattern * @description 文件日志记录器工厂类,具体工厂 * @date 2021/7/7 11:18 */ public class FileLoggerFactory implements LoggerFactory { @Override public Logger CreateLogger() { //创建文件日志记录器 return new FileLogger(); } }
/** * @author *** * @title: DatabaseLoggerFactory * @projectName design-pattern * @description TODO * @date 2021/7/7 11:16 */ public class DatabaseLoggerFactory implements LoggerFactory { @Override public Logger CreateLogger() { //连接数据库 //创建记录器对象 return new DatabaseLogger(); } }
/** * @author *** * @title: Logger * @projectName design-pattern * @description 日志记录器接口 充当抽象产品接口 * @date 2021/7/7 11:10 */ public interface Logger { /** * 抽象产品方法 */ void WriteLog(); }
/** * @author *** * @title: FileLogger * @projectName design-pattern * @description 文件日志记录器 具体产品 * @date 2021/7/7 11:15 */ public class FileLogger implements Logger{ @Override public void WriteLog() { System.out.println("写入文件日志"); } }
/** * @author *** * @title: DatabaseLogger * @projectName design-pattern * @description 数据库日志记录器 具体产品 * @date 2021/7/7 11:11 */ public class DatabaseLogger implements Logger{ @Override public void WriteLog() { System.out.println("写入数据库日志!!!"); } }
/** * @author *** * @title: ClientTest * @projectName design-pattern * @description 客户端测试 * @date 2021/7/7 11:19 */ public class ClientTest { public static void main(String[] args) { //抽象工厂 LoggerFactory factory; //抽象产品 Logger logger; //new DatabaseLoggerFactory 可以更换为数据库日志记录器 factory = new FileLoggerFactory(); //抽象工厂方法 logger = factory.CreateLogger(); //抽象产品方法 logger.WriteLog(); } //如果要添加新的日志记录器,只要增加新的具体工厂类,并在客户端中修改具体工厂的类名便可以,从而避免了对原类的修改 }
三、工厂方法模式的重载
在某种情况下,可以用不同的方式来初始化一个产品类,在Logger记录器中,连接数据库的操作可以不用在每次CreateLog后再传入。
而是可以将相关参数封装在一个object中,通过object对象将参数传入工厂类中,或者在参数列表中给出连接方式来连接数据库。
为此,可以提供一组重载的工厂方法,以不同的方式创建产品。
修改代码如下:
/** * @author *** * @title: LoggerFactory * @projectName design-pattern * @description TODO * @date 2021/7/7 11:39 */ public interface LoggerFactory { Logger CreateLogger();//抽象工厂方法 Logger CreateLogger(String args); Logger CreateLogger(Object obj); }
/** * @author LuQiMing * @title: DatabaseLoggerFactory * @projectName design-pattern * @description TODO * @date 2021/7/7 11:41 */ public class DatabaseLoggerFactory implements LoggerFactory { @Override public Logger CreateLogger() { //连接数据库 //创建记录器对象 return new DatabaseLogger(); } @Override public Logger CreateLogger(String args) { //用args连接数据库 Logger logger = new DatabaseLogger(); ... return logger; } @Override public Logger CreateLogger(Object obj) { //将参数封装在obj中来连接数据库 Logger logger = new DatabaseLogger(); ... return logger; } }
四、工厂方法模式的隐藏
在客户端中,为简化使用,可以隐藏工厂方法。在工厂类调直接调用产品类的方法,在客户端中无需用工厂方法创建产品对象,直接使用工厂对象即可调用所创建的产品对象中的业务方法
修改代码如下:
/** * @author *** * @title: LoggerFactory * @projectName design-pattern * @description 改接口为抽象类 * @date 2021/7/7 12:00 */ public abstract class LoggerFactory { /** * 工厂类直接调用日志记录器的WriteLog() */ public void WriteLog() { Logger logger = this.CreateLogger(); logger.WriteLog(); } public abstract Logger CreateLogger(); }
/** * @author LuQiMing * @title: ClientTest * @projectName design-pattern * @description 客户端修改 * @date 2021/7/7 11:19 */ public class ClientTest { public static void main(String[] args) throws Exception { LoggerFactory factory = null; //直接使用工厂对象调用产品对象的业务方法 factory = (LoggerFactory)Class.forName("com.lqm.factoryMethod.demo2.hide.DatabaseLoggerFactory").newInstance(); factory.WriteLog(); } }
目录结构:
五、将简单工厂改造为工厂方法
在原有的基础上新增加了运算类的抽象工厂,其他算法工厂实现该工厂
/** * @author *** * @title: IFactory * @projectName design-pattern * @description 工厂接口 * @date 2021/7/6 17:00 */ public interface IFactory { Operation createOperation(); }
/** * @author *** * @title: AddFactory * @projectName design-pattern * @description 加法工厂 * @date 2021/7/6 17:01 */ public class AddFactory implements IFactory{ @Override public Operation createOperation() { return new OperationAdd(); } }
/** * @author *** * @title: AddFactory * @projectName design-pattern * @description 减法工厂 * @date 2021/7/6 17:01 */ public class SubFactory implements IFactory{ @Override public Operation createOperation() { return new OperationSub(); } }
/** * @author *** * @title: AddFactory * @projectName design-pattern * @description 乘法工厂 * @date 2021/7/6 17:01 */ public class MulFactory implements IFactory{ @Override public Operation createOperation() { return new OperationMul(); } }
/** * @author *** * @title: AddFactory * @projectName design-pattern * @description 除法工厂 * @date 2021/7/6 17:01 */ public class DivFactory implements IFactory{ @Override public Operation createOperation() { return new OperationDiv(); } }
/** * @author *** * @title: Operation * @projectName design-pattern * @description 抽象运算类 * @date 2021/7/6 16:54 */ public abstract class Operation { public double number1; public double number2; public double getNumber1() { return number1; } public void setNumber1(double number1) { this.number1 = number1; } public double getNumber2() { return number2; } public void setNumber2(double number2) { this.number2 = number2; } public abstract double getResult() throws Exception; }
/** * @author *** * @title: OperationAdd * @projectName design-pattern * @description 加法运算类 * @date 2021/7/6 16:55 */ public class OperationAdd extends Operation { @Override public double getResult() throws Exception { return number1+number2; } }
/** * @author *** * @title: OperationAdd * @projectName design-pattern * @description 减法运算类 * @date 2021/7/6 16:55 */ public class OperationSub extends Operation { @Override public double getResult() throws Exception { return number1-number2; } }
/** * @author *** * @title: OperationAdd * @projectName design-pattern * @description 乘法运算类 * @date 2021/7/6 16:55 */ public class OperationMul extends Operation { @Override public double getResult() throws Exception { return number1 * number2; } }
/** * @author *** * @title: OperationAdd * @projectName design-pattern * @description 除法运算类 * @date 2021/7/6 16:55 */ public class OperationDiv extends Operation { @Override public double getResult() throws Exception { if(0==number2){ throw new Exception("被除数不能为零"); } return number1/number2; } }
/** * @author *** * @title: ClassT * @projectName design-pattern * @description 客户端测试类 * @date 2021/7/6 17:04 */ public class ClassT { public static void main(String[] args) { IFactory factory = new AddFactory(); Operation oper = factory.createOperation(); oper.number1 = 1; oper.number2 = 2; try { System.out.println(oper.getResult() + ""); } catch (Exception e) { e.printStackTrace(); } IFactory factory1 = new SubFactory(); Operation oper1 = factory1.createOperation(); oper1.number1 = 1; oper1.number2 = 2; try { System.out.println(oper1.getResult() + ""); } catch (Exception e) { e.printStackTrace(); } IFactory factory2 = new MulFactory(); Operation oper2 = factory2.createOperation(); oper2.number1 = 1; oper2.number2 = 2; try { System.out.println(oper2.getResult() + ""); } catch (Exception e) { e.printStackTrace(); } IFactory factory3 = new DivFactory(); Operation oper3 = factory3.createOperation(); oper3.number1 = 1; oper3.number2 = 2; try { System.out.println(oper3.getResult() + ""); } catch (Exception e) { e.printStackTrace(); } } }
六、工厂方法模式的优缺点和适用环境
1. 工厂方法模式的优点
1)用户只需要关心产品对应的工厂,甚至无需关心创建细节或具体产品类的类名
2)基于工厂角色和产品的多态性设计是工厂模式的关键。它能自主决定如何创建哪种产品对象,而创建细节都封装在具体工厂内部
3)在系统要添加新的产品时,无需修改抽象工厂和抽象产品提供的接口,无需修改客户端,也无需修改其他的具体工厂和具体产品,只要添加一个具体工厂和具体产品即可,从而提高系统的可扩展性(符合开闭原则)
2. 工厂方法模式的缺点
1)在添加新产品时,要编写新的具体产品类,并要提供与之对应的具体工厂类。系统软件个数也成对增加,从而增加了系统的复杂度,带来更多开销
2)由于系统的可扩展性,在客户端中要引入抽象层进行定义,从而增加了系统的抽象性和理解难度
3. 工厂方法模式的适用环境
1)客户端不需要知道它需要的对象的类。只需知道对应的工厂即可
2)抽象工厂类通过子类来指定创建那哪个对象。即抽象工厂类只需要提供一个创建产品的接口,而由其子类确定具体要创建的对象,利用多态性和里氏代换原则,子类对象将覆盖父类对象,从而使系统更容易扩展