**流式接口**（fluent interface）是软件工程中面向对象API的一种实现方式，以提供更为可读的源代码。最早由Eric Evans（英语：）与Martin Fowler于2005年提出。

通常采取方法瀑布调用 (具体说是方法链式调用)来转发一系列对象方法调用的上下文 。这个上下文（context）通常是指：

* 通过被调方法的返回值定义
* 自引用，新的上下文等于老的上下文。
* 返回一个空的上下文来终止。

C++的iostream流式调用就是一个典型的例子。Smalltalk在1970年代就实现了方法瀑布调用。

例子
--

### JavaScript

用于数据库查询的jQuery，例如https://github.com/Medium/dynamite （页面存档备份，存于） :

```
// getting an item from a table
client.getItem('user-table')
 .setHashKey('userId', 'userA')
 .setRangeKey('column', '@')
 .execute()
 .then(function(data) {
 // data.result: the resulting object
 })

```

JavaScript使用原型继承与`this`.

```
// example from http://schier.co/post/method-chaining-in-javascript
// define the class
var Kitten = function() {
 this.name = 'Garfield';
 this.color = 'brown';
 this.gender = 'male';
};

Kitten.prototype.setName = function(name) {
 this.name = name;
 return this;
};

Kitten.prototype.setColor = function(color) {
 this.color = color;
 return this;
};

Kitten.prototype.setGender = function(gender) {
 this.gender = gender;
 return this;
};

Kitten.prototype.save = function() {
 console.log(
 'saving ' + this.name + ', the ' +
 this.color + ' ' + this.gender + ' kitten...'
 );

 // save to database here...

 return this;
};

// use it
new Kitten()
 .setName('Bob')
 .setColor('black')
 .setGender('male')
 .save();

```

### Java

jOOQ库模拟了SQL

```
Author author = AUTHOR.as("author");
create.selectFrom(author)
 .where(exists(selectOne()
 .from(BOOK)
 .where(BOOK.STATUS.eq(BOOK\_STATUS.SOLD\_OUT))
 .and(BOOK.AUTHOR\_ID.eq(author.ID))));

```

### C#

C#在LINQ中大量使用 *standard query operators*与扩展方法。

```
var translations = new Dictionary<string, string>
 {
 {"cat", "chat"},
 {"dog", "chien"},
 {"fish", "poisson"},
 {"bird", "oiseau"}
 };

// Find translations for English words containing the letter "a",
// sorted by length and displayed in uppercase
IEnumerable<string> query = translations
 .Where (t => t.Key.Contains("a"))
 .OrderBy (t => t.Value.Length)
 .Select (t => t.Value.ToUpper());

// The same query constructed progressively:
var filtered = translations.Where (t => t.Key.Contains("a"));
var sorted = filtered.OrderBy (t => t.Value.Length);
var finalQuery = sorted.Select (t => t.Value.ToUpper());

```

流式接口可用于一系列方法，他们运行在同一对象上。

```
// Defines the data context
class Context
{
 public string FirstName { get; set; }
 public string LastName { get; set; }
 public string Sex { get; set; }
 public string Address { get; set; }
}

class Customer
{
 private Context \_context = new Context(); // Initializes the context

 // set the value for properties
 public Customer FirstName(string firstName)
 {
 \_context.FirstName = firstName;
 return this;
 }

 public Customer LastName(string lastName)
 {
 \_context.LastName = lastName;
 return this;
 }

 public Customer Sex(string sex)
 {
 \_context.Sex = sex;
 return this;
 }

 public Customer Address(string address)
 {
 \_context.Address = address;
 return this;
 }

 // Prints the data to console
 public void Print()
 {
 Console.WriteLine("First name: {0} \nLast name: {1} \nSex: {2} \nAddress: {3}", \_context.FirstName, \_context.LastName, \_context.Sex, \_context.Address);
 }
}

class Program
{
 static void Main(string[] args)
 {
 // Object creation
 Customer c1 = new Customer();
 // Using the method chaining to assign & print data with a single line
 c1.FirstName("vinod").LastName("srivastav").Sex("male").Address("bangalore").Print();
 }
}

```

### C++

下述代码对比了传统的风格与流式接口的实现风格：

```
 // Basic definition
 class GlutApp {
 private:
 int w\_, h\_, x\_, y\_, argc\_, display\_mode\_;
 char \*\*argv\_;
 char \*title\_;
 public:
 GlutApp(int argc, char\*\* argv) {
 argc\_ = argc;
 argv\_ = argv;
 }
 void setDisplayMode(int mode) {
 display\_mode\_ = mode;
 }
 int getDisplayMode() {
 return display\_mode\_;
 }
 void setWindowSize(int w, int h) {
 w\_ = w;
 h\_ = h;
 }
 void setWindowPosition(int x, int y) {
 x\_ = x;
 y\_ = y;
 }
 void setTitle(const char \*title) {
 title\_ = title;
 }
 void create(){;}
 };
 // Basic usage
 int main(int argc, char \*\*argv) {
 GlutApp app(argc, argv);
 app.setDisplayMode(GLUT\_DOUBLE|GLUT\_RGBA|GLUT\_ALPHA|GLUT\_DEPTH); // Set framebuffer params
 app.setWindowSize(500, 500); // Set window params
 app.setWindowPosition(200, 200);
 app.setTitle("My OpenGL/GLUT App");
 app.create();
 }

 // Fluent wrapper
 class FluentGlutApp : private GlutApp {
 public:
 FluentGlutApp(int argc, char \*\*argv) : GlutApp(argc, argv) {} // Inherit parent constructor
 FluentGlutApp &withDoubleBuffer() {
 setDisplayMode(getDisplayMode() | GLUT\_DOUBLE);
 return \*this;
 }
 FluentGlutApp &withRGBA() {
 setDisplayMode(getDisplayMode() | GLUT\_RGBA);
 return \*this;
 }
 FluentGlutApp &withAlpha() {
 setDisplayMode(getDisplayMode() | GLUT\_ALPHA);
 return \*this;
 }
 FluentGlutApp &withDepth() {
 setDisplayMode(getDisplayMode() | GLUT\_DEPTH);
 return \*this;
 }
 FluentGlutApp &across(int w, int h) {
 setWindowSize(w, h);
 return \*this;
 }
 FluentGlutApp &at(int x, int y) {
 setWindowPosition(x, y);
 return \*this;
 }
 FluentGlutApp &named(const char \*title) {
 setTitle(title);
 return \*this;
 }
 // It doesn't make sense to chain after create(), so don't return \*this
 void create() {
 GlutApp::create();
 }
 };
 // Fluent usage
 int main(int argc, char \*\*argv) {
 FluentGlutApp(argc, argv)
 .withDoubleBuffer().withRGBA().withAlpha().withDepth()
 .at(200, 200).across(500, 500)
 .named("My OpenGL/GLUT App")
 .create();
 }

```

### Ruby

Ruby语言允许修改核心类，这使得流式接口成为原生易于实现。 

```
# Add methods to String class
class String
 def prefix(raw)
 "#{raw} #{self}"
 end
 def suffix(raw)
 "#{self} #{raw}"
 end
 def indent(raw)
 raw = "" \* raw if raw.kind\_of? Fixnum
 prefix(raw)
 end
end
 
# Fluent interface
message = "there"
puts message.prefix("hello")
 .suffix("world")
 .indent(8)

```

### Scala

Scala supports a fluent syntax for both method calls and class mixins, using **trait**s and the **with** keyword. For example:

```
class Color { def rgb(): Tuple3[Decimal] }
object Black extends Color { override def rgb(): Tuple3[Decimal] = ("0", "0", "0"); }

trait GUIWindow {
 // Rendering methods that return this for fluent drawing
 def set\_pen\_color(color: Color): this.type
 def move\_to(pos: Position): this.type
 def line\_to(pos: Position, end\_pos: Position): this.type

 def render(): this.type = this // Don't draw anything, just return this, for child implementations to use fluently

 def top\_left(): Position
 def bottom\_left(): Position
 def top\_right(): Position
 def bottom\_right(): Position
}

trait WindowBorder extends GUIWindow {
 def render(): GUIWindow = {
 super.render()
 .move\_to(top\_left())
 .set\_pen\_color(Black)
 .line\_to(top\_right())
 .line\_to(bottom\_right())
 .line\_to(bottom\_left())
 .line\_to(top\_left())
 }
}

class SwingWindow extends GUIWindow { ... }

val appWin = new SwingWindow() with WindowBorder
appWin.render()

```

### Perl 6

In Perl 6, there are many approaches, but one of the simplest is to declare attributes as read/write and use the `given` keyword. The type annotations are optional, but the native gradual typing makes it much safer to write directly to public attributes.

```
class Employee {
    subset Salary         of Real where * > 0;
    subset NonEmptyString of Str  where * ~~ /\S/; # at least one non-space character

    has NonEmptyString $.name    is rw;
    has NonEmptyString $.surname is rw;
    has Salary         $.salary  is rw;

    method gist {
        return qq:to[END];
 Name: $.name
 Surname: $.surname
 Salary: $.salary
 END
    }
}
my $employee = Employee.new();

given $employee {
    .name    = 'Sally';
    .surname = 'Ride';
    .salary  = 200;
}

say $employee;

# Output:
# Name: Sally
# Surname: Ride
# Salary: 200

```

### PHP

在PHP中，可以使用表示实例的特殊变量$this返回当前对象。因此返回$this将使方法返回实例。下面的示例定义了一个Employee类和三个方法来设置它的名称、姓和薪水。每个Employee类的实例允许调用这些方法。

```
<?php
class Employee
{
    public $name;
    public $surName; 
    public $salary;

    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    public function setSurname($surname)
    {
        $this->surName = $surname;

        return $this;
    }

    public function setSalary($salary)
    {
        $this->salary = $salary;

        return $this;
    }

    public function \_\_toString()
    {
        $employeeInfo = 'Name: '. $this->name. PHP\_EOL;
        $employeeInfo .= 'Surname: '. $this->surName. PHP\_EOL;
        $employeeInfo .= 'Salary: '. $this->salary. PHP\_EOL;

        return $employeeInfo;
    }
}

# Create a new instance of the Employee class, Tom Smith, with a salary of 100:
$employee = (new Employee())
                ->setName('Tom')
                ->setSurname('Smith')
                ->setSalary('100');

# Display the value of the Employee instance:
echo $employee;

# Display:
# Name: Tom
# Surname: Smith
# Salary: 100

```

### Python

Python通过在实例方法中返回`self`：

```
class Poem(object):
    def \_\_init\_\_(self, content):
        self.content = content

    def indent(self, spaces):
        self.content = "" \* spaces + self.content
        return self

    def suffix(self, content):
        self.content = self.content + "- "+ content
        return self

```

```
>>> Poem("Road Not Travelled").indent(4).suffix("Robert Frost").content
' Road Not Travelled - Robert Frost'

```

  

### Visual Basic.Net

参考文献
----

1. ↑ .  [2016-07-02]. （原始内容存档于2021-03-08）.

外部链接
----

* Martin Fowler's original bliki entry coining the term （页面存档备份，存于）
* A Delphi example of writing XML with a fluent interface （页面存档备份，存于）
* A .NET fluent validation library written in C# （页面存档备份，存于）
* A tutorial for creating formal Java fluent APIs from a BNF notation （页面存档备份，存于）

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
软件设计模式 |
| --- |
|  |
| 设计模式 | 

|  |  |
| --- | --- |
| 创建型 | 
* 抽象工厂
* 生成器
* 工厂方法
* 惰性初始
* 原型
* 单例

 |
|  |
| 结构型 | 
* 适配器
* 桥接
* Composite（英语：）
* 修饰
* 外观
* 享元
* 代理

 |
|  |
| 行为型 | 
* 责任链
* 命令
* Interpreter（英语：）
* 迭代器
* 中介者
* Memento（英语：）
* 观察者
* Specification（英语：）
* State（英语：）
* 策略
* 模板方法
* 访问者

 |

 |
|  |
| 并行模式 | 
* 主动对象（英语：）
* 阻止（英语：）
* Binding properties（英语：）
* 双重检查锁定模式
* 异步方法调用
* Future与promise
* Guarded suspension（英语：）
* Join（英语：）
* 锁
* Messaging（英语：）
* 监视器
* Proactor（英语：）
* 反应器
* 读写锁
* 调度
* 线程池
* 线程局部存储

 |
|  |
| 架构模式 | 
* ADR（英语：）
* 主动记录
* Broker（英语：）
* 主從式架構
* cbd
* DAO
* DDD
* Data transfer object（英语：）
* Front controller（英语：）
* Identity map（英语：）
* Interceptor（英语：）
* 控制反转
* MVC
* 微服務
* MVP
* 單體式應用程式
* 多层架构
* *n*-tier（英语：）
* Naked objects（英语：）
* P2P
* 发布/订阅
* REST
* SOA
* Specification（英语：）

 |
|  |
| 云分布式 | 
* 断路器
* CQRS（英语：）
* 补偿交易（英语：）
* 索引表
* 领导者选举（英语：）
* MapReduce
* 物化视图（英语：）
* 管道
* 过滤器（英语：）
* 发布/订阅
* 分片（英语：）
* 节流（英语：）

 |
|  |
| 其他模式 | 
* Blackboard（英语：）
* Business delegate（英语：）
* Composite entity（英语：）
* 委托
* 依赖注入
* Intercepting filter（英语：）
* 惰性載入
* Method chaining（英语：）
* 模拟对象
* 空对象
* 对象池
* Servant（英语：）
* Twin（英语：）
* Type tunnel（英语：）

 |
|  |
| 书籍 | 
* 设计模式：可复用面向对象软件的基础
* 企业集成模式（英语：）

 |
|  |
| 人物 | 
* 克里斯托佛·亚历山大
* 埃里希·伽瑪
* Ralph Johnson（英语：）
* John Vlissides（英语：）
* 葛來迪·布區
* 肯特·貝克
* 沃德·坎宁安
* 马丁·福勒
* Robert Martin（英语：）
* Jim Coplien（英语：）
* Douglas Schmidt（英语：）
* Linda Rising

 |
|  |
| 社区 | 
* The Hillside Group（英语：）
* The Portland Pattern Repository（英语：）

 |

 |