大话设计模式之抽象工厂模式总结-java实现

注:示例来自《大话设计模式》

现有如下需求 写一个基本的数据访问程序 数据库用SqlServer 简单代码实现如下

用户类

package Test15;

public class User {

    private int id;
    private String name;

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

}

操作表类

package Test15;

public class SqlserverUser {

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

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

}

客户端代码

package Test15;

public class Program {

    public static void main(String[] args) {

        User user = new User();

        SqlserverUser su = new SqlserverUser();
        su.Insert(user);
        su.GetUser(1);

    }

}

上面的写法 客户端与SqlServer强耦合 如果想要更换数据库为Access 该怎么办呢
下面使用工厂方法模式进行重构 代码如下

IUser接口

package Test15;

public interface IUser {

    void Insert(User user);

    User GetUser(int id);

}

SqlserverUser类

package Test15;

public class SqlserverUser implements IUser {

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

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

}

AccessUser类

package Test15;

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接口

package Test15;

public interface IFactory {

    IUser CreateUser();

}

SqlServerFactory类

package Test15;

public class SqlServerFactory implements IFactory {

    @Override
    public IUser CreateUser() {
        return new SqlserverUser();
    }

}

AccessFactory类

package Test15;

public class AccessFactory implements IFactory {

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

}

客户端代码

package Test15;

public class Program {

    public static void main(String[] args) {

        User user = new User();
        //IFactory factory = new SqlServerFactory();
        IFactory factory = new AccessFactory();

        IUser iu = factory.CreateUser();

        iu.Insert(user);
        iu.GetUser(1);

    }

}

现在如果要换数据库 只需要把new SqlServerFactory()改成new AccessFactory()
但是问题还没有完全解决 数据库里不可能只有一个User表 很可能有其他表 比如增加部门表 此时该怎么办呢 修改代码如下

部门类

package Test15;

public class Department {

    private int id;
    private String deptName;;

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getDeptName() {
        return deptName;
    }
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }

}

IDepartment接口

package Test15;

public interface IDepartment {

    void Insert(Department department);

    Department GetDepartment(int id);

}

SqlserverDepartment类

package Test15;

public class SqlserverDepartment implements IDepartment {

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

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

}

AccessDepartment类

package Test15;

public class AccessDepartment implements IDepartment {

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

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

}

IFactory接口

package Test15;

public interface IFactory {

    IUser CreateUser();

    IDepartment CreateDepartment();

}

SqlServerFactory类

package Test15;

public class SqlServerFactory implements IFactory {

    @Override
    public IUser CreateUser() {
        return new SqlserverUser();
    }

    @Override
    public IDepartment CreateDepartment()
    {
        return new SqlserverDepartment();
    }

}

AccessFactory类

package Test15;

public class AccessFactory implements IFactory {

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

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

}

客户端代码

package Test15;

public class Program {

    public static void main(String[] args) {

        User user = new User();
        Department dept = new Department();

        //IFactory factory = new SqlServerFactory();
        IFactory factory = new AccessFactory();
        IUser iu = factory.CreateUser();

        iu.Insert(user);
        iu.GetUser(1);

        IDepartment id = factory.CreateDepartment();
        id.Insert(dept);
        id.GetDepartment(1);

    }

}

不知不觉间 我们已经重构出了抽象工厂模式

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

抽象工厂模式最大的好处便是易于交换产品系列 由于具体工厂类 例如IFactory factory = new AccessFactory() 在一个应用中只需要在初始化的时候出现一次 这就使得改变一个应用的具体工厂变得非常容易 它只需要改变具体工厂即可使用不同的产品配置
第二大好处是 它让具体的创建实例过程与客户端分离 客户端是通过它们的抽象接口操纵实例 产品的具体类名也被具体工厂的实现分离 不会出现在客户代码中

缺点 如果需要增加新的表 那么就要新增三个类 并且要修改三个工厂类 这样大批量的改动 显然是非常丑陋的做法

下面我们用简单工厂来改进抽象工厂 代码如下

DataAccess类

package Test15;

public class DataAccess {

    private static final String db = "Sqlserver";
    //private static final String db = "Access";

    public static IUser CreateUser()
    {
        IUser 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;
    }

}

客户端代码

package Test15;

public class Program {

    public static void main(String[] args) {

        User user = new User();
        Department dept = new Department();

        IUser iu = DataAccess.CreateUser();

        iu.Insert(user);
        iu.GetUser(1);

        IDepartment id = DataAccess.CreateDepartment();
        id.Insert(dept);
        id.GetDepartment(1);

    }

}

现在客户端已经不再受改动数据库访问的影响了 但是如果我需要增加Oracle数据库访问 本来抽象工厂只增加一个OracleFactory工厂类就可以了 现在就比较麻烦了
下面我们用反射+抽象工厂的方式进行重构 代码如下

修改DataAccess类

package Test15;

public class DataAccess {

    private static final String AssemblyName = "Test15";
    private static final String db = "Sqlserver";
    //private static final String db = "Access";

    public static IUser CreateUser() throws ClassNotFoundException, InstantiationException, IllegalAccessException
    {
        String className = AssemblyName+"."+db+"User";
        Class<IUser> forName = (Class<IUser>) Class.forName(className); 
        return forName.newInstance();
    }

    public static IDepartment CreateDepartment() throws ClassNotFoundException, InstantiationException, IllegalAccessException
    {
        String className = AssemblyName+"."+db+"Department";
        Class<IDepartment> forName = (Class<IDepartment>) Class.forName(className); 
        return forName.newInstance();
    }

}

现在如果增加Oracle数据访问 我们只需更改private static final String db = “Sqlserver”;为private static final String db = “Oracle”;
但是 总感觉还是有点遗憾 因为在更换数据库访问时 我还是需要去改程序重编译
下面我们用反射+配置文件的方式进行重构

添加一个sql.properties文件 内容如下

db=Sqlserver

DataAccess类

package Test15;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;

public class DataAccess {

    private static final String AssemblyName = "Test15";

    public static String getDb() throws IOException {

        Properties properties = new Properties();
        String db = "";
        // 使用BufferedReader加载properties配置文件生成对应的输入流
        BufferedReader bufferedReader = new BufferedReader(new FileReader(System.getProperty("user.dir")+"/src/test/java/Test15/sql.properties"));
        // 使用properties对象加载输入流
        properties.load(bufferedReader);
        //获取key对应的value值
        db = properties.getProperty("db");
        return db;

    }

    public static IUser CreateUser() throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException
    {   
        String className = AssemblyName+"."+getDb()+"User";
        Class<IUser> forName = (Class<IUser>) Class.forName(className);
        return forName.newInstance();
    }

    public static IDepartment CreateDepartment() throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException
    {
        String className = AssemblyName+"."+getDb()+"Department";
        Class<IDepartment> forName = (Class<IDepartment>) Class.forName(className); 
        return forName.newInstance();
    }

}

现在如果需要更换数据库 只需要修改配置文件就可以了 从这个角度上说 所有在用简单工厂的地方 都可以考虑用反射技术来去除switch或if 解除分支判断带来的耦合

猜你喜欢

转载自blog.csdn.net/qq_26814945/article/details/82382783