为什么工厂模式可以解耦?(一)

1、聊聊解耦?

耦合:代码之间的关联关系称为耦合,具有强关联关系的称为强耦合。

解耦:解除代码之间的关联关系,使每个业务代码职责单一,目的明确,通常我们在业务上称为解耦。

2、代码示例

我们以传统的EJB开发模式为例子,先不以框架展示,大家可以看看一些改代码难受的场景。

业务来了:我需要把一段数据保存到mysql数据库中,按照分层逻辑实现(controller,service,dao)

Dao接口层:

public interface UserDao {

    /**
     * 保存的接口方法
     */

    void save();
}

Dao的mysql实现:

public class UserDaoMysqlImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("保存mysql数据库成功!");
    }
}

Service接口层:

public interface UserService {

    /**
     * 业务接口保存方法
     */
    void save();
}

Service的实现:

public class UserServiceImpl implements UserService {

    //业务层调用数据dao层,这里不解释了
    private UserDao userDao = new UserDaoMysqlImpl();

    @Override
    public void save() {
        userDao.save();
    }
}

Controller视图层:

public class UserController {

    private UserService userService = new UserServiceImpl();

    public void save(){
        userService.save();
    }
}

很明显,我们已经实现了业务功能:保存一段数据进mysql数据库

这个时候,你的产品经理说,客户mysql坏了,刚装了个oracle,你再改改吧?

然后你这个时候也就加个oracle,实际上不费时,需求我们再补充下

现在需求:保存一段数据,可以保存在mysql,也可以保存在oracle

上面已经有mysql代码了,我们可以知道,我需要增加个dao的实现,称为UserDaoOracleImpl

上代码先:

public class UserDaoOracleImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("保存oracle数据库成功!");
    }
}

ok我们还要改一个地方,就是UserServiceImpl,之前父类接口指向的子类引用要改成oracle

public class UserServiceImpl implements UserService {

    //业务层调用数据dao层,这里不解释了
//    private UserDao userDao = new UserDaoMysqlImpl();

    private UserDao userDao = new UserDaoOracleImpl();
    
    @Override
    public void save() {
        userDao.save();
    }
}

我们发现,在目前的需求形式上,dao的扩展我们是一定会需要改的,因为满足多态的特性,但是我们增加一个dao层的某个业务,连service业务层代码也要动,你想想,如果业务层代码达到了上千,即便你有了注释,改了某一层,另一层也要跟着改动,是不是很难受?

所以我们目前要解决:(如果每次dao进行扩展都去service修改源码来切换到新的dao,这样做法耦合度太高,每次都需要去修改UserServiceImpl的代码)

这个场景,我们很经典的称为耦合!!!!我们可以自己给耦合多一个定义

耦合:代码之间的关联关系称为耦合,更具体的说,在当前主流的职责划分层次(controller,service,dao)明确的前提下进行编码,某一层的改动,会导致另一个层跟着变动,可以称为耦合。

3、工厂模式可以解耦

在不了解工厂模式的前提下,就默认把这个作为生产对象的地方;

我们新建一个工厂,称为BeanFactory

/**
 * 定义一个bean工厂,专门生产普通对象
 */
public class BeanFactory {
    public static UserDao getBean() {
        return new UserDaoMysqlImpl();
    }
}

然后我们要对耦合的那一层修改,目前看是把service解耦了吧?我所有创建对象的操作,都在一个具体的工厂类里;

public class UserServiceImpl implements UserService {

    //业务层调用数据dao层,这里不解释了
//    private UserDao userDao = new UserDaoMysqlImpl();

//    private UserDao userDao = new UserDaoOracleImpl();

    private UserDao userDao = BeanFactory.getBean();
    @Override
    public void save() {
        userDao.save();
    }
}

很明显,我已经进行了service和dao解耦;但是!!!!!!

该死的需求是:我要改成oracle,sqlserver,......

那我们继续:我顺便把之前的getBean换成了mysql的

public class UserServiceImpl implements UserService {

    //业务层调用数据dao层,这里不解释了
//    private UserDao userDao = new UserDaoMysqlImpl();

//    private UserDao userDao = new UserDaoOracleImpl();

//    private UserDao userDao = BeanFactory.getMysqlBean();

    private UserDao userDao = BeanFactory.getOracleBean();
    @Override
    public void save() {
        userDao.save();
    }
}

很明显我现在已经把dao和service的耦合,转移到了工厂和service上了,这就是解耦的一步;但是大家还是疑惑,我感觉我代码增多了?或者更麻烦了?我们继续看

我们每次增加新的业务,扩展,都要修改工厂,所以这个我们可以不可以不在代码里直接做这个事情?--------引出配置文件

我们在resources下定义一个applicationContext.properties

userDao=com.chenxin.gmall.user.demo.dao.UserDaoMysqlImpl
userService=com.chenxin.gmall.user.demo.service.UserServiceImpl

如果我们需要换成oracle,我们只改这个配置文件,改成UserDaoOracleImpl,不需要去动代码

那我们刚刚的BeanFactory就又可以通过读取配置文件的方式,用反射来创建对象,反射创建对象是根据对象的全类名做的,不是直接new,看看效果

/**
 * 定义一个bean工厂,专门生产普通对象
 */
public class BeanFactory {

    private static Properties properties = new Properties();

    //1.加载配置文件
    static {
        InputStream resourceAsStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
        try {
            properties.load(resourceAsStream);
            resourceAsStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

//    public static UserDao getMysqlBean() {
//        return new UserDaoMysqlImpl();
//    }
//
//    public static UserDao getOracleBean() {
//        return new UserDaoOracleImpl();
//    }

    public static UserDao getBean(String key){
        // 2.使用key获得value
        String className = properties.getProperty(key);
        // 3.使用value利用反射技术创建对象
        try{
            return (UserDao) Class.forName(className).newInstance();
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }
    }
}

好了,我们来需求,我们要保存到oracle,sqlserver数据库,看看你是不是只需要多一个dao的oracle实现,然后去改配置文件就ok?多一个sqlserver后,改下applicationContext.properties

算了我直接写个代码吧:

/**
* 新增了sqlserver的支持,多态的表现
*/
public class UserDaoSqlServerImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("保存SqlServer数据库成功!");
    }
}

改下applicationContext.properties

userDao=com.chenxin.gmall.user.demo.dao.UserDaoSqlServerImpl
userService=com.chenxin.gmall.user.demo.service.UserServiceImpl

你可以试一下,是不是这么干的!

现在看来,是解耦了不少吧。但是会有人发现吗,这个getBean返回值,是UserDao,如果你有很多,是不是我们要写很多很多的Dao的getBean?别急,后面一步一步带你走向spring的思路

觉得还可以,先收藏下呗,我会持续更新思想和过程,到源码的!!!!

 

最后一句话,解耦不代表代码一定少,更多时候是你用更多的代码来解决人力成本,所以新手一定要记得,解耦的原则,是减少开发中出现的问题,增加开发效率,不代表代码一定会减少下去,希望不要有这样的误区!

 

 

 

 

 

你可能感兴趣的:(Spring源码分析篇)