zoukankan      html  css  js  c++  java
  • 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。

  • 相关阅读:
    《House of Cards》观后感
    几个常见的壳的脱壳
    【转】Arp的攻防实战
    Back Track 5 之 漏洞攻击 && 密码攻击 && Windows下渗透工具
    Back Track 5 之 Web踩点 && 网络漏洞
    Back Track 5 之 网络踩点(二)
    Back Track 5 之 网络踩点
    51单片机总线与非总线的程序对比
    关于PHP写的投票网站之刷票终结版
    关于PowerShell中的命令的别名
  • 原文地址:https://www.cnblogs.com/wayfarer/p/1966581.html
Copyright © 2011-2022 走看看