A számítógép-programozásban a folyékony interfész egy módszer objektumorientált API-k megalkotására. A program forrásának olvashatósága megközelíti, vagy eléri az általános írott szövegekét.

A folyékony interfészt rendszerint metódusok láncolásával valósítják meg, egy utasítás kontextusát a következő utasításnak adva át. Általában a kontextus: meghatározója a hívás visszaadott értéke, önhivatkozó, a következő kontextus ekvivalens az utolsóval, void kontextus visszaadásával terminál. Története Az interfész általános stílusa az 1970-es évekre megy vissza, egészen a Smalltalkig. Az 1980-as évekből számos példa ismert. Ismert példa a C++ iostream könyvtára, aminek << és >> operátorai több adatot küldenek el ugyanannak az objektumnak, és más metódushívások számára lehetővé teszik manipulátorok használatát. További korábbi példák a Garnet system (1988-tól, Lisp-ben) és az Amulet system (1994-től, C++-ban). A folyékony interfész elnevezést 2005 végén alkották meg.

Példák C# A C#-ban gyakori a folyékony interfész használata a LINQ-val, amikor lekérdezéseket építenek szabványos lekérdezésoperátorokkal. A megvalósítás kiterjesztési metódusokon alapul. 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 can also be used to chain a set of method, which operates/shares the same object. Like instead of creating a customer class we can create a data context which can be decorated with fluent interface as follows. // 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++ C++-ban a folyékony interfész gyakori az iosteam könyvtár használatakor, ami túlterhelt operátorokat kapcsol össze. A következő példa folyékony interfész adaptert mutat egy hagyományosabb interfész fölött:

// 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(); } D Az egységes függvényhívás szintaxisnak köszönhetően (Uniform Function Call Syntax, UFCS) D-ben különösen egyszerű a metódusok láncolása.Uniform Function Call Syntax, Dr. Dobbs Journal, 28 Mar 2012

Ha azt írjuk, hogy x.toInt(); de nincs x típusának toInt() tagfüggvénye, akkor a fordító keres egy toInt(x); formájú független függvényt. Ez lehetővé teszi a metódusok láncolását x.toInt().toString(format); formában, ahelyett, hogy toString(toInt(x),format); Java A jOOQ könyvtár az SQL-t folyékony interfészként modellezi: 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))));

Az op4j könyvtárhttp://www.op4j.org/ lehetővé teszi kisegítő feladatok beépítését a láncba, mint struktúra iteráció, adatkonverzió, szűrés: String[] datesStr = new String[] {"12-10-1492", "06-12-1978"}; ... List<Calendar> dates = Op.on(datesStr).toList().map(FnString.toCalendar("dd-MM-yyyy")).get();

A fluflu annotációfeldolgozóhttps://github.com/verhas/fluflu annotációkat biztosít folyékony interfész létrehozásához.

A JaQue könyvtárhttps://github.com/TrigerSoft/jaque lehetővé teszi a Java 8 lambdáinak kifejezésfa objektumkéntttp://msdn.microsoft.com/en-us/library/bb397951.aspx való reprezentációját. Ezzel típusbiztos folyékony interfész alakítható ki. Azaz ahelyett, hogy: Customer obj = ... obj.property("name").eq("John") írható, hogy: method<Customer>(customer -> customer.getName() == "John")

Az EasyMock mock objektumos tesztelő könyvtárhttp://easymock.org/ kiterjedten használja ezt a stílust, hogy kifejező interfészt adjon a programnak: Collection mockCollection = EasyMock.createMock(Collection.class); EasyMock .expect(mockCollection.remove(null)) .andThrow(new NullPointerException()) .atLeastOnce();

A Java Swing APIban a LayoutManager interfész definiálja, hoigy a Container objektumok hogyan lehet ellenőrzött Component elhelyezésük. Az egyik legjobban konfigurálható LayoutManager a GridBagLayout, amiben GridBagConstraints osztály használható az elhelyezés vezérlésére. Egy tipikus példa: GridBagLayout gl = new GridBagLayout(); JPanel p = new JPanel(); p.setLayout( gl );

JLabel l = new JLabel("Name:"); JTextField nm = new JTextField(10);

GridBagConstraints gc = new GridBagConstraints(); gc.gridx = 0; gc.gridy = 0; gc.fill = GridBagConstraints.NONE; p.add( l, gc );

gc.gridx = 1; gc.fill = GridBagConstraints.HORIZONTAL; gc.weightx = 1; p.add( nm, gc );

Ez hosszú kód írását igényli, amiben nehéz látni, hogy mi is történik. A Packer osztály folyékony mechanizmussal látja el az osztályt, ezzel a fenti kód tömörebben írható: JPanel p = new JPanel(); Packer pk = new Packer( p );

JLabel l = new JLabel("Name:"); JTextField nm = new JTextField(10);

pk.pack( l ).gridx(0).gridy(0); pk.pack( nm ).gridx(1).gridy(0).fillx(); Még több példa is létezik, amikor a folyékony interfész nagyban egyszerűsíti a program lekódolását, továbbá segíti egy API nyelv létrehozását, ami segíti az API kezelését, mivel egy metódus visszatérési értéke kontextust ad a következő akciónak. JavaScript Több JavaScript könyvtár is ezen a megközelítésen alapul. Talán a jQuery a legismertebb. Tipikusan folyékony építőket használ az adatbázis lekérdezésekhez, például a dynamite-ban: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 })

Egyszerű példa JavaScriptben a prototípus öröklés és a this használata: // 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(); Perl 6 Perl 6-ban a folyékony interfészre több megközelítés is létezik. Az egyik legegyszerűbb az attribútumok deklarálása read/write tulajdonságokkal, és a given kulcsszó használata. A típus annotációk opcionálisak, de a natív graduális típusozottság sokkal biztonságosabbá teszi a publikus attribútumok közvetlen írását. 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-ben az aktuális objektum hivatkozható a $this változóval, ami a példányt reprezentálja. Így a return $this; utasítás az aktuális példányt adja vissza. A példa definiál egy Employee osztályt és három metódust a név, vezetéknév és a fizetés beállítását. Mindegyik visszaadja a példányt, így a metódusok összekapcsolhatók.

<?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 Pythonban a folyékony interfész megvalósításának egy módja, hogy a példánymetódusok a self objektumot adják vissza: 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