欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

设计模式 | 抽象工厂模式(abstract factory)

程序员文章站 2022-04-28 14:44:50
定义: 定义: 提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。 结构:(书中图,侵删) 这个图相对来说有一点点复杂,其实就是在工厂方法模式的基础上做了一些扩展,工厂方法模式只用于生成一种产品(把上图ProductB相关的都去掉就是了),而抽象工厂模式可用于生产多种产品。 加上例 ......

定义:

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

结构:(书中图,侵删)

设计模式 | 抽象工厂模式(abstract factory)

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

实例:

鉴于书中的例子相当的常见,所以决定延用书中的例子。
就是更换数据库的例子。
假设系统中有员工、部门两个类。
然后系统需要使用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);

    }
}
大功告成!
 

总结:

抽象工厂设计模式和其他的工厂类设计模式一样,就是将客户端与具体的对象创建过程分离。
只不过这里所涉及到的不再是一种类,而是多种类,结构相对复杂。
同时也像上文说的一样,存在一些不足,可以具体情况具体分析,应该如何使用。