零基础介绍简单工厂、工厂模式和抽象工厂区别,抽象工厂的优化

参考书籍《大话设计模式》

案例

假设现在有基于Mysql数据库操作用户的的MysqlUserServiceImpl和基于Oracle数据库操作用户的OracleUserServiceImpl,代码如下:

public class User {

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

    User findById(Integer id);
}
public class MysqlUserServiceImpl implements UserService {
    @Override
    public void insert(User user) {
        System.out.println("mysql 插入 user");
    }

    @Override
    public User findById(Integer id) {
        System.out.println("mysql 获取 user");
        return null;
    }
}

public class OracleUserServiceImpl implements UserService {
    @Override
    public void insert(User user) {
        System.out.println("oracle 插入 user");
    }

    @Override
    public User findById(Integer id) {
        System.out.println("oracle 获取 user");
        return null;
    }
}

客户端的代码如下,可以在客户端选择自己需要的数据库对应的服务。

public class Client {
    public static void main(String[] args) {
        // 如果客户端想使用基于Mysql的服务
        // ......这里省略若干代码(在创建对应的产品时,可能有其他逻辑代码)
//        UserService userService = new MysqlUserServiceImpl();

        // 如果客户端想使用基于Oracle的服务
        // ......这里省略若干代码(在创建对应的产品时,可能有其他逻辑代码)
        UserService userService = new OracleUserServiceImpl();
        userService.insert(new User());
        userService.findById(1);
    }
}

对于这样的代码,有许多弊端。

  1. 客户端需要知道具体的服务(产品)。比如这里的MysqlUserServiceImpl和OracleUserServiceImpl
  2. 代码重复。如果有多个客户端,都这样创建服务(产品),因为在创建服务时可能还有其他逻辑代码,就会造成大量代码重复。

这些问题可以使用简单工厂解决。

简单工厂

提供一个工厂类来创建不同类型的产品对象,根据输入的参数决定返回哪个具体的产品。
一个工厂类负责创建所有产品,适用于产品种类较少的情况。

image.png

public interface UserService {
    void insert(User user);

    User findById(Integer id);
}
public class OracleUserServiceImpl implements UserService {
    @Override
    public void insert(User user) {
        System.out.println("oracle 插入 user");
    }

    @Override
    public User findById(Integer id) {
        System.out.println("oracle 获取 user");
        return null;
    }
}
public class MysqlUserServiceImpl implements UserService {
    @Override
    public void insert(User user) {
        System.out.println("mysql 插入 user");
    }

    @Override
    public User findById(Integer id) {
        System.out.println("mysql 获取 user");
        return null;
    }
}
public class UserServiceFactory {

    public static UserService getUserService(String databaseType) {
        UserService userService = null;
        switch (databaseType) {
            case "Mysql":
                userService = new MysqlUserServiceImpl();
                break;
            case "Oracle":
                userService = new OracleUserServiceImpl();
                break;
        }
        return userService;
    }
}
public class Client {
    public static void main(String[] args) {
        UserService userService = UserServiceFactory.getUserService("Mysql");
        userService.insert(new User());
        userService.findById(1);
    }
}

在简单工厂代码中,客户端依旧可以自行选择使用哪种数据库,同时让客户端的代码和业务代码隔离出来,变得更加简洁。
但是简单工厂也有自己的弊端。

  1. 违背了"开闭原则"。如果现在想要增加一个基于SqlServer数据库的服务SqlServerUserServiceImpl,那么我们得去修改工厂,在switch分支中增加代码。这违法了"开闭原则"(对扩展开放,对修改关闭)。
  2. 不同产品的创建都在同一个工厂中,会使这个工厂变得臃肿。每个产品创建时可能有其他逻辑代码(比如在创建MysqlUserServiceImpl后,调用它的set方法设置它的属性),那么这个工厂就会变得庞大复杂。

工厂模式

工厂方法模式将对象的创建延迟到子类中进行。具体的工厂类负责创建产品实例,而客户端只需要依赖抽象工厂接口,具体的工厂实现类会创建相应的具体产品。
通过工厂接口和具体工厂类,允许子类决定创建哪种产品,适用于需要扩展产品类型的情况。

image.png
现在我们把前面的简单工厂类UserServiceFactory定义成一个接口了,然后定义具体的工厂创建对应的产品。这样如果现在要新增一个基于Sqlserver的服务,只要新增一个具体的工厂SqlserverUserServiceFactory实现UserServiceFactory工厂,新增一个具体的产品SqlserverUserServiceImpl实现UserService。

public interface UserServiceFactory {

    UserService getUserService();
}
public class OracleUserServiceFactory implements UserServiceFactory {
    @Override
    public UserService getUserService() {
        // ......这里省略若干代码(在创建对应的产品时,可能有许多前置逻辑代码)
        return new OracleUserServiceImpl();
    }
}
public class MysqlUserServiceFactory implements UserServiceFactory {
    @Override
    public UserService getUserService() {
        // ......这里省略若干代码(在创建对应的产品时,可能有许多前置逻辑代码)
        return new MysqlUserServiceImpl();
    }
}
public class SqlserverUserServiceFactory implements UserServiceFactory {
    @Override
    public UserService getUserService() {
        // ......这里省略若干代码(在创建对应的产品时,可能有许多前置逻辑代码)
        return new SqlServerUserServiceImpl();
    }
}
public class Client {
    public static void main(String[] args) {
        // 客户端想要使用mysql的服务,创建对应的工厂即可
        //        UserServiceFactory userServiceFactory = new MysqlUserServiceFactory();
        // 客户端想要使用oracle的服务,创建对应的工厂即可
        //        UserServiceFactory userServiceFactory = new OracleUserServiceFactory();
        // 客户端想要使用sqlserver的服务,创建对应的工厂即可
        UserServiceFactory userServiceFactory = new SqlserverUserServiceFactory();
        UserService userService = userServiceFactory.getUserService();
        userService.insert(new User());
        userService.findById(1);
    }
}

这就解决了简单工厂中违背“开闭原则”问题。把创建产品放到对应的具体工厂,就能把创建某个产品时的其他逻辑代码维护到具体的工厂,就不会出现所有产品的创建的代码都在一个工厂类(简单工厂就是把所有产品的创建维护在一个工厂中)。
缺陷:

  1. 复杂度增高,新增一个产品时需要新增对应的工厂和产品类。
  2. 客户端需要知道具体的工厂。(这个和一开始没有使用简单工厂案例有点类似,那个是客户端需要知道具体的产品)

抽象工厂

用于创建一系列相关产品,适用于多个产品系列的情况。

image.png
假设现在新增一个专门管理部门的服务。对应代码如下:

public interface DeptService {
    void insert(Dept dept);

    Dept findById(Integer id);
}

public class MysqlDeptServiceImpl implements DeptService {
    @Override
    public void insert(Dept dept) {
        System.out.println("mysql 插入 dept");
    }

    @Override
    public Dept findById(Integer id) {
        System.out.println("mysql 获取 dept");
        return null;
    }
}

public class OracleDeptServiceImpl implements DeptService {
    @Override
    public void insert(Dept dept) {
        System.out.println("oracle 插入 dept");
    }

    @Override
    public Dept findById(Integer id) {
        System.out.println("oracle 获取 dept");
        return null;
    }
}
public interface UserService {
    void insert(User user);

    User findById(Integer id);
}

public class MysqlUserServiceImpl implements UserService {
    @Override
    public void insert(User user) {
        System.out.println("mysql 插入 user");
    }

    @Override
    public User findById(Integer id) {
        System.out.println("mysql 获取 user");
        return null;
    }
}


public class OracleUserServiceImpl implements UserService {
    @Override
    public void insert(User user) {
        System.out.println("oracle 插入 user");
    }

    @Override
    public User findById(Integer id) {
        System.out.println("oracle 获取 user");
        return null;
    }
}

public interface ServiceFactory {

    UserService getUserService();

    DeptService getDeptService();
}

public class MysqlServiceFactory implements ServiceFactory {
    @Override
    public UserService getUserService() {
        // ......这里省略若干代码(在创建对应的产品时,可能有许多前置逻辑代码)
        return new MysqlUserServiceImpl();
    }

    @Override
    public DeptService getDeptService() {
        return new MysqlDeptServiceImpl();
    }
}

public class OracleServiceFactory implements ServiceFactory {
    @Override
    public UserService getUserService() {
        // ......这里省略若干代码(在创建对应的产品时,可能有许多前置逻辑代码)
        return new OracleUserServiceImpl();
    }

    @Override
    public DeptService getDeptService() {
        return new OracleDeptServiceImpl();
    }
}
public class Client {
    public static void main(String[] args) {

        ServiceFactory serviceFactory = new OracleServiceFactory();
        UserService userService = serviceFactory.getUserService();
        DeptService deptService = serviceFactory.getDeptService();

        userService.insert(new User());
        userService.findById(1);

        deptService.insert(new Dept());
        deptService.findById(1);

    }
}

抽象工厂主要管理的是一系列的产品,比如这里基于Mysql的MysqlDeptServiceImpl和MysqlUserServiceImpl就是属于同一系列的产品。
抽象工厂这样做的好处:

  1. 便于切换产品的系列,可以避免依赖导致的错误。假设没有抽象工厂,那你正好使用了基于mysql的MysqlUserServiceImpl和基于oracle的OracleDeptServiceImpl,两个service之间存在某种依赖,会因为数据库的不同可能导致某些错误。
  2. 客户端不需要知道具体的产品。对于客户端来说,只知道是UserService,不需要知道是MysqlUserServiceImpl还是OracleUserServiceImpl

弊端:

  1. 新增产品复杂度变高。假设现在需要增加一个role表,需要创建RoleService、MysqlRoleServiceImpl、OracleRoleServiceImpl。需要改动ServiceFactory、MysqlServiceFactory、OracleServiceFactory。才能实现
  2. 客户端多时,切换产品系列复杂。假设现在有100个客户端,都使用的是ServiceFactory serviceFactory = new OracleServiceFactory();现在要改成Mysql的,那么就要修改100行代码为 ServiceFactory serviceFactory = new MysqlServiceFactory();

简单工厂+反射优化抽象工厂

public interface DeptService {
    void insert(Dept dept);

    Dept findById(Integer id);
}

public class OracleDeptServiceImpl implements DeptService {
    @Override
    public void insert(Dept dept) {
        System.out.println("oracle 插入 dept");
    }

    @Override
    public Dept findById(Integer id) {
        System.out.println("oracle 获取 dept");
        return null;
    }
}


public class MysqlDeptServiceImpl implements DeptService {
    @Override
    public void insert(Dept dept) {
        System.out.println("mysql 插入 dept");
    }

    @Override
    public Dept findById(Integer id) {
        System.out.println("mysql 获取 dept");
        return null;
    }
}
public interface UserService {
    void insert(User user);

    User findById(Integer id);
}

public class OracleUserServiceImpl implements UserService {
    @Override
    public void insert(User user) {
        System.out.println("oracle 插入 user");
    }

    @Override
    public User findById(Integer id) {
        System.out.println("oracle 获取 user");
        return null;
    }
}

public class MysqlUserServiceImpl implements UserService {
    @Override
    public void insert(User user) {
        System.out.println("mysql 插入 user");
    }

    @Override
    public User findById(Integer id) {
        System.out.println("mysql 获取 user");
        return null;
    }
}
public class ServiceFactory {

    private final static String dataType = "Oracle";
    private final static String packedPath = "org.aqiu.design1.demo4";

    public static UserService getUserService() throws Exception {
        return (UserService) Class.forName(packedPath + "."  + dataType + "UserServiceImpl").newInstance();
    }

    public static DeptService getDeptService() throws Exception {
        return (DeptService) Class.forName(packedPath + "."  + dataType + "DeptServiceImpl").newInstance();
    }
}
public class Client {
    public static void main(String[] args) throws Exception {
        UserService userService = ServiceFactory.getUserService();
        DeptService deptService = ServiceFactory.getDeptService();
        userService.insert(new User());
        userService.findById(1);

        deptService.insert(new Dept());
        deptService.findById(1);
    }
}

优化后的代码,去掉了抽象工厂和具体工厂。现在只有一个简单工厂,利用反射机制,复杂不同产品的创建,同时保持了抽象工厂的特点(能够管理一系列产品)。
这样解决了抽象工厂的弊端:

  1. 解决新增产品复杂度变高。现在新增产品,只要创建RoleService、MysqlRoleServiceImpl、OracleRoleServiceImpl,改一下ServiceFactory类,就能实现。
  2. 解决客户端多时,切换产品系列复杂。现在只要修改ServiceFactory类的属性dataType,就能让任意多的客户端从使用mysql改为使用oracle。甚至我们可以编写一个配置文件,让这个字段的值从配置文件中读取。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值