引入间接隔离变化(一)

简介:

David Wheeler有一句名言:“计算机科学中的大多数问题都可以通过增加一层间接性来解决。”间接代表着迂回。世间没有哪一条道路是完全笔直的。蜿蜒曲折的道路并非出于美的灵感,不过是因为我们需要绕开路途中的障碍罢了。

我们在设计中遇到的最大障碍,无疑就是变化。若能御变化于实现之外,软件开发就会变得美好。

应对变化的要诀是隔离。设计者需要界定对象的不变部分与可变部分,然后将可变部分隐藏起来,即使发生了变化,也不会影响到外部。这就是封装的含义。正如地壳核心的变化如此的狂暴与迅捷,但对于地面上生活的人类而言,几乎微不可察。然而,一旦地壳的变化冲出地表,就会酿成天大的灾难。变化对软件系统造成的灾难,并不亚于地震或者火山。封装为对象内部的实现设定了一层隔离带,将复杂变化的业务逻辑或者算法策略隐藏在对象之内。只要保证对象的接口不发生变化,调用者与对象内部的实现就可以单独演化了。

当我们发现一个对象需要依赖另一个不稳定的对象,同时,还需要执行复杂的交互逻辑时,就可以考虑引入一个新的对象来封装这些逻辑,从而解除二者之间的耦合,隔离变化。Spring MVC中的ModelAndView对象扮演的正是这一角色。根据MVC模式,控制器需要将模型对象所持有的数据以及数据的变化呈现到视图中。它通过寻找正确的视图对象,完成页面的展现。控制器承担了这一职责,就意味它必须依赖于视图对象。例如这样的代码:

public class CustomerController implements Controller { 
    @Override 
    public View handleRequest( 
            HttpServletRequst request, 
            HttpServletResponse response) throws Exception { 
        Map model = new HashMap(); 
        model.put(“customers”, getCustomerList()); 
        return new InternalResourceView(”/WEB-INF/jsp/customerList.jsp”); 
    } 
}

View具体对象的创建,使得CustomerController与InternalResourceView紧紧地绑定起来,失去了灵活性,导致我们无法自由改变View的实现。作为一个灵活的MVC框架,显然很难容忍这二者之间的强耦合。要打破这种耦合关系,就需要封装寻找以及创建视图的职责,并将这一职责放到合适的对象中。这正是引入ModelAndView类的缘由。Controller放心地将所有与View相关的职责转移给ModelAndView,而它只需要悠闲地传递一个视图名称即可。

public class CustomerController implements Controller { 
    @Override 
    public ModelAndView handleRequest( 
            HttpServletRequst request, 
            HttpServletResponse response) throws Exception { 
        Map model = new HashMap(); 
        model.put(“customers”, getCustomerList()); 
        return ModelAndView(”customerList”, model); 
    } 
}

通过字符串类型的名称常量去寻找合适的视图,而不是具体的View对象,就使得Controller冲破了View类型的约束,变得自在而开放。因为封装的作用,Controller对象变得无知,然而,“无知者无畏”,它也不用害怕视图呈现所发生的变化了。image隔离变化的另一条途径是寻觅对象的共性,对这些共性进行抽象。我们不必考虑对象实现细节的不同之处,只需要把握对象的共同特征,即可完成接口的定义。接口可以看做是对象的角色。Rebecca认为:“客户访问接口比访问具体类要灵活得多,它们不需要知道具体实现,而只需明了接口中声明的公共角色即可。”【注:Rebecca Wirfs-Brock《对象设计:角色、责任和协作》】角色代表一种功能或职责的扮演,它并非演员本身,只是形象化地以某种形态或语言来表现角色的喜怒哀乐而已。例如,我们需要在项目中指定规则以限定渲染的格式。这个规则可以是数据区间,只要数据在这个区间范围之内,就应该设置为对应的格式;也可以是某种约束条件,当条件满足时,以相应的格式渲染。从实现细节来看,区间与约束是迥然不同的两种实现;可是从抽象的角度看,它们无疑扮演的都是同一种角色,那就是匹配器。只要规则匹配,就应该获得正确的格式。imagepublic interface Matcher { 
    public boolean matches(Object value); 
}

public class Range implements Matcher{ 
    private double min; 
    private double max; 
    public Range(double min, double max) { 
        this.min = min; 
        this.max = max; 
    } 
    private boolean in(double data) { 
        //判断data是否在此区间 
    }

    public boolean matches(Object value) { 
        try { 
            return in((double)value); 
        }catch (InvalidCastException) { 
            return false; 
        } 
    } 
}

public class Constraint implements Matcher { 
    private String expected; 
    private boolean ignoreCase; 
    public Constraint(String expected) { 
        this.expected = expected; 
        ignoreCase = true; 
    }

    public Constraint(String expected, boolean ignoreCase) { 
    } 
    public boolean matches(Object value) { 
        if (ignoreCase) { 
            return expected.equalsIgnoreCase(value.toString()); 
        } else { 
            return expected.equals(value.toString()); 
        } 
    } 
}

Matcher接口抽象了区间与约束的共性特征,使得二者在规则中能够友好相处: 
public class Rule { 
    public Rule(Matcher matcher, Format format){} 
    public Matcher getMatcher(){} 
    public void setMatcher(Matcher matcher){} 
    public Format getFormat(){} 
    public void setFormat(Format format){} 
}

如果需要更多的匹配器,只要实现Matcher接口,就可以放入Rule中,作为格式规则的一部分。这种包容变化的能力,正是抽象能够提供的。








本文转自wayfarer51CTO博客,原文链接:http://blog.51cto.com/wayfarer/478189,如需转载请自行联系原作者

相关文章
|
4月前
|
缓存 前端开发 数据格式
构建前端防腐策略问题之保证组件层的代码不受到接口版本变化的问题如何解决
构建前端防腐策略问题之保证组件层的代码不受到接口版本变化的问题如何解决
|
4月前
|
监控 安全 网络安全
内部隔离
【8月更文挑战第18天】
50 5
|
5月前
软件复用问题之在复用组件降低成本和复用组件引入依赖之间取得平衡,如何解决
软件复用问题之在复用组件降低成本和复用组件引入依赖之间取得平衡,如何解决
|
5月前
软件复用问题之复用性风险是如何定义的
软件复用问题之复用性风险是如何定义的
|
设计模式 Java
JAVA设计模式7:适配者模式,彻底解决两不兼容接口之间的问题
JAVA设计模式7:适配者模式,彻底解决两不兼容接口之间的问题
164 0
|
机器学习/深度学习 分布式计算 自动驾驶
按需求构建架构才是正确之举,过度工程只会“劳民伤财”
按需求构建架构才是正确之举,过度工程只会“劳民伤财”
|
存储 小程序 JavaScript
再也不用担心组件跨层级的数据共享和方法驱动了
再也不用担心组件跨层级的数据共享和方法驱动了
128 0
|
安全 Java API
了解程序运行逻辑的必要性及应用和硬件的关系
了解程序运行逻辑的必要性及应用和硬件的关系
112 0