重构,解耦,工厂,泛型,类加载
重构
1.引入接口(面向接口编程)
◇ 接口本质上是一套规范,从接口的定义方面来说, 接口其实就是类和类之间的一种协定,一种约束.
◇ 在软件开发中,某一层可能会被修改或者替换,在完全替换某一层时,要保证新的层和被替换的层,虽然具体实现不同,但是必须得实现相同的功能。这样才能尽可能的不影响到其他层。
2.耦合和解耦
耦合:
◇ 所谓的耦合指的是在软件开发中,在层与层之间产生了某种紧密的关系,这种关系可能会导致,在我们修改或者是替换某一层时会影响其他的层,像这种情况我们就称之为层与层之间产生了耦合。
◇ 由于耦合(层与层之间的紧密关系)可能会导致我们在修改某一层时影响到其他的层,而这严重违反了我们对软件进行分层的最初设想 -- 软件各层之间应该相互独立、互不干扰,在修改或者是替换某一层时,应该做到不影响其他层。
解耦:
◇ 去除耦合的过程,称为解耦。
◇ 首先应该在开发程序时保证不要胡乱传递只属于某一层特有的对象或跨层调用只属于某一层特有的方法,从而避免产生不必要的耦合。但是无论如何小心,层与层之间最终还是会有关系,对于这种无法避免的耦合,我们应该想办法管理起来。
解耦: 接口+配置文件+工厂
-----------------------------------------------------------------------
这里需要提一下泛型:
泛型
JDK5开始提供的新特性,表示不确定的类型
1.集合泛型:
List list0 = new ArrayList();
List<String> list1 = new ArrayList<String>();
List<String> list2 = new ArrayList();
List list3 = new ArrayList<String>();
////List<Object> list4 = new ArrayList<String>(); //不支持
////List<String> list5 = new ArrayList<Object>(); //不支持
总结: 可以两边都没有,可以一边有一边没有,可以两边都有,但是一旦有了必须一模一样,泛型没有继承关系!
2.自定义泛型:
(1)方法上的泛型
◇ 定义在方法上的泛型作用范围是当前方法内部。
◇ 泛型需要先定义再使用,方法上的泛型需要定义在方法的返回值之前,通常泛型的名称会定义为一个大写的字母(但并不是强制要求),也可以一次定义多个泛型,中间用逗号分隔。
◇ 方法上的泛型的具体类型不需要明确的指定,会在方法调用时自动的推断出具体类型。
(2)类上的泛型
◇ 定义在类上的泛型作用范围是当前类的内部。
◇ 泛型需要先定义再使用,类上泛型需要定义在类名之后,通常泛型的名称会定义为一个大写的字母(但并不是强制要求),也可以一次定义多个泛型,中间用逗号分隔
◇ 类上定义的泛型需要在创建对象/引用时明确的指定泛型的具体类型
◇ 类上定义的泛型无法在静态方法中使用,如果想在静态方法中使用泛型需要自己定义自己使用
-----------------------------------------------------------------------
**案例: 使用 接口+配置文件+工厂 进行项目中层与层之间的解耦
准备,java结构
准备一个外部配置文件conf.properties 内容为:
1 UserDao=cn.tedu.dao.UserDaoImpl 2 UserService=cn.tedu.service.UserServiceImpl
:注意这里的路径名 是连包一起的。
准备工具类PropUtils,用来读取和类加载
1 package cn.tedu.utils; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.util.Properties; 6 7 public class PropUtils { 8 9 private static Properties prop=new Properties(); 10 public PropUtils() { 11 } 12 static { 13 try { 14 //先使用类加载器的方法加载外部配置文件的相对路径,存为字符串, 15 String path = PropUtils.class.getClassLoader().getResource("conf.properties").getPath(); 16 // io.load()方法从输入流中读取属性列表(键和元素对)。 17 prop.load(new FileInputStream(new File(path))); 18 } catch (Exception e) { 19 e.printStackTrace(); 20 throw new RuntimeException(e); 21 } 22 } 23 24 /**根据key返回对应的value 25 * @param key 配置文件中的key 26 * @return value 配置文件中的value 27 */ 28 public static String getValue(String key){ 29 return prop.getProperty(key); 30 } 31 }
准备工厂BasicFactory,顾名思义,是用来批量生产用的,就是我 给个模型,你就开始给我 造:
1 package cn.tedu.dao.factory; 2 3 import cn.tedu.utils.PropUtils; 4 5 public class BasicFactory { 6 private static BasicFactory factory = new BasicFactory(); 7 //单例模式 8 private BasicFactory() {} 9 //返回工厂实例 10 public static BasicFactory getFactory(){ 11 return factory; 12 } 13 //根据配置文件中的配置项生产Userdao实例 14 public <T> T getInstance(Class<T> clazz){ 15 try { 16 //getSimpleName()获取你输入的 类的底层类名 17 //输入接口如UserDao,从配置文件中查找这个key对应的value实现类,如cn.tedu.dao.UserDaoImpl(注意这里带了包名的) 18 String className = PropUtils.getValue(clazz.getSimpleName()); 19 //使用类的全名,返回具有指定名的class对象 20 Class clz = Class.forName(className); 21 //newInstance创建此 Class 对象所表示的类的一个新实例,意思就是成功创建了这个类, 22 return (T)clz.newInstance(); 23 } catch (Exception e) { 24 e.printStackTrace(); 25 throw new RuntimeException(e); 26 } 27 } 28 29 }
在UserServiceImpl中引用这个工厂,动态加载dao的实现类,以后如果不是service层出问题,这个代码就不用看了,改一个配置文件就搞定,重构的好处就在这,
1 package cn.tedu.service; 2 3 import cn.tedu.dao.UserDao; 4 import cn.tedu.dao.UserDaoImpl; 5 import cn.tedu.dao.factory.BasicFactory; 6 import cn.tedu.domain.User; 7 import cn.tedu.exception.MsgException; 8 9 public class UserServiceImpl implements UserService { 10 //通过类加载的方式动态加载了dao层的接口,实现了重构 11 private UserDao dao = BasicFactory.getFactory().getInstance(UserDao.class); 12 //上面那一段相当于UserDao daa= new UserDaoImpl(),但是缺少了灵活性,因为我们改一个配置文件就可以修改实现类了,
-----------------------------------------------------------------------
需要了解的知识点:类加载器创建对象,加载外部资源,对io流的掌握,泛型,解耦的目的,重构的优点。
最后说一下,有框架的话,跟本不需要这么麻烦啊,,,,封装严重的时代。