1. 终极手撕之架构大全: 分布式 + 框架 + 微服务 + 性能优化, 够不够?
2. 学习复盘笔记: 数据库 + 算法 + 数据结构 + JVM + 网络 + JAVA 核心知识整理 + JAVA + 分布式 + 操作系统 + 架构
1, 啥是耦合, 解耦?
既然是程序解耦, 那我们必须要先知道啥是耦合, 耦合简单来说就是程序的依赖关系, 而依赖关系则主要包括
1, 类之间的依赖 2, 方法间的依赖
比如下面这段代码:
- public class A{
- public int i;
- }
- public class B{
- public void put(A a){
- System.out.println(a.i);
- }
- }
上面这个例子中 A 类和 B 类之间存在一种强耦合关系, B 类直接依赖 A 类, B 类的 put 方法非 A 类类型不可, 我们把这种情况叫做强耦合关系.
实际开发中应该做到: 编译期不依赖, 运行时才依赖. 怎么理解呢? 我们很容易想到多态向上转型, 是的, 编译时不确定, 运行时才确定, 当然接触面更广一点的童鞋会想到接口回调, 是的接口回调方式也能有效的解耦! 如下代码:
- // 一个接口叫做 Inter, 里面定义了一个 happy()方法, 有两个类 A,B 实现了这个接口
- interface Inter{
- void happy();
- }
- class A implements Inter{
- @Override
- public void happy() {
- System.out.println("happy...A");
- }
- }
- class B implements Inter{
- @Override
- public void happy() {
- System.out.println("happy...B");
- }
- }
- public class Test{
- public void happys(Inter inter){
- inter.happy();
- }
- }
是的, 如上代码正是典型的接口回调, Test 类中的 happys 方法参数变的相对灵活起来, 代码中 Test 类与 A 类, B 类之间就存在一种弱耦合关系, Test 类的 happys 方法的参数可以使 A 类类型也可以是 B 类类型, 不像强耦合关系中非 A 类类型不可的情形.
从某一意义上来讲使用类的向上转型或接口回调的方式进行解耦都是利用多态的思想!
当然解耦的方式还有很多, 从根本意义上讲实现低耦合就是对两类之间进行解耦, 解除类之间的直接关系, 将直接关系转换成间接关系, 从而也有很多设计模式也对程序进行解耦, 比如: 适配器模式, 观察者模式, 工厂模式.... 总之, 必须明确一点: 耦合性强的程序独立性很差!
2, jdbc 程序进行解耦
先来看一段代码:
- //1, 注册驱动
- DriverManager.registerDriver(new com.MySQL.jdbc.Driver()); // 如果把 jdbc 的 MySQLjar 包依赖去除直接编译失败提示没有 MySQL
- //2, 获取连接
- Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/ufida","root","root");
- //3, 获取操作数据库的预处理对象
- PreparedStatement pstm=conn.prepareStatement("select * from client");
- //4, 执行 SQL, 得到结果集
- ResultSet rs=pstm.executeQuery();
- //5 \ 遍历结果集
- while(rs.next()){
- System.out.println(rs.getString("name"));
- }
- //6, 释放资源
- rs.close();
- pstm.close();
- conn.close();
等等等等, 好熟悉好怀念的代码.....
没错就是 jdbc 的代码, 不是用来怀旧的, 而是如果这样设计, 你会觉得这样的程序耦合性如何? 又如何进行解耦? 先仔细思考一番.
一分钟过去了..... 两分钟过去了.....
好了, 我们都知道 jdbc 连接 MySQL 需要一个 MySQL-connector 的 jar 包, 如果我们把这个 jar 包依赖或者这个 jar 包给去掉, 显然上面的这个程序会编译报错, 如下图
image.PNG
显然这样的程序耦合性过高! 于是我们可以这样设计, 将第一步的注册驱动代码 new 的方式改成反射的方式如下:
- //1,new 的方式注册驱动
- DriverManager.registerDriver(new com.MySQL.jdbc.Driver()); // 如果把 jdbc 的 MySQLjar 包依赖去除直接编译失败提示没有 MySQL 相关的 jar 包
改为如下方式
- //2, 反射的方式注册驱动
- Class.forName("com.mysql.jdbc.Driver"); // 改用这种方式注册驱动会发现不会编译失败, 相比上面的方式相对解耦, 但是依然存在缺陷: 若连接改为 Oracle 数据库, 这里的字符串又要进行改动!
正如注释的解释一样, 又一个缺陷就浮现了: 若连接改为 Oracle 数据库, 这里的字符串又要进行改动!
于是对于这个 jdbc 程序来说就有这样的一个解耦思路:
第一步: 通过反射来创建对象, 尽量避免使用 new 关键字 第二步: 通过读取配置文件来获取创建的对象全限定类名
3, 传统 dao,service,controller 的程序耦合性
顺着 jdbc 程序的解耦思路, 我们再来看看传统 dao,service,controller 的程序耦合性分析
由于只是一个 demo, 省去 dao 层的操作.....
定义一个 Service 接口
- public interface IAccountOldService{
- public void save();
- }
Service 接口实现类
- public class AccountServiceOldImpl implements IAccountOldService{
- @Override
- public void save() {
- System.out.println("save 成功一个账户....");
- }
- }
controller 代码:
- public class AccountCencollertOld {
- public static void main(String[] args) {
- IAccountOldService iaccount=new AccountServiceOldImpl ();
- iaccount.save(); // 运行结果: save 成功一个账户....
- }
- }
到这里, 有何想法? 表面上来看是没有一点问题的, So Beautiful, 但仔细的看. 表现层与业务层, 业务层与持久层紧紧的互相依赖关联, 这与我们开发程序的高内聚低耦合原则相违背, 哦 My God,So Bad! 我们顺着 jdbc 程序的解耦思路, 我们应该尽量避免使用 new 关键字, 我们发现这些层里面 service 层 new 持久层 dao,controller 表现层 new 业务层 service.... 太糟糕了
那么对此, 你有何解耦思路?
4, 使用工厂模式实现解耦
别想了, 工厂模式实现程序解耦你值得拥有! 顺着 jdbc 程序的解耦思路:
1, 通过读取配置文件来获取创建的对象全限定类名 2, 通过反射来创建对象, 尽量避免使用 new 关键字
- /**
- * 一个创建 Bean 对象的工厂
- *
- * 1, 需要一个配置文件来配置我们的 service 和 dao 配置文件的内容: 唯一标识 = 全限定类名(key-value)
- * 2, 通过读取配置文件中配置的内容, 反射创建对象
- *
- * 场景: 主要是 service 调用 dao,controller 调用 service 的程序. 这里面耦合性非常的高, 互相 new 互相依赖
- *
- * 为了解耦, 利用工厂模式进行
- */
- public class BeanFactoryOld {
- private static Properties props;
- static{
- try {
- // 实例化对象
- props = new Properties();
- // 获取 properties 文件的流对象
- InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
- props.load(in);// 加载其对应路径下的配置文件
- }catch (Exception e){
- throw new ExceptionInInitializerError("初始化 properties 失败!");
- }
- }
- // 根据 bean 的名称获取 bean 对象
- public static Object getBean(String beanName){
- Object bean=null;
- try {
- String beanPath= props.getProperty(beanName);
- bean = Class.forName(beanPath).newInstance(); // 这里的 newInstance 创建实例 (默认无参构造器) 每次执行都需要创建一次
- } catch (Exception e) {
- e.printStackTrace();
- }
- return bean;
- }
- }
- /**
- * 这里模拟一个 controller 调用 service
- *
- */
- public class AccountCencollertOld {
- public static void main(String[] args) {
- // IAccountOldService iaccount=new AccountServiceOldImpl (); // 使用工厂方法不再通过 new 方式
- IAccountOldService iaccount= (IAccountOldService) BeanFactoryOld.getBean("accountServiceOld");
- iaccount.save(); // 运行结果: save 成功一个账户.... 说明成功调用了 service
- }
- }
- for(int i=0;i<5;i++){
- IAccountOldService iaccount= (IAccountOldService) BeanFactoryOld.getBean("accountServiceOld");
- iaccount.save();
- }
- public class AccountServiceImpl implements IAccountService {
- // 定义类成员
- private int i=1;
- @Override
- public void save() {
- System.out.println("save 成功一个账户....");
- System.out.println(i);
- i++;
- }
- }
- public class AccountServiceOldImpl implements IAccountOldService {
- // private int i=1;
- @Override
- public void save() {
- int i=1; // 改为局部变量
- System.out.println("save 成功一个账户....");
- System.out.println(i);
- i++;
- }
- }
- public interface IAccountService {
- public void save();
- }
- public class AccountServiceImpl implements IAccountService{
- @Override
- public void save() {
- System.out.println("save 成功一个账户....");
- }
- }
- /**
- * 这里模拟一个 controller 调用 service
- *
- */
- public class AccountCencollert {
- public static void main(String[] args) {
- // IAccountService iaccount=new AccountServiceImpl();
- IAccountService iaccount= (IAccountService) BeanFactory.getBean("accountService");
- iaccount.save(); // 运行结果: save 成功一个账户.... 说明了成功调用了 service
- }
- }
- public class BeanFactory {
- private static Properties props;
- // 定义一个 map 容器, 用于存放创建的对象
- private static Map<String,Object> beans; // 改进的代码 ============
- static{
- try {
- // 实例化对象
- props = new Properties();
- // 获取 properties 文件的流对象
- InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
- props.load(in);// 加载其对应路径下的配置文件
- //////////////////// 以下是改进的代码 =======================
- // 实例化容器
- beans=new HashMap<String,Object>();
- // 取出配置文件中所有的 key 值
- Enumeration<Object> keys = props.keys();
- // 遍历枚举
- while(keys.hasMoreElements()){
- // 取出每个 key
- String key = keys.nextElement().toString();
- // 根据 key 取出对应的 value (这里因为每个 value 值对应着类路径)
- String beanPath = props.getProperty(key);
- // 反射创建对象
- Object value = Class.forName(beanPath).newInstance();
- // 把 key 和 value 存入容器中
- beans.put(key,value);
- }
- }catch (Exception e){
- throw new ExceptionInInitializerError("初始化 properties 失败!");
- }
- }
- // 随着代码的改进, 我们就可以简化下面的获取 bean 对象的方法, 如下代码
- /**
- * 根据 bean 的名称获取对象(单例)
- */
- public static Object getBean(String beanName){
- // 通过 Map 容器对应 key 来获取对应对象
- return beans.get(beanName); // 这里通过 Map 容器中获取, 这样就不会每次都创建一次实例!
- }
- // 不再使用下面的方法
- /*
- // 根据 bean 的名称获取 bean 对象
- public static Object getBean(String beanName){
- Object bean=null;
- try {
- String beanPath= props.getProperty(beanName);
- bean = Class.forName(beanPath).newInstance(); // 这里的 newInstance 创建实例 (默认无参构造器) 每次执行都需要创建一次, 这样就不好了
- } catch (Exception e) {
- e.printStackTrace();
- }
- return bean;
- }*/
- }
- public class AccountServiceImpl implements IAccountService {
- private int i=1; // 类成员属性
- @Override
- public void save() {
- System.out.println("save 成功一个账户....");
- System.out.println(i);
- i++;// 二次改革代码
- }
- }
- /**
- * 这里模拟一个 controller 调用 service
- *
- */
- public class AccountCencollert {
- public static void main(String[] args) {
- for(int i=0;i<5;i++){
- IAccountService iaccount= (IAccountService) BeanFactory.getBean("accountService");
- System.out.println(iaccount); // 打印的是五个不同的对象, 说明是多例的
- iaccount.save(); // 会发现打印的 i 值都是 1, 并没有自增成功
- }
- }
- 1
- com.factory.service.impl.AccountServiceImpl@1540e19d
- 2
- com.factory.service.impl.AccountServiceImpl@1540e19d
- 3
- com.factory.service.impl.AccountServiceImpl@1540e19d
- 4
- com.factory.service.impl.AccountServiceImpl@1540e19d
- public class AccountServiceImpl implements IAccountService {
- @Override
- public void save() {
- int i=1; // 局部成员属性
- System.out.println("save 成功一个账户....");
- System.out.println(i);
- i++;
- }
- }
- 1
- com.factory.service.impl.AccountServiceImpl@1540e19d
- 1
- com.factory.service.impl.AccountServiceImpl@1540e19d
- 1
- com.factory.service.impl.AccountServiceImpl@1540e19d
- 1
- com.factory.service.impl.AccountServiceImpl@1540e19d
来源: http://www.jianshu.com/p/21d56cdd136c