dao-impl-UserDaoImpForMySQL
package com.sunsplanter.dao.impl;
import com.sunsplanter.dao.UserDao;
public class UserDaoImpForMySQL implements UserDao {
@Override
public void deleteById() {
System.out.println("deleting User...");
}
}
dao-UserDao
package com.sunsplanter.dao;
//yuyuemima wg9b
public interface UserDao {
void deleteById();
}
service-Impl-UserServiceImp
package com.sunsplanter.service.impl;
import com.sunsplanter.dao.UserDao;
import com.sunsplanter.dao.impl.UserDaoImpForMySQL;
import com.sunsplanter.service.Userservice;
public class UserServiceImp implements Userservice {
//控制层调用(操作)持久层,因此必须创造一个dao对象(控制具体的对象)
//UserDao是抽象接口(框架),UserDaoImpForMySQL是实现类(实物),
//new出一个实物,填进框架
private UserDao userDao = new UserDaoImpForMySQL();
@Override
public void deleteUser() {
//然后操作具体的userDao对象调用UserDao的方法
userDao.deleteById();
}
}
service-UserService
package com.sunsplanter.service;
public interface Userservice {
void deleteUser();
}
web-UserAction
package com.sunsplanter.web;
import com.sunsplanter.service.Userservice;
import com.sunsplanter.service.impl.UserServiceImp;
public class UserAction {
//表现层调用(操作)控制层,因此必须创造一个service对象(控制具体的对象)
private Userservice userservice = new UserServiceImp();
//如果有删除信息的需要,就调用表现层的deleteRequest方法,再套娃调用控制层的deleteUser方法
public void deleteRequest(){
userservice.deleteUser();
}
}
client-Test
package com.sunsplanter.client;
import com.sunsplanter.web.UserAction;
public class Test {
public static void main(String[] args) {
//测试类,调用表现层去测试,因此先创造表现层对象
UserAction userAction = new UserAction();
userAction.deleteRequest();
}
}
执行Test中的main方法,嵌套调用表现-控制-持久,最终输出deleting。。。
OCP开闭原则
然而,若持久层的代码发生了改变-例如从MySQL数据库更改为Oracle数据库,则dao-Impl中的实现类要重写,例如重写一个UserDaoImpForOracle,在这个新类中实现具体连接Oracle的代码.。
最糟的是,这还没完,由于三层架构是嵌套调用的关系,例如:在service-impl-UserService实现类中,有“ private UserDao userDao = new UserDaoImpForMySQL();”既然将dao层的实现类更改为了ForOracle,那么自然,控制层的实现类也要修改。
这样就违反了OCP开闭原则:对扩展开放,对修改关闭。
在上例中,如果因为修改持久层牵动控制层,那么后续要做的测试工作非常麻烦,甚至可能修改错。
DIP依赖倒置原则
在初始版本中,表现层必须new出控制层的对象才能操纵控制层,控制层又必须new出持久层对象。也即上层(表现层)依赖了下层(控制层)。
要满足该原则,以web-UserAction表现层实现类来说:
//原本是这样,既然调用了控制层的实现类,那当然依赖于控制层
private Userservice userservice = new UserServiceImp();
//目标,不调用控制层实现类构造对象,但也能直接获取控制层的对象
private Userservice userservice;
如果代码是这样编写的,才算是完全面向接口编程,才符合依赖倒置原则。
但是随之而来的一个问题是,这样userDao是null,在执行的时候就会出现空指针异常。而解决空指针异常的问题,其实就是解决两个核心的问题:
● 第一个问题:谁来负责对象的创建。【也就是说谁来:new UserDaoImplForOracle()/new UserDaoImplForMySQL()】
● 第二个问题:谁来负责把创建的对象赋到这个属性上。【也就是说谁来把上面创建的对象赋给userDao属性】
如果我们把以上两个核心问题解决了,就可以做到既符合OCP开闭原则,又符合依赖倒置原则。
Spring框架可以做到。
在Spring框架中,它可以帮助我们new对象,并且它还可以将new出来的对象赋到属性上。换句话说,Spring框架可以帮助我们创建对象,并且可以帮助我们维护对象和对象之间的关系。比如:
Spring可以new出来UserDaoImplForMySQL对象,也可以new出来UserDaoImplForOracle对象,并且还可以让new出来的dao对象和service对象产生关系(产生关系其实本质上就是给属性赋值)。
很显然,这种方式是将对象的创建权/管理权交出去了,不再使用硬编码的方式了。同时也把对象关系的管理权交出去了,也不再使用硬编码的方式了。像这种把对象的创建权交出去,把对象关系的管理权交出去,被称为控制反转。
IoC控制反转
…控制反转,是面向对象编程中的一种设计思想,可以用来降低代码之间的耦合度,符合依赖倒置原则。
控制反转的核心是:将对象的创建权交出去,将对象和对象之间关系的管理权交出去,由第三方容器来负责创建与维护。
…Spring应用了IoC:一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。换种说法,不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
控制反转常见的实现方式:依赖注入(Dependency Injection,简称DI)
注入:让两个对象产生联系
通常,依赖注入的实现又包括两种方式:
● set方法注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
● 构造方法注入
public UserServiceImp(UserDao userDao) {
this.userDao = userDao;
}
而Spring框架就是一个实现了IoC思想的框架。
//原本是这样,既然调用了控制层的实现类,那当然依赖于控制层
private Userservice userservice = new UserServiceImp();
//目标,不调用控制层实现类构造对象,但也能直接获取控制层的对象
private Userservice userservice;
Spring框架可以帮忙创建并管理UserServiceImp对象,每一个Spring框架创建并管理的对象,都称为一个Bean。