前言23设计模式简写单例模式工厂方法模式前面我们讲完了工厂方法模式,发现工厂方法模式有一个严重的问题:一个特定的工厂而已可以创建一种产品,但在实际过程中,一个工厂往往需要生产多种产品。为了解决以上问题,可以使用抽象工厂模式。本篇文章,我们就和大家一起来了解一下抽象工厂模式。模式定义抽象工厂模式,即AbstractFactoryPattern,提供了一个接口来创建一系列相关或相互依赖的对象,而无需指定它们的具体类;特定的工厂负责实现特定的产品实例。抽象工厂模式是工厂方法模式的升级版。工厂方法模式只生产一级产品,而抽象工厂模式可以生产多级产品。待解决的问题每个工厂只能创建一种产品,即工厂方法模式的缺点。抽象工厂模式与工厂方法模式相同。工厂中的方法数量不同,抽象产品的数量也不同。下面我们来分析一下它的基本结构和实现方法。使用步骤创建一个抽象工厂类,并定义具体工厂的公共接口;创建抽象产品族类,定义抽象产品的公共接口;创建抽象产品类(继承抽象产品族类),定义具体产品的公共接口;创建一个具体的产品类(继承抽象产品类)&定义生产的具体产品;创建具体工厂类(继承抽象工厂类),定义创建相应具体产品实例的方法;客户端实例化具体的工厂类,调用它创建不同目标产品的方法来创建不同具体产品类别的实例。工厂所在地的客户也需要模具产品,B工厂所在地的客户也需要容器产品;冲突:没有资源(资金+租金)在当地增开一家注塑分厂解决方案:在原有的两个塑料厂之外,增加生产需求的功能,即A厂可以生产容器+模具产品;B厂可以生产模具+容器产品。即使用抽象工厂模式的步骤第一步:创建一个抽象工厂类,定义具体工厂的公共接口。abstractclassFactory{publicabstractAbstractProductManufactureContainer();publicabstractAbstractProductManufactureMould();}第二步:创建抽象产品族,定义具体产品的公共接口。abstractclassAbstractProduct{publicabstractvoidmethod1();}第三步:创建抽象产品类,定义具体产品的公共接口。//容器产品抽象类abstractclassContainerProducttextendsAbstractProduct{@Overridepublicabstractvoidmethod1();}//模具产品抽象类abstractclassMouldProducttextendsAbstractProduct{@Overridepublicabstractvoidmethod1();}第四步:创建具体产品类(继承抽象产品类),定义具体产品类被生产。//容器产品A类classContainerProductAextendsContainerProduct{@Overridepublicvoidmethod1(){System.out.println("容器产品A已生产");}}//容器产品B类classContainerProductBextendsContainerProduct{@Overridepublicvoidmethod1(){System.out.println("容器产品B已生产");}}//模具产品类AclassMouldProductAextendsMouldProduct{@Overridepublicvoidmethod1(){System.out.println("模具产品A已生产");}}//模具产品类BclassMouldProductBextendsMouldProduct{@Overridepublicvoidmethod1(){System.out.println("模具产品B已生产");}}第五步:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法.//工厂A-生产模具+容器产品classFactoryAextendsFactory{@OverridepublicAbstractProductManufactureContainer(){returnnewContainerProductA();}@OverridepublicAbstractProductManufactureMould(){returnnewMouldProductA();}}//工厂B-生产模具+容器产品classFactoryProductBextendverContainer{@OstricreturnnewContainerProductB();}@OverridepublicAbstractProductManufactureMould(){returnnewMouldProductB();}}第六步:客户端通过实例化一个具体的工厂类,调用其方法创建不同的目标产品,从而创建不同具体产品类的实例。publicclassAbstractFactoryPattern{publicstaticvoidmain(String[]args){FactoryAmFactoryA=newFactoryA();FactoryBmFactoryB=newFactoryB();//工厂A本地客户需要容器产品AmFactoryA.ManufactureContainer().method1();//工厂A本地客户需要moldproductsAmFactoryA.ManufactureMould().method1();//B工厂本地客户需要容器产品BmFactoryB.ManufactureContainer().method1();//B工厂本地客户需要模具产品BmFactoryB.ManufactureMould().method1();}}输出结果生产容器产品A生产模具产品A生产容器产品B生产模具产品B类而不必专门引入多个新类来管理。当需要一个产品族时,抽象工厂可以保证客户端始终只使用同一产品的产品组。抽象工厂增强了程序的可扩展性。添加新产品系列时,原有代码无需修改,满足开闭原则。缺点当需要将新产品添加到产品系列时,需要修改所有工厂类。增加了系统的抽象性和理解难度。这是因为在抽象工厂接口中已经确定了可以创建的产品集合。如果需要增加新的产品,此时必须修改抽象工厂的接口,涉及到抽象工厂类和所有子类的变化。也违反了“开发-关闭”的原则。对于一个新的产品族,符合开闭原则;对于一个新的产品品类,不符合开闭原则。这种特性称为开闭原理的倾斜。当应用场景程序需要处理不同系列的相关产品,但又不想依赖于这些产品的具体类时,可以使用抽象工厂。源码中的应用#JDKjava.sql.Connectionjava.sql.Driver#mybatisSqlSessionFactoryjava.sql.ConnectionpublicinterfaceConnectionextendsWrapper,AutoCloseable{//...//返回普通sql执行器StatementcreateStatement()throwsSQLException;//返回带有参数化预编译函数的sqlexecutorPreparedStatementprepareStatement(Stringsql)throwsSQLException;//返回可以执行存储过程的sql执行器CallableStatementprepareCall(Stringsql)throwsSQLException;//...}从上面的注释可以看出,这是一个典型的抽象工厂接口,描述不同产品级Statement、PreparedStatement和CallableStatement,它们都位于抽象接口Statement产品级结构中。我们可以继续寻找抽象工厂接口的实现类。以Mysql为例,可以找到Mysql对这个工厂接口的实现类ConnectionImpl。ConnectionImpl没有直接实现java.sql.Connection,而是实现了自己扩展的MySQLConnection接口,该接口也间接继承了java.sql.ConnectionpublicclassConnectionImplextendsConnectionPropertiesImplimplementsMySQLConnection{//...publicjava.sql.StatementcreateStatement()throwsSQLException{returncreateStatement(DEFAULT_RESULT_SET_TYPE,DEFAULT_RESULT_SET_CONCURRENCY);}publicjava.sql.PreparedStatementprepareStatement(Stringsql)throwsSQLException{returnprepareStatement(sql,DEFAULT_RESULT_SET_TYPE,DEFAULT_RESULT_SET_CONCURRENCY);}publicjava.sql.CallableStatementprepareCall(Stringsql)throwsSQLException{returnprepareCall(sql,DEFAULT_RESULT_SET_TYPE,DEFAULT_CRESULT_SET}创建...举个例子,跟踪它的调用代码,可以看到StatementImpl类就是具体实现了java.sql.Statement的产品类publicjava.sql。StatementcreateStatement(intresultSetType,intresultSetConcurrency)throwsSQLException{checkClosed();StatementImplstmt=newStatementImpl(getMultiHostSafeProxy(),this.database);stmt.setResultSetType(resultSetType);stmt.setResultSetConcurrency(resultSetConcurrency);returnstmt;}SqlSessionFactory/***Createsan{@linkSqlSession}outofaconnectionoraDataSource**@authorClintonBegin*/publicinterfaceSqlSessionFactory{SqlSessionopenSession();SqlSessionopenSession(booleanautoCommit);SqlSessionopenSession(Connectionconnection);SqlSessionopenSession(TransactionIsolationLevellevel);SqlSessionopenSession(ExecutorTypeexecType);SqlSessionopenSession(ExecutorTypeexecType,booleanautoCommit);SqlSessionopenSession(ExecutorTypeexecType,TransactionIsolationLevellevel);SqlSessionopenSession(ExecutorTypeexecType,Connectionconnection);ConfigurationgetConfiguration();}SqlSessionFactory也是抽象工厂接口,Configuration和SqlSession都是在不同的产品等级通过IDEA工具,通过UML图可以清楚的看到SqlSessionFactory的工厂实现类。上面两个例子都是和数据库操作相关的,同样用到了抽象工厂模式。在jdbc中,客户端通过Connection工厂获取Statement产品对象,然后通过该对象进行增删改查操作。对于mybatis这样的数据库操作框架(底层也封装了jdbcapi),也是一样的目的,通过SeqSessionFactory工厂获取。SqlSession产品对象,然后增删改查。PS:以上代码提交在Github上:https://github.com/Niuh-Study/niuh-designpatterns.git
