• 开发,从需求出发 · 之四 春天在这里


    首先,我要大字标语表达立场:

    你所使用的framework & non-core features,就跟女人穿在身上的衣服一样,越少越好!


    扯淡完成,说正经的。


    让我们继续盯着花姐——啊,不——是 BeanFactory看。

    	public static SearchService getSearchService() {
    		if(MOCK) {
    			return new SearchServiceMock();
    		} else {
    			LuceneDAO luceneDAO = getLuceneDAO();
    			MysqlDAO mysqlDAO = getMysqlDAO();
    			
    			return new SearchServiceInRealBiz(luceneDAO, mysqlDAO);
    		}
    	}

    有木有点儿标题所说的“春天在这里”的意思了?

    纳尼?没看出来?

    好吧,或许你习惯了spring的xml装配方式,所以认为把两者关联起来看实在须要超常的想象力,那么,

    我把BeanFactory改头换面。简单的实现一个基于文本字符串的装配,咋们再来看看效果:


    package cn.com.sitefromscrath;
    
    import java.lang.reflect.Constructor;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class BeanFactory {
    	
    	private static Map<String, String> appBean = new HashMap<String, String>();
    	private static Map<String, String[]> appRef = new HashMap<String, String[]>();
    	
    	static {
    		appBean.put("luceneDAO", "cn.com.sitefromscrath.dao.LuceneDAOMock");
    		appBean.put("mysqlDAO", "cn.com.sitefromscrath.dao.MysqlDAOMock");
    		appBean.put("searchService", "cn.com.sitefromscrath.service.SearchServiceInRealBiz");
    		
    		appRef.put("searchService", new String[]{"luceneDAO", "mysqlDAO"});
    	}
    		
    	public static Object getBean(String id) {
    		
    		try {
    		
    			String className = appBean.get(id);		
    			Class clazz = Class.forName(className);
    			Constructor constructor;
    			
    			String[] ref = appRef.get(id);
    			
    			if(ref == null || ref.length == 0) {			
    				constructor = clazz.getConstructor();
    				return (Object)constructor.newInstance();
    			}
    			
    			Class[] parameterTypes = new Class[ref.length];
    			Object[] initargs = new Object[ref.length];
    			
    			for(int i = 0; i < ref.length; i++) {
    				String r = ref[i];
    				
    				String rclassName = appBean.get(r);
    				parameterTypes[i] = Class.forName(rclassName).getInterfaces()[0]; //这里我偷懒了:)
    				initargs[i] = getBean(r);
    			}
    			
    			constructor = clazz.getConstructor(parameterTypes);
    			
    			return (Object)constructor.newInstance(initargs);
    			
    		} catch (Exception e) {
    			e.printStackTrace();
    			return null;
    		}		
    	}
    	
    	public static void main(String ... arg) {
    		
    		LuceneDAO luceneDAO = (LuceneDAO) getBean("luceneDAO");
    		int[] vals = luceneDAO.findDocIDs("test");
    		for(int v : vals) {
    			System.out.println(v);
    		}
    		
    		String keywords = "test";
    		SearchService searchService = (SearchService)getBean("searchService");	
    		List results = searchService.search(keywords);
    		for(int i = 0; i < results.size(); i++) {
    			Result result = (Result) results.get(i);
    			System.out.print("[" + result.title + "]");
    			System.out.println(result.content);
    		}
    	}
    
    }
    

    执行结果输出:

    1
    2
    3
    4
    [result 1]something..................
    [result 2]something..................
    [result 3]something..................
    [result 4]something..................
    

    结果正确!


    再看看我们对类的装配:

    	private static Map<String, String> appBean = new HashMap<String, String>();
    	private static Map<String, String[]> appRef = new HashMap<String, String[]>();
    	
    	static {
    		appBean.put("luceneDAO", "cn.com.sitefromscrath.dao.LuceneDAOMock");
    		appBean.put("mysqlDAO", "cn.com.sitefromscrath.dao.MysqlDAOMock");
    		appBean.put("searchService", "cn.com.sitefromscrath.service.SearchServiceInRealBiz");
    		
    		appRef.put("searchService", new String[]{"luceneDAO", "mysqlDAO"});
    	}

    请比較和spring.xml的差别:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans >
    
    	<bean id="luceneDAO" class="cn.com.sitefromscrath.dao.LuceneDAOMock" />
    	
    	<bean id="mysqlDAO" class="cn.com.sitefromscrath.dao.MysqlDAOMock" /> 
    	
    	<bean id="searchService" class="cn.com.sitefromscrath.service.SearchServiceInRealBiz">
    		<constructor-arg index="1" ref="luceneDAO" />
    		<constructor-arg index="2" ref="mysqlDAO" />
    	</bean>  
    
    </beans>

    好吧,没什么根本上的差别,我们相同可以解析xml,得到我们自己实现的BeanFactory所须要的一切要素。

    好了。经过我们将代码从零開始,重复重构,到一个比較“经典”的模式。我们找到了“春天”。这也是spring的core features。


    或许是对spring看待的角度不同,我发现我对spring的依赖注入和控制反转的用途和不少人并不一致。下一章。我打算结合一些开发和測试技巧,论述一下我的看法。

    只是。如今,是吐槽spring的时间:

    我在我的博文《thinking in asp》appendix A - 吐槽JAVA 中以前说到:

    还有新版本号的spring,怎么说呢,它把java的annotation机制玩儿到了“奇技淫巧”的程度。

    因为那个系列主要是讲视频编转码技术。因此。对spring仅仅是一带而过,如今总算找到机会了:)


    Long long ago, in the old good times, spring还是依赖xml做装配的小姑娘,青春漂亮,带着点儿书卷学生气。

    可是如今看,这丫头已经涂脂抹粉,跻身上流社会。出入商务场合。尽管不讨厌,可是不让人认为亲近了 -_-b


    看传统的 spring.xml,就如同看电路板设计图,一个个元器件清清楚楚,什么型号,怎么走线。怎样装配。尽管没有图形化。可是一目了然,基本上能够做到不用查看源代码。就能把整个系统的逻辑关系梳理的八九不离十。

    而自从有了annotation,比方autowire。xml不重要了,你无法再从一个基于spring的大型项目中迅速找到一份天然的“提纲”。

    ——spring開始不顾一切的媚俗。——或许俺是个受虐狂,只是“ruby way”和“傲娇”的python显然更对我的胃口——设计原则是不能够松口的:)

    我以前问spring的一些使用者,怎样找到通过annotation装配尤其是自己主动装配的类,或者是某个隐藏在spring-mvc框架下annotation声明的URL,

    给我的答案例如以下:

    首先:


    然后:


    好吧。我得到的结论是:与其如此,不如不用:)

    当然,假设你说。通过一些词法/语法解析器,也能够得到基于annotation的“提纲”。比方用 lex+yacc 亦或 antlr 打造一个工具。

    本座的答复是:老子被编译原理搞的几宿没睡了,小心一指头把你戳出去三公里远去~~~!

    发火歧视大笑


    接着。说说spring框架下怎样强測试的问题:

    时刻记住。每个模块。甚至最“小”的方法,在实现它之前,都必需要先设计怎样測试它。


    由于我们如今讨论的是一个web项目,我想说一个非常多开发人员会使用的方法:

    ContextListener --> WebApplication --> BeanFactory

    因为spring的“人性化”。这个步骤甚至不须要你写代码。

    如今的问题是,假设我们的项目採取这种方式。你怎样做dao 或者 service层的单元測试?

    比方前面提到的 SearchService,他须要通过spring装配LuceneDAO 和 MysqlDAO,可是问题出现了:

    你假设想让SearchService的方法跑起来,你必须启动TOMCAT等web容器!

    这样的紧耦合的程度简直是令人发指 :) 让一切单元測试成为不可能~~~~

    而我,在前面的章节重复强调 “不须要启动tomcat。不须要查看网页的实际效果,也能保证系统模块的正确” 就是这个意思:)


    当然。spring提供了ApplicationContext,在web容器中相同能用——这也是我使用的方式——可是,我想说的是。spring的WebApplication模式错误的诱导了开发人员。引发了大量的 bad smell。


    在玩儿了一把怎样从最简单的需求出发。重复重构到模式/框架之后,下一章我会再次绕回去,spring已经说得够多的啦:)

    to be continued.....

  • 相关阅读:
    (C#) 设定时间格式
    (WPF) MVVM: 动态添加控件及绑定。
    (WPF) MVVM: DataGrid Binding
    (WPF) MVVM: ComboBox Binding, XML 序列化
    (C#) 判断相等?
    ASP.NET MVC过滤器中权限过滤器ValidateAntiForgeryToken的用法(Post-Only)
    根据2个经纬度点,计算这2个经纬度点之间的距离
    ASP.NET导出Excel(利用NPOI和EPPlus库,无需安装Office)
    nopcommerce 二次开发
    SQL效率低下原因主要有
  • 原文地址:https://www.cnblogs.com/yfceshi/p/6921195.html
Copyright © 2020-2023  润新知