设计模式之抽象工厂模式

一、场景

生活中总是存在着一些依赖于产品系列的问题,不同的系列中的产品可能相互不兼容,在使用时经常需要使用同一系列产品来完成工作。例如:单反摄像机的品牌有多种,如佳能、尼康等。单反摄像机配件是玩摄影的一个很大的开支,为了使拍出的照片更加漂亮,除了购买机身外还需要购买一些配件,如镜头、支架等。而不同品牌的这些配件是不相兼容,因此买了佳能的单反相机不能使用尼康的镜头。所以,一般购买某个品牌的单反后,就会购买其相应的配件。

生活中还有一些别的相似场景,如:

  1. 电脑组装。电脑组装时需要考虑CUP、主板、内存等兼容性问题,不同的系列如Intell和AMD等有所不同,可能会引起兼容性问题。

  2. 操作系统软件。一个软件不能同时在不同操作系统上运行,必须针对不同的电脑系统开发相应的软件程序。如eclipse需要针对mac、window和linux设计软件,因为不同的系列使用的软件不同。

  3. 多个数据库切换问题。不同的数据库,如SQLServer、Access、MySql和Orcal等在连接方式、使用细节上有些不同,所以在编程中针对不同的数据库要定义相应的数据库访问操作。

二、定义和意图

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

抽象工厂与工厂方法的区别是,抽象工厂是针对多个系列产品,而工厂方法是针对同个系列产品。

三、结构

AbstractFactory:定义了创建抽象产品对象的操作接口。


public interface AbstractFactory {
    AbstractProductA createProductA();
    AbstractProductB createProductB();
}

ConcreteFactory:实现创建具体产品对象的操作


public class ConcreteFactory1 implements AbstractFactory {

    @Override

    public AbstractProductA createProductA() {

        return new ProductA1();

    }

    @Override

    public AbstractProductB createProductB() {

        return new ProductB1();

    }

}


public class ConcreteFactory2 implements AbstractFactory {

    @Override

    public AbstractProductA createProductA() {

        return new ProductA2();

    }

    @Override

    public AbstractProductB createProductB() {

        return new ProductB2();

    }

}

AbstractProduct: 为一类产品对象声明接口


public interface AbstractProductA {

}



public interface AbstractProductB {

}

ConcreteProduct:具体的产品对象


//系列1的产品A

public class ProductA1 implements AbstractProductA {

    public ProductA1(){

        System.out.println("ProductA1");

    }

}

//系列2的产品A

public class ProductA2 implements AbstractProductA {

    public ProductA2(){

        System.out.println("ProductA2");

    }

}

//系列1的产品B

public class ProductB1 implements AbstractProductB {

    public ProductB1(){

        System.out.println("ProductB1");

    }

}

// 系列2的产品B

public class ProductB2 implements AbstractProductB {

    public ProductB2(){

        System.out.println("ProductB2");

    }

}

Client:使用AbstractFactory和AbstractProduct类声明的接口。

四、举例

以数据库的用户User和部门Dept的操作为例,不同数据库的SQL可能有些不同,因此需要为不同的数据库定义User和Dept的访问操作。

通过客户端client调用不同的工厂获取不同数据库的访问方式。具体如下:

DbFactory,创建访问不同数据库表的抽象工厂,


public interface IFactory {

    IDept getIDept();

    IUser getIUser();

}

MySqlFactory访问Mysql数据的对象工厂,AccessFacotry是访问Access数据库数据的对象工厂。


public class MysqlFactory implements IFactory {

    @Override

    public IDept getIDept() {

        return new MySqlDeptDao();

    }

    @Override

    public IUser getIUser() {

        return new MysqlUserDao();

    }

}

public class AccessFactory implements IFactory {

    @Override

    public IDept getIDept() {

        return new AccessDeptDao();

    }

    @Override

    public IUser getIUser() {

        return new AccessUserDao();

    }

}

访问User表的接口,和访问Dept的接口。


public interface IUser {

    //添加用户

    boolean addUser(User user);

    //删除用户

    boolean delUser(User user);

}

public interface IDept {

    //插入部门

    boolean addDept(Dept dept);

    //删除部门

    boolean delDept(Dept dept);

}

具体实现:


public class MySqlDeptDao implements IDept {

    @Override

    public boolean addDept(Dept dept) {

        System.out.println("mysql 中添加dept");

        return true;

    }

    @Override

    public boolean delDept(Dept dept) {

        System.out.println("mysql 中删除dept");

        return true;

    }

}


public class MysqlUserDao implements IUser{

    @Override

    public boolean addUser(User user) {

        System.out.println("mysql 中添加user");

        return true;

    }



    @Override

    public boolean delUser(User user) {

        System.out.println("mysql 中删除user");

        return true;

    }

}


public class AccessDeptDao implements IDept{

    @Override

    public boolean addDept(Dept dept) {

        System.out.println("access 中添加dept");

        return true;

    }



    @Override

    public boolean delDept(Dept dept) {

        System.out.println("access 中删除dept");

        return true;

    }

}


public class AccessUserDao implements IUser{

    @Override

    public boolean addUser(User user) {

        System.out.println("access 中添加user");

        return true;

    }



    @Override

    public boolean delUser(User user) {

        System.out.println("access 中删除user");

        return true;

    }

}

客户端的实现:


public class Client {

    public static void main(String[] args){

        Dept dept = new Dept();

        //只要更换IFactory 就可以切换不同的数据库处理。这边可以采用配置和反射机制改进

        IFactory dbFactory = new MysqlFactory();

        IDept deptDao = dbFactory.getIDept();

        deptDao.addDept(dept);

    }

}

五、优缺点

抽象工厂有以下优点:

  1. 易于交换产品系列。如上面的例子中,只要切换不同的具体工厂类就可以得到不同的数据库访问方式。

  2. 有利于保持产品的一致性。 因为具体工厂能够生产同一系列的产品,如MysqlFactory主要生产对Mysql数据库不同表访问的操作类,因此能够保持高度同一。

  3. 分离了具体类,实现了低耦合。将用户类和具体实现类分开,使得用户不知道对象的创建细节等,降低耦合性。

抽象工厂的不足:

  1. 难以支持新来的产品。 例如上例中只支持对User表和Dept表的操作,如果有新的表需要操作,意味着需要在很多地方添加方法,破坏了开放-封闭原则。

  2. 要求每个产品系列都要有一个新的具体工厂子类,即使这些系列产品差别很小也要为不同系列创建具体子类。这样也很容易造成类爆炸问题。

六、参考

[1] 四人帮《设计模式》

[2] 《大话设计模式》
[3] http://www.cnblogs.com/java-my-life/archive/2012/03/28/2418836.html

posted @ 2017-02-25 21:10  along-JL  阅读(278)  评论(0编辑  收藏