美文网首页
设计模式(3) : 抽象工厂模式

设计模式(3) : 抽象工厂模式

作者: a_salt_fish | 来源:发表于2019-01-17 00:00 被阅读0次

    定义

    • 提供一个创建一系列相关或相互依赖对象的接口.

    这里引出两个概念, 产品族产品类型, 举个例子汽车有 轿车, SUV, 跑车等, 同时汽车又分为不同的品牌, 如大众, 奔驰等. 大众轿车和大众SUV都是大众品牌,是同一个家族的产品, 轿车和SUV是车的不同产品类型. 定义中所说的一系列指的就是同一个家族中一系列不同的产品类型.

    类型

    • 创建型

    使用场景

    • 产品类型相对固定,产品族变化较多, 同时创建同一产品族的一系列产品时需要大量的重复代码.

    coding

    业务场景: 拿上述汽车工厂的例子来举例,客户端需要获取不同类型不同品牌的汽车实例

    如果用工厂方法模式应该怎么实现呢?
    定义抽象产品类轿车接口 SaloonCar, Suv 接口

    public interface SaloonCar {
        String getCar();
    }
    public interface SuvCar {
        String getCar();
    }
    

    创建产品类
    大众轿车, 奔驰轿车, 大众SUV, 奔驰SUV

    public class BenzSaloonCar implements SaloonCar {
        @Override
        public String getCar() {
            return "奔驰轿车";
        }
    }
    public class BenzSuvCar implements SuvCar {
        @Override
        public String getCar() {
            return "奔驰SUV";
        }
    }
    public class VwSaloonCar implements SaloonCar {
        @Override
        public String getCar() {
            return "大众轿车";
        }
    }
    public class VwSuvCar implements SuvCar {
        @Override
        public String getCar() {
            return "大众SUV";
        }
    }
    
    

    接下来按照工厂方法的模式, 创建一个对应的工厂方法
    两个产品的抽象工厂方法

    public interface SuvFactory {
        SuvCar getSuvCar();
    }
    public interface SaloonFactory {
        SaloonCar getSaloonCar();
    }
    

    对应的产品工厂方法

    public class BenzSaloonFactory implements SaloonFactory {
        @Override
        public  SaloonCar getSaloonCar() {
            return new BenzSaloonCar();
        }
    }
    public class BenzSuvFactory implements SuvFactory {
        @Override
        public SuvCar getSuvCar() {
            return new BenzSuvCar();
        }
    }
    public class VwSaloonFactory implements SaloonFactory {
        @Override
        public SaloonCar getSaloonCar() {
            return new VwSaloonCar();
        }
    }
    public class VwSuvFactory implements SuvFactory {
        @Override
        public SuvCar getSuvCar() {
            return new VwSuvCar();
        }
    }
    

    balh balh 写了一堆工厂, 我都要写吐了, 来测试一下

    public class Test {
        public static void main(String[] args) {
            SaloonFactory saloonFactory = new BenzSaloonFactory();
            SuvFactory suvFactory = new VwSuvFactory();
            SaloonCar saloonCar = saloonFactory.getSaloonCar();
            SuvCar suvCar = suvFactory.getSuvCar();
            System.out.println(saloonCar.getCar());
            System.out.println(suvCar.getCar());
        }
    }
    

    运行结果当然没有问题,但是我们也注意到了开发过程中写了一大堆的工厂类(如果新增一个其他品牌还会写更多,类的数量会急剧上升 [个数 = 产品类型数量 * 产品族数量])

    另外一个明显的问题就是如果我们的系统中要求只使用同一族的产品, 但是在我们的代码中同族的产品却没有任何关系,我们只能依赖程序员的编码素质来保证这一点.


    下面用本章的主角 : 抽象工厂模式来重构一下代码

    很自然的,我们会想到, 既然大众的轿车和SUV是同一个牌子的, 那我们把它们放到同一个工厂里生产不就好了吗, 这就是抽象工厂模式的核心, 工厂类创建一系列先关的对象.

    开始重构 : 抽象类 SaloonCar ,SuvCar以及它们的实现类保持变

    新增品牌工厂, 生产一个产品族的所有类型的产品

    public interface BrandFactory {
        SaloonCar getSaloonCar();
        SuvCar getSuvCar();
    }
    

    不同的厂家分别实现品牌工厂接口

    public class BenzFactory implements BrandFactory{
        @Override
        public SaloonCar getSaloonCar() {
            return new BenzSaloonCar();
        }
        @Override
        public SuvCar getSuvCar() {
            return new BenzSuvCar();
        }
    }
    public class VwFactory implements BrandFactory{
        @Override
        public SaloonCar getSaloonCar() {
            return new VwSaloonCar();
        }
        @Override
        public SuvCar getSuvCar() {
            return new VwSuvCar();
        }
    }
    

    测试一下

    public class Test {
        public static void main(String[] args) {
            BrandFactory factory = new BenzFactory();
            SaloonCar saloonCar = factory.getSaloonCar();
            SuvCar suvCar = factory.getSuvCar();
            System.out.println(saloonCar.getCar());
            System.out.println(suvCar.getCar());
        }
    }
    

    可以明显看到, 工厂方法减少了很多, 客户端只需获取一个品牌的工厂类即可, 保证了工厂中获取到的方法都是同族的.


    源码解析

    JDK 中 java.sql.Connection 是一个接口 定义了java获取数据库连接的规范, 不同的数据库厂商有不同的实现.
    这个接口中定义了一系列的方法,用于获取同的数据库连接或其它数据

    // 用于执行不带参数的简单SQL语句
    Statement createStatement() throws SQLException;
    // 预编译的SQL语句的对象,用于执行带参数的预编译的SQL语句。
    PreparedStatement prepareStatement(String sql)
            throws SQLException;
    // 调用数据库中存储过程的接口
    CallableStatement prepareCall(String sql) throws SQLException;
    // 元数据
    DatabaseMetaData getMetaData() throws SQLException;
    

    不同的数据库厂商根据自家数据库开发驱动,实现Connection 接口

    在这个例子中, 不同的数据库就是不同的产品族, Statement 与CallableStatement 这些属于产品类型.

    在实际项目中, 一般只会用到一种数据库, 所以通过Connection获取到的 产品类型必定属于同一族, 不会出现Mysql的Statement 和 Oracle的CallableStatement 混搭的情况.

    优点

    • 对客户端屏蔽了实例创建的细节.
    • 相对于工厂方法模式来说,将同族产品放到同一个工厂中,减少了类的数量.

    缺点

    • 产品类型不易扩展(产品类型需要经常发生改变时不宜使用).

    github源码

    相关文章

      网友评论

          本文标题:设计模式(3) : 抽象工厂模式

          本文链接:https://www.haomeiwen.com/subject/uvuvdqtx.html