程序之间耦合以及解耦问题探究

之前的一个jdbc查询数据的案例

package com.ithema.jdbc;

import java.sql.*;

/**
 * 程序耦合
 */
public class JdbcDemo1 {
    public static void main(String[] args) {
        try {
            DriverManager.registerDriver(new com.mysql.jdbc.Driver());
            Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/fresh","root","root");
            PreparedStatement pstm=conn.prepareStatement("select * from usertable");
            ResultSet rs=pstm.executeQuery();
            while (rs.next()){
                System.out.println(rs.getString("username"));
            }
            rs.close();
            pstm.close();
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

下面是pom里面的依赖包文件的引入

<dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
</dependencies>

当程序正常运行,可以看到是有查询到数据输出

9571bfde64cb19fa8fb6d827ef2b0e5d09e.jpg

但是当我们把jdbc的依赖包注销掉

<!--<dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
</dependencies>-->

程序还会正常查询语句嘛,显示结果如下

6f61839d2c4bc15406b2a1bf0074c55df19.jpg

在程序编译的初期就显示,这个依赖类不存在,那程序就不能运行,这个就是我们说的耦合!!简单点来说就是程序之间的依赖关系

耦合主要分为两种:类之间的依赖、方法之间的依赖

解耦的含义就是降低程序之间的依赖关系,所以我们在实际开发中要做到编译期不依赖,运行期才依赖

如何解决这个依赖问题?类之间的依赖关系

1、在创建对象的时候,使用反射来创建对象,避免使用new关键

3b28a5e792333a8df3b848731a80ef5ab87.jpg

划线部分就是使用了反射注册驱动,里面的内容只是字符串,不是一个类,但是里面字符串是一个mysql数据库,当以后要换数据库的时候还是要改驱动

2、通过读取配置文件来获取创建对象全限定类名

下面是另外一个具体案例

5554ad5a484ce4379f6e6318d72ef95fee8.jpg

三层架构我们前面都学过,层与层之间都是相互依赖的关系,ui(视图层)调用service里面的实现方法,service调用dao层持久层里面的方法实现数据的查询,这就使得相互之间的依赖关系很强,缺少那一个实现方法,程序都不能正常实现,那有什么好的解决方法没???

解决方法一、创建一个BeanFactory,用工厂模式解耦

633d9182fd19c95f35ae8fffa3d06362673.jpg

实现思路

1、需要一个文件来配置我们的service和dao,配置的内容为:唯一标识=全限定类名(keyvalue)

2、通过读取配置文件中配置的内容来反射创建对象

package com.ithema.jdbc.factory;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;

/**
 * 一个Bean对象的工厂
 * Bean在计算机英语中,有可重用组件的含义
 * JavaBean:用Java语言编写的可重用组件
 *       JavaBean>实体类
 * 作用它就是创建我们使用service和dao对象的
 *
 * 实现方法:
 * 1、需要一个文件来配置我们的service和dao
 *    配置内容:唯一标识=全限定类名(keyvalue)
 * 2、通过读取配置文件中配置的内容,反射对象
 *
 * 配置文件可以是properties或者是xml
 */
public class Beanfactory {
    //定义一个properties对象
    private static Properties props;
    //使用静态代码块创建Properites对象
    static {
        try {
            //实例化对象
            props=new Properties();
            //获取properites流对象
            InputStream in=Beanfactory.class.getClassLoader().getResourceAsStream("bean.properties");
            props.load(in);
        }catch (Exception e){
            throw new ExceptionInInitializerError("初始化properties失败");
        }
    }

    /**
     * 根据bean的名称获取bean对象
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName) {
        Object bean=null;
        try {
            String beanPath = props.getProperty(beanName);
            bean = Class.forName(beanPath).newInstance();//通过反射来创建对象
        } catch (Exception e) {
            e.printStackTrace();
        }
        return  bean;
    }
}

properites配置文件内容

#全限定类名
accountService=com.ithema.jdbc.service.impl.AccountServiceImpl
accountDao=com.ithema.jdbc.dao.impl.AccountDaoImpl
package com.ithema.jdbc.service;

/**
 * 账户业务层的接口
 */
public interface IAccountService {
    /**
     * 模拟保存账户
     */
    void saveAccount();
}
package com.ithema.jdbc.service.impl;

import com.ithema.jdbc.dao.IAccountDao;
import com.ithema.jdbc.dao.impl.AccountDaoImpl;
import com.ithema.jdbc.factory.Beanfactory;
import com.ithema.jdbc.service.IAccountService;

/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl implements IAccountService {
    //private IAccountDao accountDao=new AccountDaoImpl();
    private IAccountDao accountDao= (IAccountDao) Beanfactory.getBean("accountDao");
    public  void saveAccount(){
        accountDao.saveAccount();
    };
}
package com.ithema.jdbc.dao;
/**
 * 账户的持久层接口
 */
public interface IAccountDao {
    void saveAccount();
}
package com.ithema.jdbc.dao.impl;
import com.ithema.jdbc.dao.IAccountDao;

/**
 * 账户持久层实现类
 */
public class AccountDaoImpl implements IAccountDao {
    @Override
    public void saveAccount() {
        System.out.println("保存了账户!!");
    }
}

测试类实现代码,测试结果如下

package com.ithema.jdbc.ui;
import com.ithema.jdbc.factory.Beanfactory;
import com.ithema.jdbc.service.IAccountService;
import com.ithema.jdbc.service.impl.AccountServiceImpl;

/**
 * 模拟一个表现层,用于调用业务层
 */
public class Client {
    public static void main(String[] args) {
        //IAccountService as=new AccountServiceImpl();
        IAccountService as=(IAccountService) Beanfactory.getBean("accountService");
        as.saveAccount();
    }
}

f6af0fd16e209990de959ea959297f23d7a.jpg

但是用工厂模式解耦依然存在问题,当在test多次创建对象时候结果如下

package com.ithema.jdbc.ui;
import com.ithema.jdbc.factory.Beanfactory;
import com.ithema.jdbc.service.IAccountService;
import com.ithema.jdbc.service.impl.AccountServiceImpl;

/**
 * 模拟一个表现层,用于调用业务层
 */
public class Client {
    public static void main(String[] args) {
        //IAccountService as=new AccountServiceImpl();
        for (int i=0;i<5;i++){
            IAccountService as=(IAccountService) Beanfactory.getBean("accountService");
            System.out.println(as);
        }
        //as.saveAccount();
    }
}

04b983838f024ede2630e93c2ffb7b44928.jpg

每次调用构造函数都会创建一个对象,打印出来的是多例对象,那么单例对象和多例对象有什么区别???

9d3d5a2a28172c7863c37d3c8912b1de47c.jpg

多例每次初始化都会创建一个对象,这样的话就会出现线程问题,每次获取到的对象都是不一样的,而我们在servlet和dao层中一般都是采用单例模式

造成创建多例对象的原因

bb9ccfaf3484baf5310875d3fdcbf5c5dcb.jpg

那么有什么办法能解决这个问题嘛??那就是创建一个Map集合来保存反射创建出来的对象

package com.ithema.jdbc.factory;

import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 一个Bean对象的工厂
 * Bean在计算机英语中,有可重用组件的含义
 * JavaBean:用Java语言编写的可重用组件
 *       JavaBean>实体类
 * 作用它就是创建我们使用service和dao对象的
 *
 * 实现方法:
 * 1、需要一个文件来配置我们的service和dao
 *    配置内容:唯一标识=全限定类名(keyvalue)
 * 2、通过读取配置文件中配置的内容,反射对象
 *
 * 配置文件可以是properties或者是xml
 */
public class Beanfactory {
    //定义一个properties对象
    private static Properties props;
    //定义一个Map,用于存放我们创建的对象,我们把它称之为容器
    private static Map<String,Object> beans;
    //使用静态代码块创建Properites对象
    static {
        try {
            //实例化对象
            props=new Properties();
            //获取properites流对象
            InputStream in=Beanfactory.class.getClassLoader().getResourceAsStream("bean.properties");
            props.load(in);
            //实例化容器
            beans=new HashMap<String,Object>();
            //取出配置文件中所有的key
            Enumeration keys=props.keys();
            while (keys.hasMoreElements()){
                String key=keys.nextElement().toString();
                //获取value值
                String beanPath=props.getProperty(key);
                //创建反射对象
                Object value=Class.forName(beanPath).newInstance();
                //保存到容器中
                beans.put(key,value);
            }
        }catch (Exception e){
            throw new ExceptionInInitializerError("初始化properties失败");
        }
    }

    /**
     * 根据bean的名称获取bean对象
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName) {
        return beans.get(beanName);
    }
   /* public static Object getBean(String beanName) {
        Object bean=null;
        try {
            String beanPath = props.getProperty(beanName);
            bean = Class.forName(beanPath).newInstance();//通过反射来创建对象,但是每次都会默认调用构造函数创建新的对象
            //需要一个容器来保存对象
        } catch (Exception e) {
            e.printStackTrace();
        }
        return  bean;
    }*/
}

再次运行test类Client,得到就是一个单例对象

7e933d96da0c1926cb9803deb3e18045f92.jpg

转载于:https://my.oschina.net/u/4115727/blog/3049108

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值