出处:http://blog.csdn.net/u010019717
author:孙广东 时间:2015.3.18 23:00
编程新概念:什么是流利语法fluent syntax?
首先感谢 unity的一款插件 DFTween (内容如下http://blog.csdn.net/u010019717/article/details/44359119),通过学习它知道了流利语法的概念。
Fluent interface连贯接口
在软件工程,一种Fluent interface连贯接口(作为首先由Eric Evans和MartinFowler创造)是面向对象的API,旨在提供更具可读性的代码执行。
Fluent interface通常通过使用methodcascading方法级联(具体methodchaining方法链接)中context指令连续调用执行 (但Fluent interface需要不仅仅是方法链接)。通常context上下文是 :
·定义通过返回值(类对象)调用方法
·以自我作为参照,在新的背景下是相当于最后一个context上下文
·通过返回一个void的上下文终止。
内容
1 History
2 Examples
2.1 JavaScript
2.2 Java
2.3 C++
2.7 PHP
2.8 C#
2.9 Python
历史术语"Fluent interface"是在 2005 年年底提出,虽然这种interface面的整体风格追溯到 1970 年代,Smalltalk 在级联的方法被发明,在上世纪 80 年代的许多例子。最熟悉的是在 c + +,使用iostream库<<或>> 运算符为消息传递、将多个数据发送到同一个对象和其他方法调用允许"manipulators"。
例子JavaScript
有很多使用几种变体的 JS 库的例子:可能是最广为人知的 jQuery。通常用fluent builders流利的建设者来实现 'DB 查询',例如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 中使用prototype inheritance原型继承和 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();
更通用的方式做到这一点是在mu-ffsm中实现.
var mkChained = function(spec) {
return function(init) {
var s = spec[0] ? spec[0](init) : 0;
var i = function(opt) {
return spec[1] ? spec[1](s, opt) : s;
}
Object.keys(spec).forEach(
function(name){
// skip `entry` and `exit` functions
if(/^\d+$/.test(name))
return;
// transition 'name : (s, opt) -> s'
i[name] = function(opt) {
s = spec[name](s, opt);
return i;
};
});
return i;
}
};
var API = mkChained({
0: function(opt) {return ;/* create initial state */},
then: function(s, opt) {return s; /* new state */},
whut: function(s, opt) {return s; /* new state */},
1: function(s, opt) {return ;/* compute final value */}
});
// We create an instance of our newly crafted API,
var call = API() // entry
.whut() // transition
.then() // transition
.whut(); // transition
// And call it
var result0 = call() // exit
, result1 = call() // exit
Java
JOOQ型号的库作为 fluent API 在 Java 中的 SQL
Author a = AUTHOR.as("a");
create.selectFrom(a)
.where(exists(selectOne()
.from(BOOK)
.where(BOOK.STATUS.eq(BOOK_STATUS.SOLD_OUT))
.and(BOOK.AUTHOR_ID.eq(a.ID))));
Op4j库使fluent流畅代码,用于执行辅助任务结构迭代、 数据转换、 过滤等。
String[] datesStr = new String[] {"12-10-1492", "06-12-1978"};
...
List<Calendar> dates =
Op.on(datesStr).toList().map(FnString.toCalendar("dd-MM-yyyy")).get();
Fluflu注释处理器可以使用 Java 注释 fluent API创建。
JaQue使 Java 8 lambda 时必须表示为表达式目录树在运行时,使它能够即创建类型安全连贯接口,而不是窗体中的对象:
Customer obj = ...
obj.property("name").eq("John")
一种学法:
method<Customer>(customer -> customer.getName() == "John")
C + +
C + +中,一个fluentinterface常见用途是的标准iostream,其中链重载的运算符.
以下是提供在更传统的界面,在 c + + 中的fluent interface包装程序的示例:
// 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();
}
PHP
在 PHP 中,一个人可以通过使用$this 特殊的变量所表示的实例返回当前对象。因此返回 $this ;将使返回的实例的方法。下面的示例定义一个类Employee和三种方法来设置其name, surname 和 salary。每个返回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:
$employee = new Employee();
# Employee Tom Smith has a salary of 100:
echo $employee->setName('Tom')
->setSurname('Smith')
->setSalary('100');
# Display:
# Name: Tom
# Surname: Smith
# Salary: 100
C#
C# 使用fluent在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 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());
Fluent interface也可以用于链设置方法,其中operates/shares相同的对象。而不是创建一个customer类我们可以创建一个data context数据上下文,可以用fluent interface,如下所示:
//defines the data context
class Context
{
public string fname { get; set; }
public string lname {get; set;}
public string sex { get; set; }
public string address { get; set; }
}
//defines the customer class
class Customer
{
Context context = new Context(); //initializes the context
// set the value for properties
public Customer FirstName(string firstName)
{
context.fname = firstName;
return this;
}
public Customer LastName(string lastName)
{
context.lname = 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.fname,context.lname,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();
}
}
Python
在 Python 返回 'self' 中的实例方法是实现fluent pattern的一种方法。
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'
更详细的内容观看维基百科:https://en.wikipedia.org/wiki/Fluent_interface