• 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。

  • 相关阅读:
    二阶系统-阻尼系数
    信号反射的几个重要体现(过冲、下冲、振铃)及电路设计
    iPhone换电池是原装电池好还是换第三方大容量电池好?
    转:信号完整性:端接拓扑结构
    转:DDR中端接技术基本概念
    DDR 布线规则
    开始学习linux的一些疑问
    系统集成
    Oracle pipe
    工厂模式初识
  • 原文地址:https://www.cnblogs.com/wayfarer/p/1966581.html
Copyright © 2020-2023  润新知