fbpx
维基百科

流式接口

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

通常采取方法瀑布调用 (具体说是方法链式调用)来转发一系列对象方法调用的上下文 [1]。这个上下文(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 traits 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. ^ bliki: FluentInterface. [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 (页面存档备份,存于互联网档案馆

流式接口, 本條目存在以下問題, 請協助改善本條目或在討論頁針對議題發表看法, 沒有或很少條目链入本條目, 2016年7月4日, 請根据格式指引, 在其他相關條目加入本條目的內部連結, 來建構維基百科內部網絡, 此條目需要补充更多来源, 2016年7月4日, 请协助補充多方面可靠来源以改善这篇条目, 无法查证的内容可能會因為异议提出而被移除, 致使用者, 请搜索一下条目的标题, 来源搜索, 网页, 新闻, 书籍, 学术, 图像, 以检查网络上是否存在该主题的更多可靠来源, 判定指引, 此條目翻譯品質不佳, 2016. 本條目存在以下問題 請協助改善本條目或在討論頁針對議題發表看法 沒有或很少條目链入本條目 2016年7月4日 請根据格式指引 在其他相關條目加入本條目的內部連結 來建構維基百科內部網絡 此條目需要补充更多来源 2016年7月4日 请协助補充多方面可靠来源以改善这篇条目 无法查证的内容可能會因為异议提出而被移除 致使用者 请搜索一下条目的标题 来源搜索 流式接口 网页 新闻 书籍 学术 图像 以检查网络上是否存在该主题的更多可靠来源 判定指引 此條目翻譯品質不佳 2016年7月4日 翻譯者可能不熟悉中文或原文語言 也可能使用了機器翻譯 請協助翻譯本條目或重新編寫 并注意避免翻译腔的问题 明顯拙劣的翻譯請改掛 a href Template D html class mw redirect title Template D d a a href Wikipedia CSD html G13 class mw redirect title Wikipedia CSD G13 a 提交刪除 此條目需要擴充 2018年6月4日 请協助改善这篇條目 更進一步的信息可能會在討論頁或扩充请求中找到 请在擴充條目後將此模板移除 此條目需要精通或熟悉相关主题的编者参与及协助编辑 2018年6月4日 請邀請適合的人士改善本条目 更多的細節與詳情請參见討論頁 流式接口 fluent interface 是软件工程中面向对象API的一种实现方式 以提供更为可读的源代码 最早由Eric Evans 英语 Eric Evans technologist 与Martin Fowler于2005年提出 通常采取方法瀑布调用 具体说是方法链式调用 来转发一系列对象方法调用的上下文 1 这个上下文 context 通常是指 通过被调方法的返回值定义 自引用 新的上下文等于老的上下文 返回一个空的上下文来终止 C 的iostream流式调用就是一个典型的例子 Smalltalk在1970年代就实现了方法瀑布调用 目录 1 例子 1 1 JavaScript 1 2 Java 1 3 C 1 4 C 1 5 Ruby 1 6 Scala 1 7 Perl 6 1 8 PHP 1 9 Python 1 10 Visual Basic Net 2 参考文献 3 外部链接例子 编辑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 lt string string gt 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 lt string gt 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 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 amp 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 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 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 traits 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 gt 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类的实例允许调用这些方法 lt php class Employee public 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 Create a new instance of the Employee class Tom Smith with a salary of 100 employee new Employee gt setName Tom gt setSurname Smith gt 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 gt gt gt Poem Road Not Travelled indent 4 suffix Robert Frost content Road Not Travelled Robert Frost Visual Basic Net 编辑 此章节尚無任何内容 需要扩充 参考文献 编辑 bliki FluentInterface 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 页面存档备份 存于互联网档案馆 取自 https zh wikipedia org w index php title 流式接口 amp oldid 74897673, 维基百科,wiki,书籍,书籍,图书馆,

文章

,阅读,下载,免费,免费下载,mp3,视频,mp4,3gp, jpg,jpeg,gif,png,图片,音乐,歌曲,电影,书籍,游戏,游戏。