Lying design pattern notes (xii) の abstract factory pattern

For chestnuts

Problem Description

Simulation Database Access "Add User" and "get User", assume that only the user class ID and Name two fields.

Simple implementation

User

/**
 * 用户类
 * Created by callmeDevil on 2019/7/28.
 */
public class User {

    private int id;
    private String name;

    // 省略 get set 方法

}

SqlServerUser

/**
 * 假设sqlServer 连接,用于操作User表
 * Created by callmeDevil on 2019/7/28.
 */
public class SqlServerUser {

    public void insert(User user){
        System.out.println("在SQL Server中给User表增加一条记录");
    }

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

}

test

public class Test {

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

}

Test Results

在SQL Server中给User表增加一条记录
在SQL Server中根据ID得到User表一条记录

There is a problem

If you need to connect to other databases, this wording can not be expanded, using the following factory method pattern to achieve

Factory Method pattern implementation

iuseris

/**
 * 用于客户端访问,解除与具体数据库访问的耦合
 * Created by callmeDevil on 2019/7/28.
 */
public interface IUser {
    void insert(User user);
    User getUser(int id);
}

SqlServerUser

/**
 * 用于访问SQL Server 的User
 * Created by callmeDevil on 2019/7/28.
 */
public class SqlServerUser implements IUser {

    @Override
    public void insert(User user) {
        System.out.println("在SQL Server中给User表增加一条记录");
    }

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

}

AccessUser

/**
 * 用于访问Access 的User
 * Created by callmeDevil on 2019/7/28.
 */
public class AccessUser implements IUser {

    @Override
    public void insert(User user) {
        System.out.println("在Access 中给User表增加一条记录");
    }

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

}

IFactory

/**
 * 定义一个创建访问User 表对象的抽象工厂接口
 * Created by callmeDevil on 2019/7/28.
 */
public interface IFactory {
    IUser createUser();
}

SqlServerFactory

/**
 * 实现IFactory 接口,实例化SQLServerUser
 * Created by callmeDevil on 2019/7/28.
 */
public class SqlServerFactory implements IFactory {
    @Override
    public IUser createUser() {
        return new SqlServerUser();
    }
}

AccessFactory

/**
 * 实现IFactory 接口,实例化AccessUser
 * Created by callmeDevil on 2019/7/28.
 */
public class AccessFactory implements IFactory {
    @Override
    public IUser createUser() {
        return new AccessUser();
    }
}

test

public class Test {
    public static void main(String[] args) {
        User user = new User();
        // 若要更改成 Access 数据库,只需要将此处改成
        // IFactory factory = new AccessFactory();
        IFactory factory = new SqlServerFactory();
        IUser iUser = factory.createUser();
        iUser.insert(user);
        iUser.getUser(1);
    }
}

The test results above.

Increased demand

If you want to add a table departments (Department), you need how to change?

Modify the implementation

Department

/**
 * 部门表
 * Created by callmeDevil on 2019/7/28.
 */
public class Department {

    private int id;
    private String name;

    // 省略 get set 方法

}

IDepartment

/**
 * 用于客户端访问,解除与具体数据库访问的耦合
 * Created by callmeDevil on 2019/7/28.
 */
public interface IDepartment {
    void insert(Department department);
    Department getDepartment(int id);
}

SqlServerDepartment

/**
 * 用于访问SqlServer 的Department
 * Created by callmeDevil on 2019/7/28.
 */
public class SqlServerDepartment implements IDepartment {

    @Override
    public void insert(Department department) {
        System.out.println("在 SqlServer 中给Department 表增加一条记录");
    }

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

}

AccessDepartment

/**
 * 用于访问Access 的Department
 * Created by callmeDevil on 2019/7/28.
 */
public class AccessDepartment implements IDepartment {

    @Override
    public void insert(Department department) {
        System.out.println("在Access 中给Department 表增加一条记录");
    }

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

}

IFactory

/**
 * 定义一个创建访问User 表对象的抽象工厂接口
 * Created by callmeDevil on 2019/7/28.
 */
public interface IFactory {
    IUser createUser();
    IDepartment createDepartment(); //增加的接口方法
}

SqlServerFactory

/**
 * 实现IFactory 接口,实例化SQLServerUser
 * Created by callmeDevil on 2019/7/28.
 */
public class SqlServerFactory implements IFactory {

    @Override
    public IUser createUser() {
        return new SqlServerUser();
    }

    @Override
    public IDepartment createDepartment() {
        return new SqlServerDepartment(); //增加了SqlServerDepartment 工厂
    }

}

AccessFactory

/**
 * 实现IFactory 接口,实例化AccessUser
 * Created by callmeDevil on 2019/7/28.
 */
public class AccessFactory implements IFactory {

    @Override
    public IUser createUser() {
        return new AccessUser();
    }

    @Override
    public IDepartment createDepartment() {
        return new AccessDepartment(); //增加了AccessDepartment 工厂
    }

}

test

public class Test {
    public static void main(String[] args) {
        User user = new User();
        Department dept = new Department();
        // 只需确定实例化哪一个数据库访问对象给 factory
        IFactory factory = new AccessFactory();
        // 则此时已于具体的数据库访问解除了依赖
        IUser iUser = factory.createUser();
        iUser.insert(user);
        iUser.getUser(1);

        IDepartment iDept = factory.createDepartment();
        iDept.insert(dept);
        iDept.getDepartment(1);
    }
}

Test Results

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

Abstract factory pattern

definition

Create a series of related or dependent objects interface, without specifying their concrete classes.

UML diagrams

Code

Actually achieved above changes have been met abstract factory pattern implementations, no example here.

Advantages and disadvantages

advantage

  • The biggest advantage is the ease of switching portfolio, due to the different concrete factory class, you only need to appear at initialization time in one application at a time, which makes an application to change the concrete factory has become very easy, it only needs to change the concrete factory you can use a different product configuration .
  • It allows the creation of specific examples into a separate client, the client is manipulating instances through their abstract interface, specific product class name is also specific separation plant, it will not appear in the client code .

Shortcoming

If you also add access to a list of items (Project), then the need to increase the three classes, IProject, SQLServerProject, AccessProject , also you need to change IFactory, ISQLServerFactory, AccessFactory can only be fully realized, it's too bad. Programming is an art, so large quantities of changes, obviously very ugly practice.

Simple abstract factory to improve factory

Removing IFactory The, SQLServerFactory, AccessFactory , to a the DataAccess , a simple model to achieve factory.

Structure chart

Code

DataAccess

/**
 * 统一管理数据库访问
 * Created by callmeDevil on 2019/7/28.
 */
public class DataAccess {

    // 数据库名称,可替换成 Access
    private static final String DB = "SqlServer";
//    private static final String DB = "Access";

    public static IUser createUser() {
        IUser user = null;
        switch (DB) {
            case "SqlServer":
                user = new SqlServerUser();
                break;
            case "Access":
                user = new AccessUser();
                break;
            default:
                break;
        }
        return user;
    }

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

}

test

public class Test {
    public static void main(String[] args) {
        User user = new User();
        Department dept = new Department();
        // 直接得到实际的数据库访问实例,而不存在任何的依赖
        IUser iUser = DataAccess.createUser();
        iUser.insert(user);
        iUser.getUser(1);

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

Test Results

在SQL Server中给User表增加一条记录
在SQL Server中根据ID得到User表一条记录
在SQL Server中给Department 表增加一条记录
在SQL Server中根据ID得到Department表一条记录

There is a problem

While solving the abstract factory pattern to modify many parts of the problem, but returned to the simple factory pattern beginning of the problem is that if you want to connect Oracle database, you need to modify the place is DataAccess class all methods swicth in plus case branch of.

+ + Abstract factory implemented reflection profile

Profiles (db.properties)

# 数据库名称,可更改成 Access
db=SqlServer

DataAccess

/**
 * 统一管理数据库访问
 * Created by callmeDevil on 2019/7/28.
 */
public class DataAccess {

    // 数据库名称,从配置文件中获取
    private static String DB;

    public static IUser createUser() throws Exception {
        if (DB == null || DB.trim() == "") {
            return null;
        }
        // 拼接具体数据库访问类的权限定名
        String className = "com.xxx." + DB + "User";
        return (IUser) Class.forName(className).newInstance();
    }

    public static IDepartment createDeptment() throws Exception {
        if (DB == null || DB.trim() == "") {
            return null;
        }
        // 拼接具体数据库访问类的权限定名
        String className = "com.xxx." + DB + "Department";
        return (IDepartment) Class.forName(className).newInstance();
    }

    public static String getDB() {
        return DB;
    }

    public static void setDB(String DB) {
        DataAccess.DB = DB;
    }

}

test

public class Test {
    public static void main(String[] args) throws Exception {
        // 加载配置文件
        Properties properties = new Properties();
        InputStream is = new FileInputStream(new File("xxx\\db.properties")); // 配置文件所在路径,当前方式采用绝对路径获取
        properties.load(is);
        is.close();
        String db = properties.getProperty("db");
        // 使用具体的数据库告诉管理类
        DataAccess dataAccess = new DataAccess();
        dataAccess.setDB(db);

        User user = new User();
        IUser iUser = dataAccess.createUser();
        iUser.insert(user);
        iUser.getUser(1);

        Department dept = new Department();
        IDepartment iDept = dataAccess.createDeptment();
        iDept.insert(dept);
        iDept.getDepartment(1);
    }
}

Test Results

在SQL Server中给User表增加一条记录
在SQL Server中根据ID得到User表一条记录
在SQL Server中给Department 表增加一条记录
在SQL Server中根据ID得到Department表一条记录

现在如果我们增加了 Oracle 数据库访问,相关类的增加是不可避免的,这点无论用任何办法都解决不了,不过这叫扩展,开放-封闭原则告诉我们,对于扩展,我们开放,但对于修改,我们应该尽量关闭,就目前实现方式而言,只需要将配置文件中改为 Oracle (如果新增的具体访问类名称为 OracleUserOracleDepartment 的话)即可达到目的,客户端也不需要任何修改。

反射的好处

所有在用简单工厂的地方,都可以考虑用反射技术来去除 switch 或 if,解除分支判断带来的耦合。

总结

可以发现到目前为止,就“工厂”而言,已经包含了三种设计模式:

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

对上述模式的不同点将在后续推出,本文篇幅已经过长,此处先不叙述。

Guess you like

Origin www.cnblogs.com/call-me-devil/p/11259982.html