1.什么是开闭原则?
即对修改关闭对扩展开放,允许拓展系统功能而不允许修改功能的实现方式。
而在实际开发中,开闭原则就意味着需要面向接口编程,也就是面向接口编程才能更好的实现开闭原则。
2.什么是面向接口编程?
可能直接的理解是使用接口,然后创建实现类,但这好像并不完整。
完整的面向接口编程应该是基于MVC分层来看的,MVC分层之后,每一层在调用的时候,只创建接口,具体的实现类由配置文件决定。
接口限定了每个方法的功能、参数和返回值,这样的好处在于如果需求改变的时候,只需要修改配置文件和实现类即可。
而如果使用传统的方式去编写代码,无论那一层出现问题,你都需要解决上一层的交互和下一层的交互,而且需要整个去测试,避免出现问题,而面向接口开发则不会有这个问题。
3.面向接口编程的好处:
1.易于拓展:在拓展功能的时候,只需要更换添加接口中的方法更改实现类即可实现拓展,而其他层也只需要根据给出的方法来稍加改动。
2.隐藏实现:MVC分层之后,我们调用方法的时候,只需要调用接口中的方法即可,具体的实现由配置文件和其他具体的实现类来决定。
4.代码实现案例:
在这个案例中会用到反射、properties集合等,如果对于反射不了解的话,可以看一下我写的Java学习笔记之反射机制
首先是定义好的几个接口:
//数据访问层接口
public interface UserDao {
int login(String username, String password);
}
//业务层接口
public interface UserService {
int login(String username, String password);
}
然后是数据访问层的具体实现类:
//模拟MySQL实现用户登录
public class MySqlImpl implements UserDao {
@Override
public int login(String username, String password) {
System.out.println("mysql 实现用户登录");
return 0;
}
}
//模拟Oracle实现用户登录
public class OracleImpl implements UserDao {
@Override
public int login(String username, String password) {
System.out.println("oracle 实现用户登录");
return 0;
}
}
然后是普通的业务层:
public class UserServiceImpl implements UserService {
//根据需求创建具体的类,这样做并不好
//因为一般需要变更实现类,就需要大量修改代码
private MySqlImpl user= new MySqlImpl();
@Override
public int login(String username, String password) {
return user.login(username, password);
}
}
public class UserServiceImpl implements UserService {
//初步改良后,如果修改了数据库,就只需要修改后面的实现类即可
//这样做依旧有问题,那就是直接把代码写死了
//修改实现类之后仍然需要修改这里的代码
private UserDao userDao = new MySqlImpl();
@Override
public int login(String username, String password) {
return userDao.login(username, password);
}
}
如果想要更加便捷,那么就需要通过配置文件和反射来实现了,下面是配置文件:
# 配置接口的映射信息
# 配置UserDao接口的实现类
UserDao=com.ps.dao.impl.OracleImpl
# 配置业务层UserService的实现类
UserService=com.ps.service.impl.UserServiceImpl
接下来就是创建工具类,通过工具类来创建具体的实现类对象
//用来创建数据访问层的具体实现类
public class DaoUtil {
//通过反射来创建对应的类对象,所以需要使用泛型来获得
public static <T> T newInstance(Class<T> getClass) {
try {
//1.判断传入的是否是接口类型
//如果不是接口类型就无法读取到配置文件中的信息
if (getClass.isInterface()) {
//2.获得接口的接口名
String name = getClass.getSimpleName();
//3.从配置文件中获取实现类的类全名字符串
String className = ResourceBundle.getBundle("Dao").getString(name);
//4.利用反射获取这个类的class对象
Class result = Class.forName(className);
//5.利用class对象创建类
T resultClass = (T) result.newInstance();
//6.返回得到的类型
return resultClass;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
然后修改各个层的创建方式:
业务层:
public class UserServiceImpl implements UserService {
//这样编写的话,只需要更改配置文件中的信息,即可
private UserDao userDao = DaoUtil.newInstance(UserDao.class);
@Override
public int login(String username, String password) {
return userDao.login(username, password);
}
}
表现层:
public class UserServlet extends HttpServlet {
//这样编写的话,只需要更改配置文件中的信息,即可
private UserService userService = DaoUtil.newInstance(UserService.class);
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.从页面获取数据
String username = request.getParameter("username");
String password = request.getParameter("password");
//2.调用业务层的方法
int login = userService.login(username, password);
//3.把结果返回给页面
response.getWriter().print(login);
}
}