出处: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连贯接口

在软件project,一种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


05-11 13:27