浏览以下代码:
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 种设计模式)