第一章 Spring的核心(3节 下)

 
interface 来松散耦合
问题就是耦合,在这点上, KnightOfRoundTable 静态的和 HolyGrailQuest 的绑定了。绑定有两个方面,一方面,紧密耦合的代码不容易测试,不好重用、理解等,而且还会连续产生 bug 。另一方面,完全不耦合的代码什么事情都做不了。为了做有用的事,类需要知道其他的类。耦合是必须的,但需要小心谨慎。

一个典型的降低耦合的方法是通过interface来隐藏实现细节,因此,实际的类可以进行更改而不影响到客户端。例如你想创建一个Quest interface

package com.springinaction.chapter01.knight;

 

public interface Quest {

    abstract Object embark() throws QuestFailedException;

}

接下来修改HolyGrailQuest实现这个类,embark方法的返回类型和抛出的异常都不一样了。

package com.springinaction.chapter01.knight;

 

public class HolyGrailQuest implements Quest{

    public HolyGrailQuest(){}

   

    public Object embark() throws GrailFailedException{

       //Do what ever it means to embark a quest

       return new HolyGrail();    

    }

}

    KnightOfRoundTable中修改:

    private Quest quest;

    public Object embarkOnQuest() throws GrailFailedException{

       return quest.embark();     

    }

    同样的,KnightOfRoundTable也修改实现一个接口

package com.springinaction.chapter01.knight;

 

public interface Knight {

    Object embarkOnQuest() throws GrailFailedException;

}

    把实现隐藏在接口后面是很好的一步,但很多开发者迷惑,如何得到Quest的实例?例如,看看上面的KnightOfRoundTable修改。

    KnightOfRoundTable通过Quest接口得到了一个quest。但还是得到了个具体的Quest类型。这没有比以前好多少,KnightOfRoundTable还是跟HolyGrailQuest粘合在一起。

 

    给予和拿来

    这时候你会问,是否一个knight要负责得到一个quest,或是否一个knight得到一个embark(上船,为什么?)的quest??

       看下面的修改:(set方法)

package com.springinaction.chapter01.knight;

 

public class KnightOfRoundTable implements Knight{

    private String name;

    private Quest quest;

    public KnightOfRoundTable(String name) {

       this.name = name;

    }

 

    public Object embarkOnQuest() throws GrailFailedException{

       return quest.embark();     

    }

   

    public void setQuest(Quest quest){

       this.quest = quest;

    }  

}

 

    现在knight被赋予了一个quest,而不是去得到一个questKnightOfRoundTable不负责去获取quest,且由于它只通过interface知道quest,你可以随意给一个quest的类型,不一定是HolyGrailQuest。所以在测试时候,你可以赋值一个假的questknight

 

 

 

       这就是DI的含义了:合作关系的对象间的关联,被从对象本身中分离出来了。

 

       把一个quest插入到knight

       现在你可以随意把一个quest放入到KnightOfRoundTable中,但你如何制定一个特定的quest放入其中呢?

    在程序组件间创建联系好象是电工配线一样。在Spring中,有很多方法把对象搭配在一起,但最普通的方法是通过xml文件。下面就是一个简单的Spring配置文件knight.xml,向KnightOfRoundTable注入了一个questHolyGrailQuest.
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

    <bean id="quest" class="com.springinaction.chapter01.knight.HolyGrailQuest"/>

 

    <bean id="knight" class="com.springinaction.chapter01.knight.KnightOfRoundTable">

       <constructor-arg value="Bedivere"></constructor-arg>

       <property name="quest" ref="quest"></property>

    </bean>

</beans>

这就是个简单的方法把类搭配在一起。现在不要在意细节。第二章会解释更多。

       现在我们声明了knightquest之间的关系,现在可以运行了。

看看运行一下

       Spring程序中,一个BeanFactory负责载入bean的定义并且把它们绑定在一起的。因为定义写在xml文件中,所以XmlBeanFactory需要用在这里。下面就是这个例子:

package com.springinaction.chapter01.knight;

 

import org.springframework.beans.factory.BeanFactory;

import org.springframework.beans.factory.xml.XmlBeanFactory;

import org.springframework.core.io.FileSystemResource;

 

public class KnightApp {

    public static void main(String[] args){

       BeanFactory factory = new XmlBeanFactory(new FileSystemResource("knight.xml"));

       Knight knight = (Knight)factory.getBean("knight");

       knight.embarkOnQuest();

    }

}

Main中第一行是读取xml配置文件,第二行是从factory中得到knight对象的实例,第三行是向knight发送quest(原句没搞懂)。

 

       一旦程序得到了knight的对象的实例,它就调用embarkOnQuest方法去抛弃了knight的冒险。注意,程序不知道questknight做什么,它只知道通过xml文件来把相应的quest类型注入到knight中去。

      1.3.3 企业级程序中应用DI

假设我们的系统是在线购物系统。系统中有一个订单系统组件,控制订单的一切活动。下图示例了几种web层访问这个组件的方式。

       直接初始化后访问是最简单的(第一个方法)。除了直接把web层同服务组件绑定在一起,它还导致浪费了创建的对象,当组件共享时,仅仅一个无状态的单例就够了。

       第二个方法是ejb2.x的。

       第三个方法是ejb3.x的。

       第四个方法是不论是否使用ejb,你可以选择通过一个什么东西隐藏调用细节。等等等等。

       这些方法的主要问题是web层对于如何得到它自己的依赖关系关心的太多了。它知道太多的系统实现了。

       如果了解太多的系统实现就需要大量的耦合代码来实现,那么就意味着知道越少的系统实现,代码就越松耦合。看上图,就是Checkout组件被注入了OrderService组件,而不是去得到它。下面的代码就是如何实现这种DI

private OrderService orderService;

public void doRequest(HttpServletRequest request) {

       Order order = createOrder(request);

       orderService.createOrder(order);

}

public void setOrderService(OrderService orderService){

       this.orderService = orderService;

}

现在看代码,orderService是通过setOrderService方法注入到类中的。Web组件不用知道也关心orderService是从哪里来的。它可以通过Spring或者是直接调用setOrderService方法来注入进去。它也不用管OrderService是如何实现的-它只需要知道它通过的是OrderService接口。通过DI,你的程序不用为了得到各种对象来繁忙,只需要关注自己的任务了,需要的时候,依赖关系自然可以用到了。

DI是松散耦合的福音,操。让你的程序可以尽可能的短。不过,现在你只知道了Spring容器和DI的表面的东西,第二和三章,你将会知道更多的方法通过Spring容器来联系对象。

DISpring通过POJO类进行松耦合的唯一技术。AOP提供了另外一个能力来松耦合,它提供了把横向的程序级别的功能从对象里分离的技术,就像安全和事务。下节看一下aop的基础。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值