打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
设计模式 | 抽象工厂模式(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);    }}
大功告成!
 

总结:

抽象工厂设计模式和其他的工厂类设计模式一样,就是将客户端与具体的对象创建过程分离。
只不过这里所涉及到的不再是一种类,而是多种类,结构相对复杂。
同时也像上文说的一样,存在一些不足,可以具体情况具体分析,应该如何使用。
 
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
java设计模式精讲 Debug 方式+内存分析 第13章 享元模式
构造函数与方法的覆盖
EF Core中通过Fluent API完成对表的配置
【C#】AutoMapper 使用手册
Hibernate框架基础——cascade属性
2.3.4 可配置的简单工厂
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服