• 设计模式之抽象工厂


    抽象工厂

    定义 :抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类

    创建型 :创建型

    适用场景

    • 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
    • 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码
    • 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现

    优点

    • 具体产品在应用层的代码隔离,无需关心创建的细节
    • 将一个系列的产品统一到一起创建

    缺点

    • 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;
    • 增加了系统的抽象性和理解难度

    UML类图

    Creator:产品族工厂,定义了一系列的产品生产行为

    ConcreteCreator:具体的产品族工厂

    Product:抽象产品接口

    ProductA1/A2/B1/B2:都是具体的产品,实现了相应的产品接口

    为了更好理解抽象工厂模式,需要先搞清楚产品等级和产品族的概念,举个粟子:手机有小米手机、华为手机,它们都是手机,这些具体的手机和抽象手机就构成了一个产品等级结构。同样的,路由器有小米路由器,华为路由器,这些具体的路由器和抽象路由器就构成了另外一个产品等级结构,实质上产品等级结构即产品的继承结构。小米手机位于手机产品等级结构中,小米路由器位于路由器的产品等级结构中,而小米手机和小米路由器都是小米公司生产的,就构成了一个产品族,同理,华为手机和华为路由器也构成了一个产品族 。划重点就是产品族中的产品都是由同一个工厂生产的,位于不同的产品等级结构

    对比工厂方法,其针对的是产品等级结构,而抽象工厂是针对产品族。在二者的使用选择上,需要结合实际业务,对于产品等级数量相对固定的产品族,可以优先考虑抽象工厂模式,但是如果频繁变动,则不大适用,因为在现有的产品族中新增产品等级时,就需要修改产品族工厂,也就违背了开闭原则

    示例

    这里就以上面的栗子进行代码描述

    • 定义抽象产品接口
    /**
     * 手机产品接口
     */
    public interface IPhoneProduct {
    
        /**
         * 开机
         */
        void start();
    
        /**
         * 关机
         */
        void shutdown();
    
        /**
         * 拨打电话
         */
        void callUp();
    
        /**
         * 发送短信
         */
        void sendSMS();
    
    }
    
    
    /**
     * 路由器产品接口
     */
    public interface IRouterProduct {
    
        /**
         * 开机
         */
        void start();
    
        /**
         * 关机
         */
        void shutdown();
    
        /**
         * 开启wifi
         */
        void openWifi();
    
        /**
         * 设置参数
         */
        void setting();
    
    }
    	
    
    • 定义小米品牌手机和路由器产品实现类、华为品牌手机和路由器产品实现类
    /**
     * 小米手机产品
     */
    public class XiaomiPhone implements IPhoneProduct {
        @Override
        public void start() {
            System.out.println("开启小米手机");
        }
    
        @Override
        public void shutdown() {
            System.out.println("关闭小米手机");
        }
    
        @Override
        public void callUp() {
            System.out.println("用小米手机打电话");
        }
    
        @Override
        public void sendSMS() {
            System.out.println("用小米手机发短信");
        }
    }
    
    
    /**
     * 小米路由器产品
     */
    public class XiaomiRouter implements IRouterProduct {
        @Override
        public void start() {
            System.out.println("启动小米路由器");
        }
    
        @Override
        public void shutdown() {
            System.out.println("关闭小米路由器");
        }
    
        @Override
        public void openWifi() {
            System.out.println("打开小米路由器的wifi功能");
        }
    
        @Override
        public void setting() {
            System.out.println("设置小米路由器参数");
        }
    }
    
    /**
     * 华为手机产品
     */
    public class HuaweiPhone implements IPhoneProduct {
        @Override
        public void start() {
            System.out.println("开启华为手机");
        }
    
        @Override
        public void shutdown() {
            System.out.println("关闭华为手机");
        }
    
        @Override
        public void callUp() {
            System.out.println("用华为手机打电话");
        }
    
        @Override
        public void sendSMS() {
            System.out.println("用华为手机发短信");
        }
    }
    
    
    /**
     * 华为路由器产品
     */
    public class HuaweiRouter implements IRouterProduct {
        @Override
        public void start() {
            System.out.println("启动华为路由器");
        }
    
        @Override
        public void shutdown() {
            System.out.println("关闭华为路由器");
        }
    
        @Override
        public void openWifi() {
            System.out.println("打开华为路由器的wifi功能");
        }
    
        @Override
        public void setting() {
            System.out.println("设置华为路由器参数");
        }
    }
    
    
    • 定义一个抽象工厂接口,感觉称为抽象产品族工厂可能更容易理解,毕竟它不止生产一种产品
    /**
     * 抽象产品工厂(定义了同一个产品族的产品生产行为)
     */
    public interface IProductFactory {
    
        /**
         * 生产手机
         * @return
         */
        IPhoneProduct produceTelPhone();
    
        /**
         * 生产路由器
         * @return
         */
        IRouterProduct produceRouter();
    
    }
    
    
    • 小米工厂和华为工厂实现类
    /**
     * 小米产品工厂
     */
    public class XiaomiProductFactory implements IProductFactory {
        @Override
        public IPhoneProduct produceTelPhone() {
            System.out.println(">>>>>>生产小米手机");
            return new XiaomiPhone();
        }
    
        @Override
        public IRouterProduct produceRouter() {
            System.out.println(">>>>>>生产小米路由器");
            return new XiaomiRouter();
        }
    }
    
    /**
     * 华为产品工厂
     */
    public class HuaweiProductFactory implements IProductFactory{
        @Override
        public IPhoneProduct produceTelPhone() {
            System.out.println(">>>>>>生产华为手机");
            return new HuaweiPhone();
        }
    
        @Override
        public IRouterProduct produceRouter() {
            System.out.println(">>>>>>生产华为路由器");
            return new HuaweiRouter();
        }
    }
    
    • 客户端
    /**
     * 客户端
     */
    public class Client {
    
        public static void main(String[] args) {
            System.out.println("===================小米系列产品=================");
            //小米产品工厂实例
            IProductFactory xiaomiProductFactory = new XiaomiProductFactory();
            //生产小米路由器
            IRouterProduct xiaomiRouter = xiaomiProductFactory.produceRouter();
            xiaomiRouter.start();
            xiaomiRouter.setting();
            xiaomiRouter.openWifi();
            xiaomiRouter.shutdown();
            //生产小米手机
            IPhoneProduct xiaomiPhone = xiaomiProductFactory.produceTelPhone();
            xiaomiPhone.start();
            xiaomiPhone.callUp();
            xiaomiPhone.sendSMS();
            xiaomiPhone.shutdown();
            
            System.out.println("===================华为系列产品=================");
            //华为产品工厂实例
            IProductFactory huaweiProductFactory = new HuaweiProductFactory();
            //生产华为路由器
            IRouterProduct huaweiRouter = huaweiProductFactory.produceRouter();
            huaweiRouter.start();
            huaweiRouter.setting();
            huaweiRouter.openWifi();
            huaweiRouter.shutdown();
            //生产华为手机
            IPhoneProduct huaweiPhone = huaweiProductFactory.produceTelPhone();
            huaweiPhone.start();
            huaweiPhone.callUp();
            huaweiPhone.sendSMS();
            huaweiPhone.shutdown();
        }
    }
    

    控制台输出结果:

    ===小米系列产品=

    >>>>>>生产小米路由器
    启动小米路由器
    设置小米路由器参数
    打开小米路由器的wifi功能
    关闭小米路由器
    >>>>>>生产小米手机
    开启小米手机
    用小米手机打电话
    用小米手机发短信
    关闭小米手机
    ===华为系列产品=
    >>>>>>生产华为路由器
    启动华为路由器
    设置华为路由器参数
    打开华为路由器的wifi功能
    关闭华为路由器
    >>>>>>生产华为手机
    开启华为手机
    用华为手机打电话
    用华为手机发短信
    关闭华为手机

    从上面的整个示例可以发现,如果现在想增加一个产品等级,如新加一种笔记本产品,就需要修改抽象产品工厂接口IProductFactory,如下

     * 抽象产品工厂(定义了同一个产品族的产品生产行为)
     */
    public interface IProductFactory {
    
        /**
         * 生产手机
         * @return
         */
        IPhoneProduct produceTelPhone();
    
        /**
         * 生产路由器
         * @return
         */
        IRouterProduct produceRouter();
        
        /**
         * 生产笔记本(新增)
         * @return
         */
        IComputerProduct produceComputer();
    
    }
    
    
    

    这种直接修改抽象接口的做法,会导致其所有实现子类都需要进行修改,违反了开闭原则。当然,如果这种修改是长期稳定的,那么也可以接受。看起来上面的例子可能大合适,整个工厂的产品等级数量不是很稳定(小米和华为的后续延伸产品还有很多),但足以描述抽象工厂的应用。

    如果该场景以工厂方法实现,那么我们需要定义一个手机工厂、一个路由器工厂、然后小米手机工厂实现类,华为手机工厂实现类,小米路由器工厂实现类,华为路由器工厂实现类,其余的产品接口和产品实现类均与上相同,这样就多出了一些产品工厂,略显啰嗦

    实际使用中,都需要根据业务去权衡使用工厂方法还是抽象工厂,前者关注点在产品等级上,后者关注点在产品族上,对于稳定的产品族,也即是产品等级数量稳定,使用抽象工厂会更加有效率,毕竟不再是一个工厂生产一种产品,而是一个工厂生产多种同族产品,对于不稳定的产品族,单独使用工厂方法会显得更加灵活

    使用典范

    • java.sql.Connection
    public interface Connection  extends Wrapper, AutoCloseable {
    	//...    	
        	//返回普通的sql执行器
            Statement createStatement() throws SQLException;
    		//返回具有参数化预编译功能的sql执行器
        	PreparedStatement prepareStatement(String sql) throws SQLException;
        	//返回可以执行存储过程的sql执行器
            CallableStatement prepareCall(String sql) throws SQLException;   
        //...         
    }
    

    从上面的注释就可以看出,这就是典型的抽象工厂接口,描述了不同的产品等级Statement、PreparedStatement、CallableStatement,它们都位于抽象接口Statement产品等级结构中。我们可以继续寻找该抽象工厂接口的实现类

    这里就以Mysql为例,可以找到Mysql对这个工厂接口的实现类ConnectionImpl,ConnectionImpl并不是直接实现了java.sql.Connection,而是通过实现自己扩展的MySQLConnection接口,该接口也是间接继承了java.sql.Connection

    public class ConnectionImpl extends ConnectionPropertiesImpl implements MySQLConnection {
    	//...
    
     public java.sql.Statement createStatement() throws SQLException {
            return createStatement(DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY);
        }
       
     public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException {
            return prepareStatement(sql, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY);
        }
        
     public java.sql.CallableStatement prepareCall(String sql) throws SQLException {
    
            return prepareCall(sql, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY);
        }
    	
    	//...
    }
    

    createStatement为例,跟踪其调用代码可以看到StatementImpl这个类就是实现了java.sql.Statement的具体产品类

        public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
            checkClosed();
    
            StatementImpl stmt = new StatementImpl(getMultiHostSafeProxy(), this.database);
            stmt.setResultSetType(resultSetType);
            stmt.setResultSetConcurrency(resultSetConcurrency);
    
            return stmt;
        }
    
    • mybatis中的SqlSessionFactory
    /**
     * Creates an {@link SqlSession} out of a connection or a DataSource
     * 
     * @author Clinton Begin
     */
    public interface SqlSessionFactory {
    
      SqlSession openSession();
    
      SqlSession openSession(boolean autoCommit);
      SqlSession openSession(Connection connection);
      SqlSession openSession(TransactionIsolationLevel level);
    
      SqlSession openSession(ExecutorType execType);
      SqlSession openSession(ExecutorType execType, boolean autoCommit);
      SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
      SqlSession openSession(ExecutorType execType, Connection connection);
    
      Configuration getConfiguration();
    
    }
    
    

    SqlSessionFactory也是抽象工厂接口,Configuration和SqlSession都是在不同的产品等级上。通过IDEA工具可以通过UML图清晰得看到SqlSessionFactory的工厂实现类

    以上两个例子都和数据库操作相关,同样使用了抽象工厂模式。在jdbc中,客户端通过Connection工厂获取到Statement产品对象,然后通过该对象进行增删改查操作,对于mybatis这种数据库操纵框架而言(底层也是封装了jdbc api)有异曲同工,通过SeqSessionFactory工厂获取到SqlSession产品对象,然后进行增删改查操作

  • 相关阅读:
    MySQL实现了四种通信协议
    深入了解Windows句柄到底是什么
    Linux虚拟地址空间布局以及进程栈和线程栈总结
    malloc 函数详解
    数组指针和指针数组的区别
    Linux中sudo配置
    ctrl+c,ctrl+d,ctrl+z在linux程序中意义和区别
    linux select函数详解
    linux grep命令详解
    Linux find 用法示例
  • 原文地址:https://www.cnblogs.com/DiDi516/p/12733080.html
Copyright © 2020-2023  润新知