Message Chains与Fluent Interface

Martin Fowler在其名著《重构》一书,提到了Message Chains坏味道。这种坏味道的表现特征是当调用者需要执行某个功能时,需要调用连续的多个方法,才能最终达成目的。这种调用方法的消息传递就像链条一样,因此Fowler将其命名为Message Chains。

 

这种坏味道暴露了过多实现细节。它将获得最终结果的整个过程暴露无遗。它不厌其烦地陈述着:首先该获得什么对象,然后再调用返回结果的什么方法获得中间对象,接着,再调用中间对象的对应方法去获取结果,如此传递下去,直到抵达我们的目的地。服务的提供者弄错了调用者的角色。调用者不是游客,需要导游展示沿途的风景;调用者更像是餐厅的食客,只在乎菜品的色香味,却并不关心这道菜品是怎样做出来的,那是厨师的职责。

 

导致Message Chains坏味道的原因是设计者没有很好地理解封装的概念,他希望服务更周到一些,可这种周到带来的结果是自讨没趣。

 

在设计中,有另外一种常见的模式与之相似,却能取得不同的效果,那就是Fluent Interface模式,它通常被翻译为“连贯接口”。Fluent Interface主要应用在DSL(Domain Specific Language)中。Martin Fowler将其定义为“能够为一系列方法调用中转或维护指令上下文的行为”。Fluent Interface中定义的方法常常是返回该类型自身的实例。Fluent Interface模式在很多动态语言如php、ruby中得到大量运用。C#的LINQ也使用了该模式。ThoughtWorks的意见领袖Neal Ford在其系列文章Evolutionary architecture and emergent design中,介绍了该模式的运用。文中提供了如下代码,以演示Java中如何实现Fluent Interface:

public class Appointment {
    private String _name;
    private String _location;
    private Calendar _startTime;
    private Calendar _endTime;

    public Appointment(String name) {
        this._name = name;
    }

    public Appointment() {
    }

    public Appointment name(String name) {
        _name = name;
        return this;
    }
    public Appointment at(String location) {
        _location = location;
        return this;
    }

    public Appointment at(Calendar startTime) {
        _startTime = startTime;
        return this;
    }

    public Appointment from(Calendar startTime) {
        _startTime = startTime;
        return this;
    }

    public Appointment to(Calendar endTime) {
        _endTime = endTime;
        return this;
    }

    public String toString() {
        return "Appointment:"+ _name +
                ((_location != null && _location.length() > 0) ? 
                    ", location:" + _location : "") +
                ", Start time:" + _startTime.get(Calendar.HOUR_OF_DAY) +
                (_endTime != null? ", End time: " + 
                _endTime.get(Calendar.HOUR_OF_DAY) : "");
    }
}

 

以下是Fluent Interface的使用:

AppointmentCalendarChained calendar =
            new AppointmentCalendarChained();
calendar.add("dentist").
         from(fourPM).
         to(fivePM).
         at("123 main street");

其中,AppointmentCalendarChained类的add()方法返回的是实现了Fluent Interface模式的Appointment对象。

 

采用Fluent Interface模式的代码,具有自我阐述领域逻辑的能力,它不同于Message Chains,因为它的意图并不是要展现消息传递的过程。Fluent Interface中每个方法都是调用者需要关注的。而且,Fluent Interface方法可以自由组合,并不利于封装。由于调用方式是完全一致的,只要有了合理的命名,代码的连贯性就如行云流水。领域驱动设计的布道者Eric Evans曾提及,他所接触到的Fluent Interface都是用来装配Value Object。Value Object没有具有业务领域意义的实体与之对应,它们可以被随心创建随心抛掉。本例中的Appointment正是这样的Value Object。有关Value Object的知识,请参考Eric Evans的经典名著Domain Driven Design-Tackling Complexsity in the Heart of Software。

posted @ 2011-02-27 23:08  张逸  阅读(3875)  评论(13编辑  收藏  举报