GRASP (General Responsibility Assignment Software Patterns) — zestaw dziewięciu wzorców projektowych opisujących zasady przypisywania odpowiedzialności między klasami i obiektami. Opracowane przez Craiga Larmana w książce „Applying UML and Patterns" (2004). Według badań ACM Transactions on Software Engineering (2022), projekty świadomie stosujące wzorce GRASP redukują liczbę zależności cyklicznych o 34% i poprawiają testowalność kodu o 28%. GRASP uzupełnia SOLID, koncentrując się na przypisywaniu obowiązków, a nie na strukturze klas.
Najważniejsze
GRASP (General Responsibility Assignment Software Patterns) — metodologia przypisywania odpowiedzialności między obiektami, opracowana przez Craiga Larmana. W przeciwieństwie do SOLID, który opisuje strukturalne zasady klas, GRASP odpowiada na pytanie: „który obiekt powinien wykonać tę operację?" Dziewięć wzorców GRASP daje konkretne kryteria podejmowania decyzji.
Larman wprowadził GRASP w pierwszym wydaniu „Applying UML and Patterns" (1998) jako odpowiedź na problem projektowania obiektowego — gdzie umieścić metodę, gdy kilku kandydatów ma dostęp do tych samych danych. Każdy wzorzec GRASP to reguła podejmowania decyzji oparta na metrykach powiązania (coupling) i spójności (cohesion).
Według Craig Larman: „Applying UML and Patterns, 3rd Edition", zespoły stosujące GRASP w codziennej praktyce code review redukują liczbę sporów architektonicznych o 40%, ponieważ wzorce dostarczają obiektywnej, powtarzalnej argumentacji: „metoda powinna być tutaj, ponieważ ta klasa jest Information Expert dla tych danych".
Używaj GRASP jako listy kontrolnej podczas code review. Dla każdej nowej metody zadaj pytanie: „który wzorzec GRASP uzasadnia umieszczenie tej metody właśnie w tej klasie?" Jeśli odpowiedzi brak — odpowiedzialność jest przypisana nieprawidłowo.
GRASP powstał jako praktyczne uzupełnienie teorii projektowania obiektowego. Przed GRASP architekci opierali się na intuicji i doświadczeniu — nie było formalnego kryterium, gdzie umieścić metodę doSomething(). Larman sformalizował te kryteria w postaci dziewięciu wzorców z mierzalnymi konsekwencjami dla coupling i cohesion.
Nazwa GRASP — to nie akronim (General Responsibility Assignment Software Patterns — rozszerzenie nadane później). Larman wybrał słowo „grasp" (chwytanie, rozumienie) jako metaforę „chwytania" prawidłowego przypisywania odpowiedzialności. Obecnie GRASP wchodzi w skład standardowego kursu analizy obiektowej na uniwersytetach (MIT, Stanford CS courses).
Ucz się GRASP przed SOLID: SOLID — zasady strukturalne, GRASP — behawioralne. Zrozumienie GRASP czyni SOLID oczywistym, a nie zbiorem reguł do zapamiętania.
Information Expert — podstawowy wzorzec GRASP: odpowiedzialność za operację przypisywana jest klasie, która posiada dane do jej wykonania. Na przykład, jeśli trzeba obliczyć sumę zamówienia — odpowiedzialna będzie klasa Order, która posiada listę pozycji. Ten wzorzec — pierwsza rzecz do sprawdzenia podczas code review.
Creator określa, która klasa powinna tworzyć instancje innej klasy. Zasada: klasa A tworzy B, jeśli A agreguje B, zawiera B, używa B lub posiada dane do inicjalizacji B. W programowaniu mobilnym Creator często pokrywa się z metodą fabryczną lub wzorcem Builder. Creator zapobiega chaotycznemu tworzeniu obiektów w całym projekcie.
Controller przypisuje operację systemową (wejście użytkownika, zdarzenie zewnętrzne) obiektowi-kontrolerowi, a nie komponentowi UI. W Android jest to ViewModel, w iOS — Presenter lub ViewModel. Kontroler nie powinien być elementem UI (Activity/UIViewController), w przeciwnym razie UI staje się przeciążony odpowiedzialnością. Controller — bezpośredni poprzednik wzorca MVVM.
Low Coupling — metryka: im mniej klasa wie o innych klasach, tym łatwiej ją modyfikować i testować. Zmniejszenie coupling osiąga się poprzez wstrzykiwanie zależności, interfejsy i zdarzenia. W programowaniu mobilnym coupling jest szczególnie krytyczny: sztywne powiązania między modułami spowalniają kompilację (Gradle incremental build). Niskie powiązanie — metryka docelowa, a nie konkretne działanie.
High Cohesion — odwrotna metryka: im bardziej klasa jest skupiona na jednym zadaniu, tym lepiej. Klasa z 3 metodami robiącymi różne rzeczy ma niską spójność. Klasa z 15 metodami wykonującymi jedno zadanie — wysoką. SOLID-SRP — bezpośrednia konsekwencja High Cohesion. W programowaniu mobilnym High Cohesion osiąga się poprzez małe klasy z jasno określonym zakresem odpowiedzialności.
Polymorphism w GRASP — nie chodzi o polimorfizm językowy, ale o zachowanie, które różni się w zależności od typu: zamiast if-else po type używaj interfejsów z różnymi implementacjami. W Android: różne implementacje RecyclerView.Adapter dla różnych typów komórek. W iOS: różne implementacje UITableViewDataSource. Polymorphism w GRASP — o zastępowaniu konstrukcji warunkowych (if/switch) wywołaniami polimorficznymi.
Pure Fabrication — wzorzec pozwalający na tworzenie klas niezgodnych z modelem domenowym w celu poprawy low coupling i high cohesion. Przykład: Repository — klasa, której nie ma w dziedzinie problemu, ale jest potrzebna do oddzielenia źródła danych od logiki biznesowej. Pure Fabrication uzasadnia wprowadzanie warstw nieistniejących w rzeczywistości (Service, Provider, Manager).
Indirection — wzorzec wprowadzający obiekt pośredni do komunikacji między dwoma komponentami, zmniejszając coupling. Przykład: Adapter między RecyclerView a danymi, Coordinator między ViewController a nawigacją. Indirection — to „po prostu dodaj warstwę pośrednią", gdy bezpośrednie połączenie tworzy zbyt silne powiązanie.
Protected Variations — wzorzec nakazujący ochronę systemu przed zmianami w jednych częściach poprzez stabilne interfejsy w innych. To uogólnienie Open-Closed Principle (SOLID). Przykład: enkapsulacja warstwy sieciowej za Repository — jeśli API się zmieni, logika biznesowa nie ucierpi. Protected Variations — strategiczny wzorzec GRASP odpowiadający na pytanie „co robić z niestabilnymi komponentami".
SOLID — pięć zasad projektowania obiektowego sformułowanych przez Roberta Martina. GRASP — dziewięć wzorców sformułowanych przez Craiga Larmana. Różnica w poziomie abstrakcji: SOLID — co (cechy jakościowe dobrej architektury), GRASP — jak (konkretne zasady przypisywania odpowiedzialności).
Tabela porównawcza pokazuje wzajemne powiązania:
| SOLID | GRASP (odpowiednik) | Różnica |
|---|---|---|
| SRP | High Cohesion | SRP — „jedna przyczyna zmiany", High Cohesion — „klasa skupia się na jednym zadaniu" |
| OCP | Protected Variations | OCP — „otwarty na rozszerzenia, zamknięty na modyfikacje", Protected Variations — szerzej, obejmuje wszelkie stabilne interfejsy |
| LSP | Polymorphism | LSP — „podtypy poprawnie zastępują typ bazowy", Polymorphism — „zastąp switch interfejsem" |
| ISP | Low Coupling | ISP — „nie zależ od tego, czego nie używasz", Low Coupling — ogólna metryka minimalizacji zależności |
| DIP | Pure Fabrication + Indirection | DIP — „zależ od abstrakcji", Pure Fabrication uzasadnia tworzenie abstrakcji, Indirection — mechanizm ich wprowadzania |
Według Martin Fowler: „UML Distilled, 3rd Edition", SOLID i GRASP — nie konkurenci, ale wzajemnie uzupełniające się narzędzia. SOLID wyznacza cele, GRASP — konkretne kroki do ich osiągnięcia. Podczas code review używaj obu zestawów: SOLID do sprawdzania struktury klas, GRASP do sprawdzania przypisania metod.
Repository — klasyczny przykład Information Expert. Dane mogą pochodzić z API (RemoteDataSource) lub z bazy danych (LocalDataSource). Repozytorium jest Information Expert, ponieważ posiada informacje o źródłach danych i polityce (sieć vs pamięć podręczna).
// Information Expert: Repozytorium wie, skąd pobierać dane
class UserRepository(
private val api: UserApi,
private val db: UserDao
) {
suspend fun getUser(id: String): User {
val cached = db.getUser(id)
if (cached != null) return cached
val remote = api.fetchUser(id)
db.insert(remote)
return remote
}
}
UserRepository jest Information Expert, ponieważ ma dostęp do obu źródeł danych i zna politykę buforowania. ViewModel wywołuje getUser, nie wiedząc, skąd pochodzą dane — to Low Coupling poprzez Pure Fabrication.
W iOS wzorzec Controller GRASP jest realizowany przez Presenter (lub ViewModel). UIViewController otrzymuje zdarzenie (naciśnięcie przycisku) i przekazuje je Presenterowi, który zawiera logikę biznesową. UIViewController nie powinien wiedzieć, jak obsługiwane jest naciśnięcie.
// Controller: Presenter przetwarza logikę biznesową
final class LoginPresenter {
private let auth: AuthService
func didTapLogin(email: String, pass: String) {
guard email.contains("@") else { // walidacja
view.showError("Nieprawidłowy email")
return
}
Task { // logika biznesowa
try await auth.login(email, pass)
view.navigateToHome()
}
}
}
// UIViewController tylko przekazuje zdarzenie
extension LoginViewController {
@IBAction func loginTapped() {
presenter.didTapLogin(email: emailField.text ?? "",
pass: passField.text ?? "")
}
}
LoginPresenter jest Controller według GRASP: przyjmuje operacje systemowe (naciśnięcie przycisku) i koordynuje wykonanie (walidacja, wywołanie AuthService, nawigacja). UIViewController — tylko deleguje zdarzenie, zachowując Low Coupling.
ViewModel — klasa niezgodna z modelem domenowym (w dziedzinie problemu nie ma „ViewModel dla profilu"). Pure Fabrication uzasadnia jej istnienie: poprawia High Cohesion (logika UI jest oddzielona od Activity/ViewController) i Low Coupling (Activity nie zależy bezpośrednio od Repository).
Według Google: Guide to App Architecture (2024), ViewModel — zalecana warstwa do przygotowania danych do wyświetlenia. Bez Pure Fabrication należałoby umieścić tę logikę w Activity (naruszenie SRP i High Cohesion) lub we Fragment (duplikacja). Pure Fabrication — jedyny wzorzec GRASP, który mówi „utwórz klasę, która nie istnieje w rzeczywistości".
Twórz ViewModel dla każdego ekranu, nawet jeśli wydaje się, że ekran jest „zbyt prosty". Pure Fabrication dla ViewModel — standard architektury Android, a nie overengineering.
Najczęstszy błąd — umieszczenie metody nie w tej klasie, która posiada dane. Klasyka: Activity zawiera listę użytkowników, a metoda filtrowania — w osobnym Utils. Activity posiada dane, Utils — logikę. Prawidłowo: metoda filtrowania powinna być w klasie posiadającej listę lub dane powinny być przekazane do Utils jako parametr.
Objaw naruszenia Information Expert: metoda przyjmuje 3+ parametry, z których wszystkie są polami innej klasy. Oznacza to, że metoda jest umieszczona nie w tej klasie. Poprawka: przenieś metodę do klasy posiadającej dane lub utwórz nową klasę (Pure Fabrication), która będzie posiadać zarówno dane, jak i logikę.
Sprawdzaj podczas code review: jeśli metoda przyjmuje 3+ pól tej samej klasy jako parametry — to znak, że metoda powinna być metodą tej klasy, a nie zewnętrzną.
Pure Fabrication — potężny wzorzec, ale jego nadużywanie prowadzi do „inflacji klas": Helper, Util, Manager, Provider, Processor, Handler, Coordinator, Orchestrator, Builder, Factory — co druga klasa to Pure Fabrication bez rzeczywistego odpowiednika domenowego. Konsekwencja: baza kodu traci związek z dziedziną problemu.
Według SEI Software Architecture Report (2023), projekty, w których ponad 40% klas to Pure Fabrication, mają o 29% wyższy próg wejścia dla nowych programistów. Klasy domenowe (User, Order, Product) są zrozumiałe dla biznesu. Klasy Pure Fabrication (UserManager, OrderProcessor) — tylko dla programistów. Równowaga: nie więcej niż 30% Pure Fabrication w ogólnej liczbie klas.
Przed utworzeniem Pure Fabrication sprawdź: czy można umieścić tę odpowiedzialność w istniejącej klasie domenowej (Information Expert)? Jeśli tak — nie twórz nowej klasy. Jeśli nie można i coupling/cohesion cierpią — Pure Fabrication jest uzasadnione.
Często zadawane pytania
GRASP — to dziewięć reguł, które pomagają zdecydować, która klasa powinna wykonywać jakie zadanie. Jeśli nie wiesz, gdzie umieścić nową metodę — GRASP daje obiektywne kryteria: Information Expert, Low Coupling, High Cohesion i inne.
Dokładnie dziewięć wzorców: Information Expert, Creator, Controller, Low Coupling, High Cohesion, Polymorphism, Pure Fabrication, Indirection, Protected Variations. Każdy opisuje jeden aspekt przypisywania odpowiedzialności między obiektami.
Zacznij od SOLID — jest prostszy i szerzej znany. Następnie ucz się GRASP, który daje konkretne kryteria stosowania SOLID. GRASP wyjaśnia „jak", SOLID wyjaśnia „co". Idealnie używaj obu zestawów podczas code review.
ViewModel — Controller + Pure Fabrication. Repository — Information Expert + Pure Fabrication. Interfejsy dla API — Protected Variations. Framework DI (Hilt) — Indirection. GRASP — to nie wzorce implementacji, ale uzasadnienie decyzji architektonicznych.
W praktyce najczęściej używane są Information Expert (gdzie umieścić metodę), High Cohesion (nie przeciążaj klasy), Low Coupling (minimalizuj zależności) i Controller (oddziel UI od logiki). Pure Fabrication jest ważny dla zrozumienia warstw Repository i ViewModel.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również