Newsletter

Grupa MagazynyInternetowe

Online: 906

Wyszukiwarka

Wzorce projektowe

Wzorce projektowe - stan

Wzorce projektowe - stan Pisząc programy, często trzeba zbudować klasy, których obiekty mogą znajdować się w jednym z kilku stanów. Zadanie nie wydaje się trudne, często jednak modyfikacja kodu napisanego w typowy sposób staje się uciążliwa, powodując konieczność modyfikacji wielu metod klasy.

Marcin Staniszczak

Wiele z nich zaczyna z czasem przybierać postać rosnącej drabinki warunków, co utrudnia analizę kodu. Na szczęście istnieje rozwiązanie tego problemu.

Zajmijmy się prostym programem: autoryzacją użytkowników. Użytkownicy podają login i hasło, po czym kod ma za zadanie sprawdzić, czy podane dane są poprawne i w zależności od tego wykonać odpowiednią akcję. Dodatkowo, dla utrudnienia, po trzykrotnym błędnym logowaniu zablokujemy możliwość ponownego logowania na 30 sekund, co powinno utrudnić ewentualną próbę włamania.

Najprostszym rozwiązaniem problemu jest jedna klasa z szeregiem metod:

[+]
 
PHP
Pobierz
 1. class Autoryzacja {
 2.   const SPRAWDZANIE = 1;
 3.   const BLAD_AUTORYZACJI = 2;
 4.   const BLAD_AUTORYZACJI_3_RAZY = 3;
 5.   const AUTORYZACJA_POPRAWNA = 4;
 6.   public function sprawdz($strLogin,$strHaslo) {
 7.     //...
 8.   }
 9.   public function autoryzacjaPoprawna() {
10.     //...
11.   }
12.   public function blokada() {
13.     //...
14.   }
15.   //...
16. }
[+]
 
Java
Pobierz
 1. public class Autoryzacja {
 2.   final static int SPRAWDZANIE = 1;
 3.   final static int BLAD_AUTORYZACJI = 2;
 4.   final static int BLAD_AUTORYZACJI_3_RAZY = 3;
 5.   final static int AUTORYZACJA_POPRAWNA = 4;
 6.   public void sprawdz(String login, String haslo) {
 7.     //..
 8.   }
 9.   public void autoryzacjaPoprawna() {
10.     //..
11.   }
12.   public void blokada() {
13.     //..
14.   }
15.   //...
16. }

Kod więc jest dość prosty, może przyjmować stan określony przez jedną ze stałych. Niestety będziemy tutaj mieli do czynienia z kilkoma metodami, z których każda będzie miała szereg warunków i wymagała zmian w przypadku dodawania nowego sposobu przeprowadzania autoryzacji.

Ewentualnie całą logikę można zaszyć w metodzie sprawdź, co spowoduje stworzenie potężnego drzewka warunków, jeszcze trudniejszego w pielęgnacji.

Istnieje jeszcze jeden sposób rozwiązania problemu - skorzystanie ze wzorca projektowego stan (ang. state).

Wzorzec stan

Używając wzorca stan, wyodrębniamy do osobnej klasy każdy ze stanów, w jakim może znaleźć się obiekt - tak więc stany nie będą więcej reprezentowane przez proste stałe. Każdy z obiektów stanu może mieć możliwość wpływania na stan obiektu Autoryzacji, a więc poszczególne stany powinny wiedzieć, na jaki inny stan zmodyfikować stan obiektu w zależności od zaistniałych okoliczności. Brzmi to skomplikowanie, ale w rzeczywistości jest bardzo proste. Przyjrzyj się kodowi programu.

Na samym początku należy wyodrębnić wspólne cechy wszystkich stanów, które będą interfejsem stanu. W naszym prosty przykładzie interfejs jest bardzo prosty i maa jedną metodę sprawdź:

[+]
 
PHP
Pobierz
1. interface Stan {
2.   public function__construct(Autoryzacja $objAutoryzacja);
3.   public function sprawdz($strLogin,$strHaslo);
4. }
[+]
 
Java
Pobierz
1. public interface Stan {
2.   public void sprawdz(String login,String haslo);
3. }

Sama klasa Autoryzacja powinna dostarczać metod pozwalających na odczyt oraz zmianę aktualnego stanu i metody (tzw. gettery), udostępniających obiekty wszystkich stanów, w jakich może się znaleźć dana klasa:

[-]
 
PHP
Pobierz
Listing zwinięty - 35 linii
[-]
 
Java
Pobierz
Listing zwinięty - 35 linii

Głównym stanem klasy Autoryzacja jest SparwdzanieStan - sprawdza on login oraz hasło i w zależności od wyniku sprawdzania uruchamia stan:

  • AutoryzacjaPoprawnaStan - gdy zarówno login, jak i hasło są poprawne,
  • BladAutoryzacjiStan - gdy login lub hasło jest błędne,
  • BladAutoryzacji3RazyStan - gdy trzy razy pod rząd podano błędny login i hasło.
PHP: Java:

Stany BladAutoryzacjiStan oraz Autoryzacja- PoprawnaStan są bardzo proste i nie wymagają szczegółowych wyjaśnień:

[+]
 
PHP
Pobierz
 1. class BladAutoryzacjiStan implements Stan
 2. {
 3.   private $objAutoryzacja = null;
 4.   public function__construct(Autoryzacja $objAutoryzacja) {
 5.     $this->objAutoryzacja = $objAutoryzacja;
 6.   }
 7.   public function sprawdz($strLogin,$strHaslo) {
 8.     echo "Niepoprawny login lub hasło!<br>";
 9.     $this->objAutoryzacja->setStan($this->objAutoryzacja->getStanSprawdzania());
10.   }
11. }
12. class AutoryzacjaPoprawnaStan implements Stan {
13.   public function__construct(Autoryzacja $objAutoryzacja) {
14.   }
15.   public function sprawdz($strLogin,$strHaslo) {
16.     echo "Zalogowany - witam!<br>";
17.   }
18. }
[+]
 
Java
Pobierz
 1. public class BladAutoryzacjiStan
 2. implements Stan {
 3.   private Autoryzacja autoryzacja;
 4.   public BladAutoryzacjiStan(Autoryzacja autoryzacja) {
 5.     this.autoryzacja = autoryzacja;
 6.   }
 7.   public void sprawdz(String login, String haslo) {
 8.     System.out.println("Niepoprawny login lub hasło");
 9.     autoryzacja.setStan(autoryzacja.getStanSprawdzania());
10.   }
11. }
12. public class AutoryzacjaPoprawnaStan
13. implements Stan {
14.   public AutoryzacjaPoprawnaStan(Autoryzacja autoryzacja) {
15.   }
16.   public void sprawdz(String login,String haslo) {
17.     System.out.println("Zalogowany - witam!");
18.   }
19. }

Bardziej złożony jest stan BladAutoryzacji- 3RazyStan. Jego zadaniem jest zablokowanie możliwości logowania na 30 sekund. Gdy stan ten jest ustawiony, przy pierwszym wywołaniu metody sprawdź (wywołanie to następuje z poziomu stanu SprawdzanieStan) zainicjowana zostaje zmienna przechowująca aktualny czas.

Następnie stan ten staje się domyślny na 30 sekund, nie pozwalając tym samym na zalogowanie się. Po upłynięciu 30 sekund stan BladAutoryzacji3RazyStan sam ustala stan klasy Autoryzacja ponownie na stan Sparwdzanie-Stan, co pozwala na dokonanie kolejnych prób logowania (po 3 nieudanych następuje ponowna blokada na 30 sekund):

[+]
 
PHP
Pobierz
 1. class BladAutoryzacji3RazyStan implements
 2. Stan {
 3.   private $objAutoryzacja = null;
 4.   private $intCzas = null;
 5.   public function__construct(Autoryzacja $objAutoryzacja) {
 6.     $this->objAutoryzacja = $objAutoryzacja;
 7.   }
 8.   public function sprawdz($strLogin,$strHaslo) {
 9.     if ($this->intCzas === null) {
10.       $this->intCzas = time();
11.     }
12.     if ((time() - $this->intCzas) >=5) {
13.     
    $this->objAutoryzacja->setStan($this->objAutoryzacja->getStanSprawdzania());
14.       $this->objAutoryzacja->getStan()->sprawdz($strLogin, $strHaslo);
15.       $this->intCzas = null;
16.       return;
17.     }
18.     echo "Podano trzy razy nieprawidłowy login lub hasło - możliwość logowania
    zablokowana na 30 sekund!<br>";
19.   }
20. }
[+]
 
Java
Pobierz
 1. import java.util.*;
 2. public class BladAutoryzacji3RazyStan implements Stan {
 3.   private Autoryzacja autoryzacja;
 4.   private Long czas = -1l;
 5.   public BladAutoryzacji3RazyStan(Autoryzacja autoryzacja) {
 6.     this.autoryzacja = autoryzacja;
 7.   }
 8.   public void sprawdz(String login,String haslo) {
 9.     GregorianCalendar gc = new GregorianCalendar();
10.     if (czas == -1l) {
11.       czas = gc.getTime().getTime();
12.     }
13.     if ((gc.getTime().getTime() - czas) >= 30000) {
14.       autoryzacja.setStan(autoryzacja.getStanSprawdzania());
15.       autoryzacja.getStan().sprawdz(login, haslo);
16.       czas = -1l;
17.       return;
18.     }
19.     System.out.println("Podano trzy razy nieprawidłowy login lub hasło -
    możliwość logowania zablokowana na 30 sekund!");
20.   }
21. }

W taki oto sposób wyeliminowaliśmy niepraktyczne drzewka warunków, wyodrębniliśmy zachowanie charakterystyczne dla każdego stanu oraz ułatwiliśmy modyfikowanie kodu. W tej chwili dodanie kolejnego stanu wiązało się będzie zazwyczaj z:

  • dodaniem odpowiedniego obiektu w klasie Autoryzacja,
  • dodanie gettera do klasy Autoryzacja,
  • wprowadzenie niewielkich modyfikacji, najczęściej tylko w jednym ze stanów, który będzie ustawiał nowy stan.

Na koniec

Pamiętaj, że przedstawiony tu program został napisany tak, aby był możliwie jak najbardziej prosty do zrozumienia. Hasło i login są w nim zakodowana na stałe, podczas gdy w rzeczywistości powinny być pobierane np. z bazy danych.

Znasz już kilka prostych, lecz podstawowych wzorców projektowych. Dzięki temu możesz pisać lepsze, łatwiejsze w rozwijaniu programy. Zachęcam jednocześnie do lektury kolejnych artykułów z tej serii - przed nami jeszcze długa droga.

26 marca 2008

Powiązane publikacje

Wzorce projektowe - fasada

Wzorce projektowe - fasada

Wzorce projektowe - adapter

Wzorce projektowe - adapter

Wzorce projektowe - strategia

Wzorce projektowe - strategia

Wzorce projektowe - fabryka

Wzorce projektowe - fabryka

 
Wzorce projektowe - proxy

Wzorce projektowe - proxy

Wzorce projektowe - polecenie

Wzorce projektowe - polecenie

Wzorce projektowe - obserwator

Wzorce projektowe - obserwator

Wzorce projektowe - dekorator

Wzorce projektowe - dekorator

 
Wzorce projektowe - singleton

Wzorce projektowe - singleton

 
Skomentuj
ten artykuł

Brak komentarzy

Kod obrazkowy
(Kliknij, aby zmienić)
 

Autor

Marcin Staniszczak

Artykuły tego autora:

Pozostałe publikacje

Wzorce projektowe - fasada

Wzorce projektowe - fasada

Fasada jest wzorcem zaliczającym się do wzorców strukturalnych. Jest to jeden z popularniejszych, a jednocześnie najprostszych wzorców projektowych.

Newsletter

Jesli chcesz być na bieżąco z tym co się dzieje na stronie magazynu INTERNET Maker zapisz się do naszego newslettera.