Płynne API (Fluent Interface)

Wprowadzenie

Płynne API (ang. fluent interface) to sposób projektowania interfejsów programistycznych (API), który umożliwia łańcuchowe wywoływanie metod (method chaining). Celem jest zwiększenie czytelności kodu oraz nadanie mu formy przypominającej zdania w języku naturalnym. :contentReference[oaicite:0]{index=0}

Wzorzec ten został spopularyzowany przez Erica Evansa i Martina Fowlera (ok. 2005) i jest szeroko stosowany m.in. w bibliotekach LINQ czy ORM. :contentReference[oaicite:1]{index=1}

Podstawowa idea

Każda metoda: - modyfikuje stan obiektu lub - ustawia parametr konfiguracji

i zwraca ten sam obiekt (`self`), co pozwala kontynuować wywołania.

Przykład (bez Fluent API):

ax = Axes(0, 0, 100, 100)
ax.set_xrange(0, 10)
ax.set_yrange(-1, 1)

Z Fluent API:

ax = Axes(0, 0, 100, 100) \
    .bottom(0, 10) \
    .left(-1, 1)

Kod staje się krótszy i bardziej deklaratywny.

Kluczowe zasady projektowe

  1. Metody zwracają self

def bottom(self, xmin, xmax):
    self.xmin = xmin
    self.xmax = xmax
    return self
  1. Zachowanie kontekstu

Każde kolejne wywołanie operuje na tym samym obiekcie (tym samym stanie).

  1. Czytelność jako DSL (Domain-Specific Language)

Kod powinien przypominać „zdanie”:

Axes(...).bottom(0, 10).left(-1, 1).with_grid(True)
  1. Opcjonalne zakończenie łańcucha

Czasem stosuje się metodę end() jako sygnał końca konfiguracji:

ax = Axes(...).bottom(...).left(...).end()

Zastosowanie w klasie Axes

W zadaniu projektowym klasa Axes pełni rolę konfiguratora układu współrzędnych. Fluent API pozwala oddzielić:

  • konstrukcję obiektu (geometria)

  • konfigurację (zakresy, styl, siatka)

Przykład docelowy:

axes = (
    Axes(80, 40, 580, 260)
    .bottom(0.0, 6.28)
    .left(-1.0, 1.0)
    .with_xticks(6)
    .with_yticks(4)
    .with_grid(True)
    .end()
)

Warto zwrócić uwagę:

  • metody nie tworzą nowych obiektów → modyfikują istniejący

  • kolejność wywołań ma znaczenie semantyczne

  • API powinno być spójne (np. bottom/top, left/right)

Typowe błędy

  1. Brak return self

def bottom(self, xmin, xmax):
    self.xmin = xmin
    self.xmax = xmax
    # brak return → łańcuch się psuje
  1. Niespójne nazewnictwo

Złe:

.set_xrange(...).grid_on(True)

Dobre:

.bottom(...).with_grid(True)
  1. Zbyt długa logika w metodach

Metody fluent powinny być proste i jednofunkcyjne.

Uwagi projektowe

  • Fluent API poprawia czytelność, ale może utrudniać debugowanie (łańcuch wielu wywołań w jednej linii).

  • Dobrą praktyką jest łamanie łańcucha na wiele linii.

  • W Pythonie implementacja jest naturalna dzięki zwracaniu self.