zoukankan      html  css  js  c++  java
  • [JavaScript,Java,C#,C++,Ruby,Perl,PHP,Python][转]流式接口(Fluent interface)

    原文:https://en.m.wikipedia.org/wiki/Fluent_interface(英文,完整)

    转载:https://zh.wikipedia.org/wiki/流式接口(中文,部分翻译,部分例子,破墙)

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

    通常采取方法瀑布调用 (具体说是方法链式调用)来转发一系列对象方法调用的上下文。这个上下文(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} 
    Last name: {1} 
    Sex: {2} 
    Address: {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)

    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

    In PHP, one can return the current object by using the $this special variable which represent the instance. Hence return $this; will make the method return the instance. The example below defines a class Employee and three methods to set its name, surname and salary. Each return the instance of the Employee class allowing to chain methods.

    <?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'
  • 相关阅读:
    C++之路进阶——codevs2439(降雨量)
    C++之路进阶——codevs2933(诗人小G)
    C++之路进阶——bzoj2879(美食节)
    C++之路进阶——bzoj1934(善意的投票)
    C++之路进阶——bzoj3876(支线剧情)
    C++之路进阶——codevs1281(Xn数列)
    八数码难题
    道路游戏
    细胞分裂
    最长链
  • 原文地址:https://www.cnblogs.com/hcbin/p/9081096.html
Copyright © 2011-2022 走看看