Własne typy zamiast prymitywów

Często spotykam się z kodem źródłowym, w którym klasy służą tylko do opakowania wielu zmiennych w celu zwrócenia wyniku funkcji. Typy nie powinny być kubełkami wartości tylko odzwierciedleniem idei i zamysłów programisty. Jakiś czas temu nie łatwo było mi przekonać znajomego dlaczego stworzyłem osobny typ reprezentujący język aplikacji zamiast wykorzystania zmiennej typu znakowego.

1
2
3
class Language {
  private String languageCode;
}

Ostatnio jednak wpadłem na jeszcze lepszy przykład wprost z aplikacji, nad którą aktualnie pracujemy. Główna ideą aplikacji CMS jest zarządzanie stronami i sekcjami, w których strony są umieszczane. Każda strona posiada swój unikalny adres.

1
2
3
class Page {
  String getUrl() {}
}

Powiedzmy, że nie jest to takie tragiczne rozwiązanie ale tylko do momentu kiedy zechcemy dodawać i usuwać wersję językową do adresu. W takim wypadku musielibyśmy dodać dodatkowe metody do klasy strony.

1
2
3
4
5
class Page {
  String getUrl() {}
  String getUrlWithoutLangVersion() {}
  String getUrlWithLangVersion(UserSession session) {}
}

Co jednak jeżeli chcielibyśmy wykonać jaką operację na samym tylko adresie np. pobranie nazwy strony albo wersji językowej? Zawsze możemy zastosować klasy pomocnicze.

1
2
3
4
5
6
7
8
class Page {
  String getUrl() {}
}

class PageUrlHelpers {
  static String getUrlWithoutLangVersion(String url) {}
  static String getUrlWithLangVersion(String url, UserSession session) {}
}

O ile taka implementacja pozwoli nam osiągnąć zamierzony cel, o tyle nie ma ona prawie nic wspólnego z programowaniem zorientowanym obiektowo. Dobry rozwiązaniem byłoby stworzenie osobnego typu reprezentującego adres strony. Przeniesienie logiki operacji na adresie do osobnej klasy pozwala nam używać tego typu w klasie strony ale także niezależnie jeżeli potrzebujemy operować na samym tylko adresie.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class PageUrl {
  PageUrl(String pageUrl) {}
  String toString() {}
  String getUrlWithoutLangVersion() {}
  String getUrlWithLangVersion(UserSession session) {}
}

class Page {
  PageUrl getUrl() {}
}

Co z tych rozważań wynika? Oczywiście nic nie stoi na przeszkodzie aby stosować styl proceduralny w języku zorientowanym obiektowo. Jednak tracimy wtedy wszystkie zalety naszego narzędzia, płynąć pod prąd wyrządzamy sobie sami szkodę. Problemy nie pojawią się na początku projektu kiedy złożoność jest niska. Dadzą się we znaki kiedy entropia odpowiednio wzrośnie. Dlatego trzeba złożoność systemu rozbijać na małe moduły, które wstydliwie ukrywają przed światem szczegóły ze swojej prywatności.