Aplikacje internetowe

Bełdziowe spojrzenie na aplikacje internetowe

Strategia

Jednym z przydatniejszych elementów programowania obiektowego jest dziedziczenie. Ze względu na swoją przydatność i popularność używania przez programistów często jest on nadużywany.

Załóżmy, że chcemy stworzyć zestaw klas opisujących zwierzęta. Jako, że wszystkie zwierzęta posiadając wspólne cechy, stwórzmy klasę bazową, która będzie je zawierała.

abstract class Animal
{
   private $weight;
   private $height;

   public function __set( $name, $value )
   {
      if( isset( $this -> $name ) )
         $this -> $name = $value;
      else
         throw new Exception( __CLASS__ . ' nie zawiera właściwości ' . $name );
   }

   public function __get( $name )
   {
      if( isset( $this -> $name ) )
         return $this -> $name;
      else
         throw new Exception( __CLASS__ . ' nie zawiera właściwości ' . $name );
   }
}

Naszym zadaniem będzie teraz napisanie metody informującej o sposobie poruszania się poszczególnych zwierząt. Najprostszym rozwiązaniem było by stworzenie wewnątrz klasy Animal metody zawierającej instrukcję switch, która na podstawie właściwości obiektu będzie zwracała odpowiedni wynik.

public function move( )
{
   switch( $this -> type )
   {
         case 'flying':
            echo 'latam sobie';
         break;

         case 'swimming':
            echo 'bul bul pływam sobie';
         break;
   }
}

Powyższa metoda w pełni rozwiązuje nasz problem. Jednak z rozwiązaniem tym wiąże się pewna niedogodność. Wraz z koniecznością dodania obsługi nowego sposobu poruszania się będziemy musieli modyfikować klasę bazową. Działanie takie jest wbrew jednej z zasad programowania obiektowego mówiącej, że tworzone klasy powinny być zamknięte na modyfikacje, a otwarte na rozbudowę.

W celu doprowadzenia naszych klas to zgody z powyższa zasadą możemy przemianować metodę move na abstrakcyjną oraz przenieść informowanie o sposobie poruszania bezpośrednio do poszczególnych klas reprezentujących zwierzęta.

class Fish extends Animal
{
   public function move( )
   {
         echo 'bul bul pływam sobie';
   }
}

Aktualnie klasy spełniają zarówno nasze wymagania jak i wymagania wynikające z reguł programowania obiektowego. Mimo to nadal trochę brakuje im do doskonałości. Jak łatwo zauważyć stworzenie klasy odpowiadających za inne pływające zwierzęta będzie wiązało się z koniecznością powielenia metody move, co powoduje nadmiarowość, a także może stanowić problem podczas ewentualnej zmiany sposobu działania metody.

Trzecim możliwym sposobem jest użycie wzorca strategii (ang. Strategy). Jego zadaniem jest stworzenie grupy powiązanych, hermetycznych klas, które mogą być w łatwy sposób wymieniane. Definicja mało mówi tak więc przejdźmy do przykładu.

Zwierzęta posiadają różne możliwości poruszania się. Jedne latają inne pełzają, a jeszcze inne skaczą. Wszystkie te sposoby poruszania możemy odwzorować jako grupę powiązanych ze sobą klas.

class Swimming
{
      public function  __construct( )
      {
         echo 'bul bul pływam sobie';
      }
}

Dzięki stworzeniu klas odpowiedzialnych za poruszanie się, dodanie tej funkcjonalności poszczególnym zwierzętom ogranicza się do stworzenia odpowiedniego obiektu.

class Fish extends Animal
{
   public function move( )
   {
      return new Swimming;
   }
}

Hermetyzacja sposobu poruszania sprawiła, że pozbyliśmy się problemu nadmiarowości kodu. Dodatkowo wszelkie zmiany sposobu poruszania się nie będą wymagały modyfikacji klasy zwierząt.


Tagi: , , , ,
Kategoria: Wzorce projektowe


7 komentarzy

  1. Jak to miło wiedzieć że nie świadomie używa się wzorców projektowych ;)

  2. Bardzo uproszczone wyjaśnienie wzorca. Przykład niezbyt praktyczny. Nie jest wyjaśnione przykładowe zastosowanie i przeznaczenie wzorca. Brak omówionych konsekwencji, nie wspominając o rozrysowaniu struktury i interakcji wzorca przy pomocy UML czy innego języka :D

    Żartuję oczywiście, notka bardzo przystępna, miło się czyta. Powyżej trochę krytyki, mimo wszystko uważam, że wpis ciekawy i dobry na początkowe zaznajomienie się ze wzorcem :)

  3. I właśnie o to chodzi, żeby było prosto :) jak ktoś myśli o poważnym wykorzystywaniu wzorców to i tak będzie musiał wgłębić się w tematykę. Z drugiej strony jakbym zrobił 20 stronicowy opis z diagramami i innymi kosmosami to połowa mniej zaawansowanych koderów by się wystraszyła i uciekła :) a nie o to przecież chodzi :)

  4. Radex napisał(a):

    No przykład nie najlepszy – w tym przypadku najlprostszym rozwiązaniem byłoby dziedziczenie zarówno po Animal jak i po Swimming. Niestety w PHP można dziedziczyć tylko po jednej klasie.

    Albo, żeby Fish dziedziczyło po SwimmingAnimal, a SwimmingAnimal po Animal.

  5. Kwestii wielokrotnego dziedziczenia nie ma sensu poruszać bo jak sam napisałeś PHP jej nie wspiera. Co do drugiego przykładu to weź pod uwagę, że potrzebna jest kolejna „warstwa” dziedziczenia, a właśnie strategia jest po to, żeby zminimalizować ilość niepotrzebnych dziedziczeń.

  6. Wybacz mi, ale znając już nieco wzorców z powyższego artykułu nie zrozumiałem co konkretnie daje mi wzorzec Strategii.

  7. # pozbycie się nadmiarowości kodu
    # spakowanie pewnej funkcjonalności do zestawu klas, która może być łatwo podmieniana – w przykładzie z notki można w prosty sposób podmieniać sposoby poruszania się
    # niezależność w działaniu, klasy zajmujące się poruszaniem nic nie wiedzą o obiekcie, który je stworzył

Dodaj komentarz