zoukankan      html  css  js  c++  java
  • 设计模式 | 抽象工厂模式(abstract factory)

    定义:

    提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。

    结构:(书中图,侵删)

    这个图相对来说有一点点复杂,其实就是在工厂方法模式的基础上做了一些扩展,工厂方法模式只用于生成一种产品(把上图ProductB相关的都去掉就是了),而抽象工厂模式可用于生产多种产品。
    加上例子吧,假设生产海贼的手办(路飞和娜美)。
    一个抽象工厂抽象接口(包含生成所有类型产品的方法,即生成路飞和娜美的方法)
    若干个具体工厂(各种生成产品的不同实现的工厂,理论上,同一个具体工厂底下生成的都是同一个系列的产品。类似于A工厂生成两年前的,B工厂生成两年后的,生成出来的都是同一个人物)
    若干个抽象的产品接口(这里就是路飞和娜美两个)
    每个抽象的产品接口下有若干个具体的产品类(路飞下有(两年前路飞、两年后路飞);娜美下有(两年前娜美,两年后娜美))
    根据上例照着原格式再来画张图,便于理解:(把client去掉了,懒得画)

    实例:

    鉴于书中的例子相当的常见,所以决定延用书中的例子。
    就是更换数据库的例子。
    假设系统中有员工、部门两个类。
    然后系统需要使用mysql和oracle两个数据库。
    为了代码简洁,不分什么dao层之类的,直接把调用数据库的方法写在实体里。
    员工抽象类:
    package designpattern.abstractfactory;
    
    public abstract class Employee {
        private String name;
    
        abstract void insert(Employee employee);
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Employee [name=" + name + "]";
        }
    
    }
    oracle员工类:
    package designpattern.abstractfactory;
    
    public class OracleEmployee extends Employee {
    
        @Override
        void insert(Employee employee) {
            System.out.println("往oracle数据库插入一条Employee员工数据:" + employee);
        }
    
    }
    mysql员工类:
    package designpattern.abstractfactory;
    
    public class MysqlEmployee extends Employee {
        @Override
        public void insert(Employee employee) {
            System.out.println("往mysql数据库插入一条Employee员工数据:" + employee);
        }
    
    }
    部门抽象类:
    package designpattern.abstractfactory;
    
    public abstract class Department {
        String name;
    
        abstract void insert(Department department);
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Department [name=" + name + "]";
        }
    
    }
    oracle部门类:
    package designpattern.abstractfactory;
    
    public class OracleDepartment extends Department {
    
        @Override
        void insert(Department department) {
            System.out.println("往oracle数据库插入一条Department部门数据:" + department);
        }
    
    }
    mysql部门类:
    package designpattern.abstractfactory;
    
    public class MysqlDepartment extends Department {
    
        @Override
        void insert(Department department) {
            System.out.println("往mysql数据库插入一条Department部门数据:"+department);
        }
    
    }
    抽象工厂类:
    package designpattern.abstractfactory;
    
    public interface Factory {
        Employee createEmployee();
    
        Department createDepartment();
    }
    mysql工厂类:
    package designpattern.abstractfactory;
    
    public class MysqlFactory implements Factory {
    
        @Override
        public Employee createEmployee() {
            return new MysqlEmployee();
        }
    
        @Override
        public Department createDepartment() {
            return new MysqlDepartment();
        }
    
    }
    oracle工厂类:
    package designpattern.abstractfactory;
    
    public class OracleFactory implements Factory {
    
        @Override
        public Employee createEmployee() {
            return new OracleEmployee();
        }
    
        @Override
        public Department createDepartment() {
            return new OracleDepartment();
        }
    
    }
    客户端:
    package designpattern.abstractfactory;
    
    public class Client {
        public static void main(String[] args) {
            Factory factory = new MysqlFactory();
            // Factory factory=new OracleFactory();
    
            Employee employee = factory.createEmployee();
            employee.setName("张三");
            employee.insert(employee);
    
            Department department = factory.createDepartment();
            department.setName("技术部");
            department.insert(department);
    
        }
    }
    结果输出:
    往mysql数据库插入一条Employee员工数据:Employee [name=张三]
    往mysql数据库插入一条Department部门数据:Department [name=技术部]
    这个设计模式很好的解除了客户端与实例创建过程的耦合,通过抽象出接口的方式,使客户端只需要和接口打交道。
    同时也使得切换数据库变得容易,只需要修改初始化的语句即可。
    这同样也是这个模式的不足之处,意味着所有需要用到数据库连接的地方都要写上这句初始化语句,使得修改的工作量变得很大。
     
    接下来就一步一步优化它:
    首先,使用哪个数据库的判断是在客户端,我们需要把这个判断转移,使用简单工厂模式,将判断转移至简单工厂:
    简单工厂:
    package designpattern.abstractfactory;
    
    public class SimpleFactory {
        static String db = "mysql";
        //static String db="oracle";
    
        static Employee createEmployee() {
            switch (db) {
            case "mysql":
                return new MysqlEmployee();
            case "oracle":
                return new OracleEmployee();
            default:
                return null;
            }
        }
    
        static Department createDepartment() {
            switch (db) {
            case "mysql":
                return new MysqlDepartment();
            case "oracle":
                return new OracleDepartment();
            default:
                return null;
            }
        }
    
    }
    客户端:
    package designpattern.abstractfactory;
    
    public class Client2 {
        public static void main(String[] args) {
            Employee employee = SimpleFactory.createEmployee();
            employee.setName("张三");
            employee.insert(employee);
    
            Department department = SimpleFactory.createDepartment();
            department.setName("技术部");
            department.insert(department);
    
        }
    }
    然后,如果再增加一个数据库,需要在所有的方法里增加switch的case,这也是很麻烦的事情,这里需要用到反射来解决这个问题:
    反射版简单工厂:
    package designpattern.abstractfactory;
    
    public class ReflectSimpleFactory {
        static String db = "Mysql";
        // static String db="Oracle";
    
        static String path = "designpattern.abstractfactory";// 包路径
    
        static Employee createEmployee() {
            try {
                Class<Employee> employee = (Class<Employee>) Class.forName(path + "." + db + "Employee");
                return employee.newInstance();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        static Department createDepartment() {
            try {
                Class<Department> department = (Class<Department>) Class.forName(path + "." + db + "Department");
                return department.newInstance();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return null;
        }
    
    }
    客户端:
    package designpattern.abstractfactory;
    
    public class Client3 {
        public static void main(String[] args) {
            Employee employee = ReflectSimpleFactory.createEmployee();
            employee.setName("张三");
            employee.insert(employee);
    
            Department department = ReflectSimpleFactory.createDepartment();
            department.setName("技术部");
            department.insert(department);
    
        }
    }
    通过反射,将程序由编译时改为运行时,彻底取代了switch语句。
     
    现在,还剩最后一个问题,决定使用什么数据库的字符串还是写在代码中,修改之后还需要重新编译,这里只需要把字符串改到配置文件中即可:
    简单工厂:
    package designpattern.abstractfactory;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Properties;
    
    public class ReflectSimpleFactory2 {
    
        static String path = "designpattern.abstractfactory";// 包路径
    
        static Employee createEmployee() {
            try {
                Class<Employee> employee = (Class<Employee>) Class.forName(path + "." + getDBName() + "Employee");
                return employee.newInstance();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        static Department createDepartment() {
            try {
                Class<Department> department = (Class<Department>) Class.forName(path + "." + getDBName() + "Department");
                return department.newInstance();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        private static String getDBName() {
            String dbName = null;
            try {
                InputStream in = ReflectSimpleFactory2.class.getResourceAsStream("db.properties");
                Properties pro = new Properties();
                pro.load(in);
                in.close();
                dbName = pro.getProperty("db");
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            return dbName;
    
        }
    }
    配置文件:
    db=Mysql
    #db=Oracle
    客户端:
    package designpattern.abstractfactory;
    
    public class Client4 {
        public static void main(String[] args) {
            Employee employee = ReflectSimpleFactory2.createEmployee();
            employee.setName("张三");
            employee.insert(employee);
    
            Department department = ReflectSimpleFactory2.createDepartment();
            department.setName("技术部");
            department.insert(department);
    
        }
    }
    大功告成!
     

    总结:

    抽象工厂设计模式和其他的工厂类设计模式一样,就是将客户端与具体的对象创建过程分离。
    只不过这里所涉及到的不再是一种类,而是多种类,结构相对复杂。
    同时也像上文说的一样,存在一些不足,可以具体情况具体分析,应该如何使用。
     
  • 相关阅读:
    Milk Patterns POJ
    Musical Theme POJ
    iOS
    iOS
    iOS
    iOS
    iOS
    iOS
    runloop
    OC -网络请求
  • 原文地址:https://www.cnblogs.com/imoqian/p/10777422.html
Copyright © 2011-2022 走看看