Fluent interface

Fluent interface (біжучий чи текучий інтерфейс) в програмуванні — спосіб конструювання об'єктно-орієнтованого API, в якому читабельність коду є близькою до звичайного прозового тексту. Термін вперше був застосований Еріком Евансом та Мартіном Фаулером. Нижче подано приклад для фреймворку тестування JMock: [1].

mock.expects(once()).method("m").with( or(stringContains("hello"),
                                          stringContains("howdy")) );

Історія

Термін "fluent interface" з'явився в кінці 2005, хоча подібний підхід використовувався в Smalltalk ще в 1970-х, та в інших в 1980-их. Типовим прикладом є бібліотека iostream в мові C++, де використовуються оператори << та >> для передачі повідомлень, багатократної пересилки даних до одного і того самого об'єкта.

Приклади

C++

Нижче наведено приклад, де fluent interface обгортка застосована поверх більш традиційного інтерфейсу C++:

Приклад на мові С++
 // Звичайне визначення
 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(){;}
 };
 // Базове використання
 int main(int argc, char **argv) {
     GlutApp app(argc, argv);
     app.setDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_ALPHA|GLUT_DEPTH);
     app.setWindowSize(500, 500);
     app.setWindowPosition(200, 200);
     app.setTitle("My OpenGL/GLUT App");
     app.create();
 }

 // "fluent interface" обгортка 
 class FluentGlutApp : private GlutApp {
 public:
     FluentGlutApp(int argc, char **argv) : GlutApp(argc, argv) {}
     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;
     }
     // Ланцюжок після create() немає змісту, тому не повертаємо *this
     void create() {
         GlutApp::create();
     }
 };
 // Використання "fluent interface"
 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();
 }

Python

В мові Python використовують повернення `self` в методі.

Приклад на мові Python
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'

Примітки

  1. FluentInterface [Архівовано 8 березня 2021 у Wayback Machine.], Martin Fowler, 20-12-2005