浏览以下代码:

package com.powernode.oa.controller;import com.powernode.oa.service.UserService;import com.powernode.oa.service.impl.UserServiceImpl;public class UserController {    private UserService userService = new UserServiceImpl();    public void login(){        String username = "admin";        String password = "123456";        boolean success = userService.login(username, password);        if (success) {            // 登录胜利        } else {            // 登录失败        }    }}package com.powernode.oa.service.impl;import com.powernode.oa.bean.User;import com.powernode.oa.dao.UserDao;import com.powernode.oa.dao.impl.UserDaoImplForMySQL;import com.powernode.oa.service.UserService;public class UserServiceImpl implements UserService {    private UserDao userDao = new UserDaoImplForMySQL();    public boolean login(String username, String password) {        User user = userDao.selectByUsernameAndPassword(username, password);        if (user != null) {            return true;        }        return false;    }}package com.powernode.oa.dao.impl;import com.powernode.oa.bean.User;import com.powernode.oa.dao.UserDao;public class UserDaoImplForMySQL implements UserDao {    public User selectByUsernameAndPassword(String username, String password) {        // 连贯MySQL数据库,依据用户名和明码查问用户信息        return null;    }}

能够看出,UserDaoImplForMySQL中次要是连贯MySQL数据库进行操作。如果更换到Oracle数据库上,则须要再提供一个UserDaoImplForOracle,如下:

package com.powernode.oa.dao.impl;import com.powernode.oa.bean.User;import com.powernode.oa.dao.UserDao;public class UserDaoImplForOracle implements UserDao {    public User selectByUsernameAndPassword(String username, String password) {        // 连贯Oracle数据库,依据用户名和明码查问用户信息        return null;    }}

很显著,以上的操作正在进行性能的扩大,增加了一个新的类UserDaoImplForOracle来应酬数据库的变动,这里的变动会引起连锁反应吗?当然会,如果想要切换到Oracle数据库上,UserServiceImpl类代码就须要批改,如下:

package com.powernode.oa.service.impl;import com.powernode.oa.bean.User;import com.powernode.oa.dao.UserDao;import com.powernode.oa.dao.impl.UserDaoImplForOracle;import com.powernode.oa.service.UserService;public class UserServiceImpl implements UserService {    //private UserDao userDao = new UserDaoImplForMySQL();    private UserDao userDao = new UserDaoImplForOracle();    public boolean login(String username, String password) {        User user = userDao.selectByUsernameAndPassword(username, password);        if (user != null) {            return true;        }        return false;    }}

1.1 OCP开闭准则

这样一来就违反了开闭准则OCP。开闭准则是这样说的:在软件开发过程中该当对扩大凋谢,对批改敞开。也就是说,如果在进行性能扩大的时候,增加额定的类是没问题的,但因为性能扩大而批改之前运行失常的程序,这是禁忌的,不被容许的。因为一旦批改之前运行失常的程序,就会导致我的项目整体要进行全方位的从新测试。这是相当麻烦的过程。导致以上问题的次要起因是:代码和代码之间的耦合度太高。如下图所示:

能够很显著的看出,下层是依赖上层的。UserController依赖UserServiceImpl,而UserServiceImpl依赖UserDaoImplForMySQL,这样就会导致上面只有改变,下面必然会受株连(跟着也会改),所谓牵一发而动全身。这样也就同时违反了另一个开发准则:依赖倒置准则。

1.2 依赖倒置准则DIP

依赖倒置准则(Dependence Inversion Principle),简称DIP,次要提倡面向形象编程,面向接口编程,不要面向具体编程,让下层不再依赖上层,上面改变了,下面的代码不会受到牵连。这样能够大大降低程序的耦合度,耦合度低了,扩大力就强了,同时代码复用性也会加强。(软件七大开发准则都是在为解耦合服务)
你可能会说,下面的代码曾经面向接口编程了呀:

的确曾经面向接口编程了,但对象的创立是:new UserDaoImplForOracle()显然并没有齐全面向接口编程,还是应用到了具体的接口实现类。什么叫做齐全面向接口编程?什么叫做完全符合依赖倒置准则呢?请看以下代码:

如果代码是这样编写的,才算是齐全面向接口编程,才合乎依赖倒置准则。那你可能会问,这样userDao是null,在执行的时候就会呈现空指针异样呀。你说的有情理,的确是这样的,所以咱们要解决这个问题。解决空指针异样的问题,其实就是解决两个外围的问题:

  • 第一个问题:谁来负责对象的创立。【也就是说谁来:new UserDaoImplForOracle()/new UserDaoImplForMySQL()】
  • 第二个问题:谁来负责把创立的对象赋到这个属性上。【也就是说谁来把下面创立的对象赋给userDao属性】

如果咱们把以上两个外围问题解决了,就能够做到既合乎OCP开闭准则,又合乎依赖倒置准则。
很荣幸的告诉你:Spring框架能够做到。
在Spring框架中,它能够帮忙咱们new对象,并且它还能够将new进去的对象赋到属性上。换句话说,Spring框架能够帮忙咱们创建对象,并且能够帮忙咱们保护对象和对象之间的关系。比方:

Spring能够new进去UserDaoImplForMySQL对象,也能够new进去UserDaoImplForOracle对象,并且还能够让new进去的dao对象和service对象产生关系(产生关系其实实质上就是给属性赋值)。
很显然,这种形式是将对象的创立权/管理权交出去了,不再应用硬编码的形式了。同时也把对象关系的管理权交出去了,也不再应用硬编码的形式了。像这种把对象的创立权交出去,把对象关系的管理权交出去,被称为管制反转。

1.3 管制反转IoC

管制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计思维,能够用来升高代码之间的耦合度,合乎依赖倒置准则。
管制反转的外围是:将对象的创立权交出去,将对象和对象之间关系的管理权交出去,由第三方容器来负责创立与保护。
管制反转常见的实现形式:依赖注入(Dependency Injection,简称DI)
通常,依赖注入的实现由包含两种形式:

  • set办法注入
  • 构造方法注入

而Spring框架就是一个实现了IoC思维的框架。
IoC能够认为是一种全新的设计模式,然而实践和工夫成熟绝对较晚,并没有蕴含在GoF中。(GoF指的是23种设计模式)