sobota, 6 października 2012

czy wystarczy klasa, czy już czas na interfejs?

w czym problem?

Czasami są sytuacje, kiedy poważnie się zastanawiamy nad tym, czy interfejs jest nam rzeczywiście potrzebny, czy może jego stworzenie byłoby po prostu generowaniem zbędnego kodu, mającego na celu tylko i wyłącznie dodanie kolejnego elementu do, już i tak dość skomplikowanej, struktury.

Kiedy, więc klasa już nam nie wystarcza i niezbędne staje się wprowadzenie interfejsu?

interfejs dla jednego obiektu?

Wyobraźmy sobie, że mamy klasę reprezentującą okienka systemu, które mogą być między innymi zamykane, minimalizowane, maksymalizowane, ich rozmiar może być dowolnie modyfikowany:
<?php
class Window
{
  public function close() { /* CODE */ }
  public function maximize() { /* CODE */ }
  public function minimalize() { /* CODE */ }
  public function increaseTheSize() { /* CODE */ }
  public function decreaseTheSize() { /* CODE */ }
  /* OTHER USEFUL METHODS */
}

Często, podczas pracy z naszą aplikacją, użytkownicy otwierają wiele okien, dlatego też dodaliśmy do niej sprytną funkcjonalność umożliwiającą jednoczesne zminimalizowanie, zmaksymalizowanie lub też zamknięcie wszystkich okien:
<?php
class WindowManager
{
  private $_windows = array();

  public function closeAll() { /* CODE */ }
  public function maximizeAll() { /* CODE */ }
  public function minimalizeAll() { /* CODE */ }
}

Pominąłem implementacje ciał tych metod, bo jak możecie łatwo się domyślić, każda z nich przechodzi przez wszystkie elementy i wywołuje na nich stosowną akcję.

No dobra, ale co z dodaniem tych wszystkich okien?
<?php
class WindowManager
{
  /* CODE */
  public function addWindow(Window $window) 
  { 
    $this->_windows[] = $window;
  }
}

I w tym miejscu warto się na chwilę zatrzymać. Czy rzeczywiście to klasa Window powinna być typem parametru?
Wiele przemawia za tym, że tak, ponieważ instancja klasy WindowManager będzie wykorzystywana do zarządzania oknami, jednak ja bym się nad tym jeszcze chwilę zastanowił.

tak więc, interfejs czy nie?

Dlaczego uważam, że sytuacja jest warta dogłębniejszego przemyślenia? Z jednej strony, rzeczywiście wykorzystujemy naszego managera do zarządzania jedynie oknami, ale czy wykorzystujemy pełną funkcjonalność tychże okien?

Czy nie lepiej byłoby stworzyć interfejs w stylu:
<?php
class ModifiableGuiItem
{
  public function close();
  public function maximize();
  public function minimalize();
}

Tak, aby nasz manager wyglądał tak:
<?php
class GuiItemsManager
{
  
  private $_guiItems = array();

  public function closeAll() { /* CODE */ }
  public function maximizeAll() { /* CODE */ }
  public function minimalizeAll() { /* CODE */ }
  public function addItem(ModifiableGuiItem $guiItem) { /* CODE */ }
}

czy to nie jest zbędne?

Osobiście jestem przeciwnikiem projektowania zorientowanego na przyszłość i starającego się przewidzieć to, jakie wymagania i funkcjonalności keidyśtam ktośtam może wymyślić.
Jednak w tym wypadku, poza otwartą drogą na ulepszenia, mamy coś jeszcze, jedną bardzo istotną informacje - używając interfejsu dajemy programistom wiedzę, jakie metody są wykorzystywane w klasie GuiItemsManager, a to jest naprawdę istotna informacja, która może niejednokrotnie ułatwić nasze życie. Jeżeli kiedyś będzie potrzeba zmienić z jakichś powodów klasę Window, to wiemy jakie metody muszą pozostać, aby obecna funkcjonalność działała, a które są dla niej absolutnie nieistotne.