流式介面

流式介面是軟體工程中面向物件API的一種實現方式，以提供更為可讀的原始碼。最早由與Martin Fowler於2005年提出。通常採取 (具體說是方法鏈式呼叫)來轉發一系列物件方法呼叫的上下文 。這個上下文通常是指：C++的iostream流式呼叫就是一個典型的例子。Smalltalk在1970年代就實現了。例子.JavaScript.用於資料庫查詢的jQuery，例如https://github.com/Medium/dynamite  :// getting an item from a tableclient.getItem('user-table').setHashKey('userId', 'userA').setRangeKey('column', '@').execute().then(function(data) {// data.result: the resulting objectJavaScript使用原型繼承與`this`.// example from http://schier.co/post/method-chaining-in-javascript// define the classvar 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 itnew Kitten().setName('Bob').setColor('black').setGender('male').save();Java.jOOQ庫模擬了SQLAuthor 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{"cat", "chat"},{"dog", "chien"},{"fish", "poisson"},// Find translations for English words containing the letter "a",// sorted by length and displayed in uppercaseIEnumerable query = translations.Where (t =&gt; t.Key.Contains("a")).OrderBy (t =&gt; t.Value.Length).Select (t =&gt; t.Value.ToUpper());// The same query constructed progressively:var filtered = translations.Where (t =&gt; t.Key.Contains("a"));var sorted = filtered.OrderBy (t =&gt; t.Value.Length);var finalQuery = sorted.Select (t =&gt; t.Value.ToUpper());流式介面可用於一系列方法，他們執行在同一物件上。// Defines the data contextclass Contextclass Customerprivate Context _context = new Context(); // Initializes the context// set the value for propertiespublic 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 consolepublic void Print()Console.WriteLine("First name: {0} \nLast name: {1} \nSex: {2} \nAddress: {3}", _context.FirstName, _context.LastName, _context.Sex, _context.Address);class Programstatic void Main(string[] args)// Object creationCustomer c1 = new Customer();// Using the method chaining to assign &amp; print data with a single linec1.FirstName("vinod").LastName("srivastav").Sex("male").Address("bangalore").Print();C++.下述程式碼對比了傳統的風格與流式介面的實現風格：// Basic definitionclass 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;// Basic usageint main(int argc, char **argv) {GlutApp app(argc, argv);app.setDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_ALPHA|GLUT_DEPTH); // Set framebuffer paramsapp.setWindowSize(500, 500); // Set window paramsapp.setWindowPosition(200, 200);app.setTitle("My OpenGL/GLUT App");app.create();// Fluent wrapperclass FluentGlutApp : private GlutApp {public:FluentGlutApp(int argc, char **argv) : GlutApp(argc, argv) {} // Inherit parent constructorFluentGlutApp &amp;withDoubleBuffer() {setDisplayMode(getDisplayMode() | GLUT_DOUBLE);return *this;FluentGlutApp &amp;withRGBA() {setDisplayMode(getDisplayMode() | GLUT_RGBA);return *this;FluentGlutApp &amp;withAlpha() {setDisplayMode(getDisplayMode() | GLUT_ALPHA);return *this;FluentGlutApp &amp;withDepth() {setDisplayMode(getDisplayMode() | GLUT_DEPTH);return *this;FluentGlutApp &amp;across(int w, int h) {setWindowSize(w, h);return *this;FluentGlutApp &amp;at(int x, int y) {setWindowPosition(x, y);return *this;FluentGlutApp &amp;named(const char *title) {setTitle(title);return *this;// It doesn't make sense to chain after create(), so don't return *thisvoid create() {GlutApp::create();};// Fluent usageint 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語言允許修改核心類，這使得流式介面成為原生易於實現。class Stringdef prefix(raw)"#{raw} #{self}"enddef suffix(raw)"#{self} #{raw}"enddef indent(raw)raw = " " * raw if raw.kind_of? Fixnumprefix(raw)endendmessage = "there"puts message.prefix("hello").suffix("world").indent(8)Scala.Scala supports a fluent syntax for both method calls and class mixins, using traits and the with keyword. For example:trait GUIWindow {// Rendering methods that return this for fluent drawingdef set_pen_color(color: Color): this.typedef move_to(pos: Position): this.typedef line_to(pos: Position, end_pos: Position): this.typedef render(): this.type = this // Don't draw anything, just return this, for child implementations to use fluentlydef top_left(): Positiondef bottom_left(): Positiondef top_right(): Positiondef bottom_right(): Positiontrait 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())val appWin = new SwingWindow() with WindowBorderappWin.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 codice_1 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 * &gt; 0;subset NonEmptyString of Str where * ~~ /\S/; # at least one non-space characterhas NonEmptyString $.name is rw;has NonEmptyString $.surname is rw;has Salary $.salary is rw;method gist {return qq:to[END];Name: $.nameSurname: $.surnameSalary: $.salaryENDmy $employee = Employee.new();given $employee {.name = 'Sally';.surname = 'Ride';.salary = 200;say $employee;PHP.在PHP中，可以使用表示例項的特殊變數$this返回當前物件。因此返回$this將使方法返回例項。下面的示例定義了一個Employee類和三個方法來設定它的名稱、姓和薪水。每個Employee類的例項允許呼叫這些方法。class Employeepublic $name;public $surName;public $salary;public function setName($name)$this-&gt;name = $name;return $this;public function setSurname($surname)$this-&gt;surName = $surname;return $this;public function setSalary($salary)$this-&gt;salary = $salary;return $this;public function __toString()$employeeInfo = 'Name: ' . $this-&gt;name . PHP_EOL;$employeeInfo .= 'Surname: ' . $this-&gt;surName . PHP_EOL;$employeeInfo .= 'Salary: ' . $this-&gt;salary . PHP_EOL;return $employeeInfo;$employee = (new Employee())-&gt;setName('Tom')-&gt;setSurname('Smith')-&gt;setSalary('100');echo $employee;Python.Python透過在例項方法中返回`self`：class Poem(object):def __init__(self, content):self.content = contentdef indent(self, spaces):self.content = " " * spaces + self.contentreturn selfdef suffix(self, content):self.content = self.content + " - " + contentreturn self»&gt; Poem("Road Not Travelled").indent(4).suffix("Robert Frost").content' Road Not Travelled - Robert Frost'