java之削弱程序之间耦合性的问题的见解(建议在学习spring框架之前必看)

这篇文章建议大家在学习spring框架之前先看一下,作者也是在这条路上踩了好多坑。可以说,我是第二次回过头来学习spring框架。之前老师有讲过spring,但那只是教会你如何去用这个东西,那这个东西究竟底层是如何实现的,我觉得作为一个初始这个领域的人来说,非常有必要搞清楚他的底层运作原理。
我们选择用java来开发系统,一个必须要用到的工具就是数据库,毫无疑问,不管你用的是那种数据库,基本的实现思路都是一样的。一般都是注册驱动、创建连接、创建预定义SQL语句、封装结果集,处理返回的数据、关闭连接。大致就是这样的。
我们就拿注册驱动这个过程来说(其他的暂时不考虑):
Class.forName(“com.mysql.jdbc.Driver”);
DriverManager.registerDriver(new Driver());
以上这两个都可以去去注册一个数据库驱动类,为什么我们基本选择第一种方式来加载数据库驱动。原因就是DriverManager创建了两次Driver对象,而Class.foeName()他可以在运行时改变状态。所以推荐使用第一种方式来加载数据库驱动。当然,第一种方式也存在一些问题。代码耦合性的问题,我现在连接的是Mysql数据库,如果我需要Oracle数据库,那么,我又要去修改代码。所以,最好能够以配置文件的形式来修改是最好的。
好,说了这么多废话,引入今天的正题:

怎么削减代码之间的耦合性

我设计了一个简单的demo 来给大家说清楚这个问题:
1.目录结构如下:
在这里插入图片描述
这个目录结构就是我们正常遵守的MVC架构的模式。有数据访问层dao、业务逻辑层service、test在这里就代替contorller界面层(因为这个不涉及web),大家可能很好奇,还有一个bean层,这个我们暂时不说,后面讲这个bean层。
我们在一般的开发过程中,都是界面层去调用service层,service层调用dao层,dao在进行对数据库访问。那在这个过程当中我们的界面层需要把Service作为自己的一个属性,同理,在Service层也需要把dao层作为自己的一个属性,就是通过这种关系来实现上层对下层的调用,下层为上层提供服务。即如下:
contorller层把Service层作为自己的一个属性:
在这里插入图片描述
Service层把dao层作为自己的属性:
在这里插入图片描述
其实主要就两行代码:
private static EntityService entityService = new EntityServiceImpl(); private EntityDao entityDao = new EntityDaoImpl();
我们可以看出,这每个类之间的关联性是很强的。这样,不满足我们软件设计的目标,高内聚,低耦合。这个大家应该都知道。那问题来了,怎么解决这个问题呢?答案当然是反射。其实,光是通过反射是不够的。反射我们都知道他的作用,就是在程序运行期间来动态的编译和加载以及创建一个类。所以,这里我们要用到java当中的一个工厂设计模式(这里对工厂设计模式不作详细解释,可以参考我上一篇文章),工厂就是用来生产对象的。所以,在前面提到bean这个包面有BeanFactory这个类就是用来生产bean对象的。
接下来我们就解析一下通过工厂来创建我们所需要的对象:
首先,我们要准备一个配置文件(.properties)

EntityDao=com.tff.demo.jdbc.dao.impl.EntityDaoImpl
EntityService=com.tff.demo.jdbc.service.impl.EntityServiceImpl

保存为bean.properties。申明一下,文件名随便取,不是中文就ok,扩展名必须是properties这种格式的。我们分析一下这个文件,这种文件就是以键值对形式来获取的,等号左边就是键,右边就是值。这样写的目的就是我们可以通过键来获取相对应的值。我们在看一下BeanFactory这个类:

import java.util.Properties;

public class BeanFactory {
    //定义一个properties对象
    private static Properties pros;
    //使用静态代码块为properties赋值
    static {
        try {
            pros = new Properties();
            //获取properties刘对象的
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            pros.load(in);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    //根据bean获取bean对象
    /*
     *   tff
     * @param beanName 类的全路径名
     * @return  返回一个bean对象
     * */
    public static Object getBean(String beanName){
        Object bean = null;
        try{
            String beanPath = pros.getProperty(beanName);
            bean = Class.forName(beanPath).newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
        return bean;
    }
}

简单解释一下:静态代码块中的代码主要完成了对我们bean.properties文件的加载流。保存在变量pros里面;getBean这个方法就是根据我们我们传入的类名来创建这个对象(其实这个类名就是我们bean.properties文件中的键)。
然后我们看一下测试类中的代码:

public class TestMethod {
    private static   EntityService entityService = null;
    public static void main(String[] args) {
        //不是单利模式
        for (int i =0;i<10;i++){
            entityService = (EntityService) BeanFactory.getBean("EntityService");
            System.out.println(entityService);
        }
    }
}

主要说明一下:这次我们创建对象不是之前那样:
EntityService entityService = new EntityServiceImpl();
而是:
entityService = (EntityService) BeanFactory.getBean(“EntityService”);
我们需要创建什么类就可以通过传入的参数类决定。
好了,我们运行一下:结果如下:
在这里插入图片描述
很明显,测试代码中,我让他重负了10次,所以他创建了10个对象,但是这10个对象的内存地址都是不一样的,这说明他不是单例对象(单例设计模式也可参考我之前的文章)。
那单例对象和多例对象有什么区别吗?当然有:单例就是程序运行期间这个对象只创建一次,是共享的。不是属于某一个人的。而多例恰好反之,他是不共享内存空间的。所以,对于工厂这种模式来说,就是创建对象的,我们没有必要每个人创建自己的对象,只需要创建一个对象,这个对象共享,我们大家都可以用这个对象来调用方法。这样,就节省了大量的不必要的空间。
那我们来看一下如何改进,我们一起来看一下:

package com.tff.demo.jdbc.bean;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class BeanFactory {
    //定义一个properties对象
    private static Properties pros;
    //定义容器来保存创建的对象
    private static Map<String,Object> map;
    //使用静态代码块为properties赋值
    static {
        try {
            pros = new Properties();
            //初始化容器
            map = new HashMap<String, Object>();
            //获取properties刘对象的
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            pros.load(in);
            //取出配置文件中所有key
            Enumeration keys = pros.keys();
            //遍历枚举
            while (keys.hasMoreElements()){
                //取出每个key
                String key = keys.nextElement().toString();
                //根据key获取value值
                String beanPath = pros.getProperty(key);
                Object value = Class.forName(beanPath).newInstance();
                map.put(key,value);
            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static Object getBean(String beanName){
        return map.get(beanName);
}

简单分析一下:就是我们创建了一个map容器,来保存我们创建的对象。map的key就是我们创建对象调用getBean()方法时传入的参数,value就是我们创建的对象那。
我们再看一下运行结果:
在这里插入图片描述
这下我们看到所有的地址都是一样的。
详细代码请见码云:
https://gitee.com/tuofafa/spring01

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值