大话设计模式读书笔记(抽象工厂模式)

人物:大鸟,小菜

事件:一天晚上小菜回来得很晚,大鸟问原因,原来小菜是因为做完一个项目,项目以SQL Server为基础,但后面要改成Access,换数据库用时很久导致了加班很晚才回来,大鸟告诉小菜用抽象工厂模式可以帮他在下一次还要换数据库时,节省很多时间


抽象工厂模式:

1.先实现最基本的数据访问方式,阐述不足,引出改进点

2.接着用工厂方法模式进行改进,阐述剩下的不足,引出抽象工厂模式

3.介绍抽象工厂模式

4.结合抽象工厂模式实现案例

5.阐述抽象工厂模式优劣

需求:大鸟让小菜先实现最近本的数据访问

最近本的数据访问

用户类

@Data
public class User {
    private int id;
    private String name;
}

SqlServerUser类,用于操作User表(这里只有新增和选取一条记录的功能)

@Slf4j
public class SqlServerUser {
    public void insert(User user) {
        log.info("在SQL Server中给User表增加一条记录");
    }

    public User getUser(int id) {
        log.info("在SQL Server中根据id在User表得到一条记录");
        return null;
    }
}

客户端代码:

public static void main(String[] args) {
    User user = new User();
    SqlServerUser su = new SqlServerUser();
    su.insert(user);
    su.getUser(1);
}

小菜:这就是我最开始写的,很简单。

大鸟:之所以不能换数据库,是因为SqlServerUser su = new SqlServerUser();这里使得su这个对象被框死在了Sql Server上,如果是灵活的,多态的,那么在执行su.insert()语句时,就不用考虑是在Sql Server还是Access上了 

小菜:那我可以使用工厂方法模式,因为工厂方法模式是定义用来创建对象的接口,让子类决定实例化哪一个类,用工厂方法模式来封装new SqlServer所造成的变化

用了工厂方法模式的数据访问

代码结构图

UserService接口,用于客户端访问,解除与具体数据库访问的耦合:

public interface UserService {
    void insert(User user);
User getUser(
int id); }

SqlServerUser类,用于访问SQL Server的user:

@Slf4j
public class SqlServerUser implements UserService {
    @Override
    public void insert(User user) {
        log.info("在SQL Server中给User表增加一条记录");
    }

    @Override
    public User getUser(int id) {
        log.info("在SQL Server中根据id在User表得到一条记录");
        return null;
    }
}

AccessUser类,用于访问Access的user:

@Slf4j
public class AccessUser implements UserService {
    @Override
    public void insert(User user) {
        log.info("在Access中给User表增加一条记录");
    }

    @Override
    public User getUser(int id) {
        log.info("在Access中根据id在User表得到一条记录");
        return null;
    }
}

Factory接口,定义一个创建访问User表对象的抽象的工厂接口:

public interface Factory {
    UserService createUser();
}

SqlServerFactory类,实现Factory接口,实例化SQLServerUser:

public class SqlServerFactory implements Factory {
    @Override
    public UserService createUser() {
        return new SqlServerUser();
    }
}

AccessFactory类,实现Factory类,实例化AccessUser:

public class AccessFactory implements Factory {
    @Override
    public UserService createUser() {
        return new AccessUser();
    }
}

客户端代码:

public static void main(String[] args) {
    User user = new User();
    Factory factory = new SqlServerFactory();
    UserService iu = factory.createUser();
    iu.insert(user);
    iu.getUser(1);
}

大鸟:很好,只有User类和User操作类时,是只需要工厂方法模式的,但显然数据库中有很多表,又该怎么做呢?我来介绍下抽象工厂模式就知道了

抽象工厂模式

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

2.代码结构图:

其中:

(1)AbstractProductA和AbstractProductB是两个抽象产品,之所以抽象是因为它们都有可能有两种不同的实现

(2)ProductA1,ProductA2,ProductB1,ProductB2都是对抽象产品具体分类的实现()ProductA1,ProductA2可以分别理解为:SqlServerUser和AccessUser)

(3)Factory是一个抽象工厂的接口,里面应该包含所有产品创建的抽象方法

(4)而ConcreteFactory1和ConcreteFactory2就是具体的工厂了(例如:SqlServerFactory和AccessFactory)

结合抽象工厂模式进行数据访问

IDepartment接口,用于客户端的访问,解除与具体数据库访问的耦合:

public interface IDepartment {
    void insert(Department department);
Department getDepartment(
int id); }

Department类:

@Data
public class Department {
    private int id;
    private String name;
}

SqlServerDepartment类,用于访问SQLServer:

@Slf4j
public class SqlServerDepartment implements IDepartment {
    @Override
    public void insert(Department department) {
        log.info("在SQL Server中给Department表增加一条记录");
    }

    @Override
    public Department getDepartment(int id) {
        log.info("在SQL Server中根据id得到Department表一条记录");
        return null;
    }
}

AccessDepartment类,用于访问Access:

@Slf4j
public class AccessDepartment implements IDepartment {
    @Override
    public void insert(Department department) {
        log.info("在Access中给Department表增加一条记录");
    }

    @Override
    public Department getDepartment(int id) {
        log.info("在Access中根据id得到Department表一条记录");
        return null;
    }
}

IFactory接口,用于定义一个创建访问User表对象的抽象的工厂接口:

public interface IFactory {
    UserService createUser();

    IDepartment createDepartment();
}

SQLServerFactory类,用于实现IFactory接口,实例化SQLServerUser和SQLServerDepartment:

public class SqlServerFactory implements IFactory {
    @Override
    public UserService createUser() {
        return new SqlServerUser();
    }

    @Override
    public IDepartment createDepartment() {
        return new SqlServerDepartment();
    }
}

AccessFactory类,用于实现IFactory接口,实例化AccessUser和AccessDepartment:

public class AccessFactory implements IFactory {
    @Override
    public UserService createUser() {
        return new AccessUser();
    }

    @Override
    public IDepartment createDepartment() {
        return new AccessDepartment();
    }
}

客户端代码:

public static void main(String[] args) {
    User user = new User();
    Department dept = new Department();
    IFactory factory = new AccessFactory();
    UserService iu = factory.createUser();
    iu.insert(user);
    iu.getUser(1);
    IDepartment id = factory.createDepartment();
    id.insert(dept);
    id.getDepartment(1);
}

结果显示如下:

在Access中给User表增加一条记录
在Access中根据id在User表得到一条记录
在Access中给Department表增加一条记录
在Access中根据id得到Department表一条记录

抽象工厂模式的优缺点 

优点:

1.易于交换产品系列,由于工厂类在一个应用中只需要在初始化的时候出现过一次(如:IFactory factory = new AccessFactory()),这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置

2.它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中

缺点:

1.虽然切换数据库很方便,但如果要加新的功能,比如加一个项目表Project,这时至少要增加IProject,SqlServerProject,AccessProject,还需要改IFactory,SqlServerFactory,AccessFactory才能实现,很麻烦

2.还有就是虽然IFactory factory = new AccessFactory()这样声明一次很容易改,但如果有100个地方用到,那不是要一个一个改?

用简单工厂改进抽象工厂

代码结构图:

DataAccess类:

public class DataAccess {
    private static String db = "SqlServer";
    //private static String db = "Access";

    public static UserService createUser() {
        UserService result = null;
        switch (db) {
            case "SqlServer":
                result = new SqlServerUser();
                break;
            case "Access":
                result = new AccessUser();
                break;
        }
        return result;
    }

    public static IDepartment createDepartment() {
        IDepartment result = null;
        switch (db) {
            case "SqlServer":
                result = new SqlServerDepartment();
                break;
            case "Access":
                result = new AccessDepartment();
                break;
        }
        return result;
    }
}

客户端代码:

public static void main(String[] args) {
    User user = new User();
    Department dept = new Department();

    UserService iu = DataAccess.createUser();

    iu.insert(user);
    iu.getUser(1);

    IDepartment id = DataAccess.createDepartment();
    id.insert(dept);
    id.getDepartment(1);
}

小结:这样抛弃了IFactory,SqlServerFactory,AccessFactory三个工厂类,用DataAccess类取而代之。由于事先设置了db值(SQLServer或Access),所以在客户端只需要DataAccess.createUser()或者DataAccess.createDepartment()就行,客户端没有出现SqlServer或Access字样,从而达到了解耦的目的。

         但这样还是存在一个问题(在简单工厂模式,和简单工厂模式与工厂方法模式结合时都存在的问题),如果要新增Oracle,就需要在DataAccess中的switch case中改动了,由此为了解决这个问题引入下面内容。

用反射+抽象工厂模式的数据访问

暂存

用反射+配置文件实现的数据访问

暂存

原文地址:https://www.cnblogs.com/wencheng9012/p/13426697.html