流式接口
✍ dations ◷ 2025-11-19 04:58:16 #流式接口
流式接口(fluent interface)是软件工程中面向对象API的一种实现方式,以提供更为可读的源代码。最早由Eric Evans(英语:Eric Evans (technologist))与Martin Fowler于2005年提出。
通常采取方法瀑布调用(英语:enmethod cascading) (具体说是方法链式调用(英语:method chaining))来转发一系列对象方法调用的上下文 。这个上下文(context)通常是指:
C++的iostream流式调用就是一个典型的例子。Smalltalk在1970年代就实现了方法瀑布调用(英语:enmethod cascading)。
用于数据库查询的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 object })JavaScript使用原型继承与`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库模拟了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中大量使用 与扩展方法。
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 uppercaseIEnumerable<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 contextclass 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 classclass 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) endend # Fluent interfacemessage = "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 }object Black extends Color { override def rgb(): Tuple3 = ("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 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 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; 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: 200PHP
在PHP中,可以使用表示实例的特殊变量$this返回当前对象。因此返回$this将使方法返回实例。下面的示例定义了一个Employee类和三个方法来设置它的名称、姓和薪水。每个Employee类的实例允许调用这些方法。
<?phpclass 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: 100Python
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'
相关
- 南苏拉威西南苏拉威西省(印尼语:Provinsi Sulawesi Selatan,简称为Sulsel)简称南苏省,是印度尼西亚苏拉威西岛南半岛上的一个省,萨拉亚尔群岛也是南苏拉威西省的一部分。2010年普查南苏拉威
- 国际船舶与港口设施保全章程国际船舶与港口设施章程(英文:International Ship and Port Facility Security Code;缩写:ISPS)是1978年海上人命安全国际公约针对船舶、港口及港口国政府对于保全的一项修正案,于
- 定期存款定期存款,是指存款人将现金存入在银行机构开设的定期储蓄账户内,事先约定以固定期限为储蓄时间,以高于活期存款的利率获得回报,期满后可领取本金和利息的一种储蓄形式。如果存款
- 柳原博光柳原博光(やなぎわら ひろみつ、1889年3月19日 - 1966年12月31日)为日本海军军人。最终阶级为海军中将。伯爵。原名为“大原义质”。东京都出身。大原重朝伯爵的三男,柳原义
- 4,4'-二甲氧基氧化偶氮苯4,4′-二甲氧基氧化偶氮苯(PAA)是一种有机化合物。它在固态是白色粉末,加热时形成液晶态。它是已知的且容易制备的液晶之一,在液晶显示器的发展用起着重要作用。它的液晶范围在1
- 昌盛昌盛(?年-?年),贵州都匀卫人,宣德时期的神宫监太监。洪武二十四年(1391年),昌盛在十一岁时入宫,明成祖即位,升他为长随奉御。成祖八次派他前往交趾办理事务。永乐十年(1412年),成祖派他侍候
- 黑雨 (美国电影)《黑雨》(英语:)是1989年9月22日上映的美国动作犯罪电影,由英国导演雷德利·斯科特执导,演员包括迈克尔·道格拉斯、安迪·加西亚、松田优作、高仓健及凯特·卡普肖。剧情讲述日
- 也门胡塞叛乱盖达组织活动 (2011–15)(英语:al-Qaeda insurgency in Yemen)胡赛叛乱 (2014–15)也门内战 (2015–)胡塞在也门的导弹袭击沙特阿拉伯在也门的空中打击胡塞在沙特阿拉伯的袭击
- 山下健二郎山下健二郎(日语:山下 健二郎/やました けんじろう ,1985年5月24日-)是日本舞者、演员、主持人,为男子歌舞组合三代目J Soul Brothers成员。京都府长冈京市出身。身高179公分。于2021年与朝比奈彩结婚。在京都府出生,小学二年级之前都在奈良县生活。高中就读于日本京都府立向阳高等学校,高中2年的文化节为契机,他开始跳舞。他在进入大学后不到10天就退学了,曾经经历了一年边打工边跳舞的生活。在母亲的推荐下,他就读了Cat音乐学院(日语:キャットミュージックカレッジ専門学校)学习街舞。在毕业
- 叙威高速公路叙威高速公路是四川省的一条省级高速公路,连接四川省叙永县与云南省威信县,起于泸州市叙永县正东镇附近,与厦蓉高速公路相接,经长秧、枧槽、长坝、分水,在叙永县分水镇与威信县双河镇交界处的下河附近进入云南省。该高速公路(四川段)全长36.145公里,估算总投资49.5亿元。于2016年11月18日开工,预计2020年开通。