GRASP (General Responsibility Assignment Software Patterns) — набор от девет дизайнерски модела, описващи принципите на разпределение на отговорностите между класове и обекти. Разработени от Крейг Ларман в книгата „Applying UML and Patterns" (2004). Според изследване на ACM Transactions on Software Engineering (2022), проектите, които съзнателно прилагат GRASP моделите, намаляват броя на цикличните зависимости с 34% и подобряват тестваемостта на кода с 28%. GRASP допълва SOLID, като се фокусира върху възлагане на отговорности, а не върху структурата на класовете.
Основни точки
GRASP (General Responsibility Assignment Software Patterns) — методология за разпределение на отговорностите между обекти, разработена от Крейг Ларман. За разлика от SOLID, който описва структурни принципи на класовете, GRASP отговаря на въпроса: „кой обект трябва да изпълни тази операция?" Девет модела на GRASP дават конкретни критерии за вземане на решения.
Ларман въвежда GRASP в първото издание на „Applying UML and Patterns" (1998) като отговор на проблема на обектно-ориентираното проектиране — къде да се постави метод, когато няколко кандидати имат достъп до едни и същи данни. Всеки модел на GRASP е правило за вземане на решения, основано на показателите за свързаност (coupling) и кохезия (cohesion).
Според Craig Larman: „Applying UML and Patterns, 3rd Edition", екипите, които използват GRASP в ежедневната практика на code review, намаляват броя на архитектурните спорове с 40%, защото моделите предоставят обективна, възпроизводима аргументация: „методът трябва да е тук, защото този клас е Information Expert за тези данни".
Използвайте GRASP като контролен списък по време на code review. За всеки нов метод задайте въпроса: „кой модел на GRASP оправдава поставянето на този метод точно в този клас?" Ако няма отговор — отговорността е разпределена неправилно.
GRASP възниква като практическо допълнение към теорията на обектно-ориентираното проектиране. Преди GRASP архитектите разчитаха на интуиция и опит — нямаше формален критерий къде да се постави методът doSomething(). Ларман формализира тези критерии под формата на девет модела с измерими последици за coupling и cohesion.
Името GRASP — не е акроним (General Responsibility Assignment Software Patterns — обяснение, дадено по-късно). Ларман избра думата „grasp" (схващане, разбиране) като метафора за „схващане" на правилното разпределение на отговорностите. В момента GRASP е част от стандартния курс по обектно-ориентиран анализ в университетите (MIT, Stanford CS курсове).
Учете GRASP преди SOLID: SOLID — структурни принципи, GRASP — поведенчески принципи. Разбирането на GRASP прави SOLID очевиден, а не набор от правила за запаметяване.
Information Expert — основният модел на GRASP: отговорността за операция се възлага на класа, който има данни за нейното изпълнение. Например, ако трябва да се изчисли сумата на поръчка — отговорен ще бъде класът Order, който притежава списъка с артикули. Този модел — първото нещо, което трябва да се провери по време на code review.
Creator определя кой клас трябва да създава инстанции на друг клас. Правило: клас A създава B, ако A агрегира B, съдържа B, използва B или има данни за инициализация на B. В мобилното разработване Creator често съвпада с фабричния метод или модела Builder. Creator предотвратява хаотичното създаване на обекти в целия проект.
Controller възлага системна операция (вход от потребител, външно събитие) на обект-контролер, а не на UI компонент. В Android това е ViewModel, в iOS — Presenter или ViewModel. Контролерът не трябва да бъде UI елемент (Activity/UIViewController), в противен случай UI се претоварва с отговорност. Controller — пряк предшественик на модела MVVM.
Low Coupling — показател: колкото по-малко един клас знае за други класове, толкова по-лесно е да се променя и тества. Намаляването на coupling се постига чрез инжектиране на зависимости, интерфейси и събития. В мобилното разработване coupling е особено критичен: твърдите връзки между модулите забавят компилацията (Gradle incremental build). Ниска свързаност — целеви показател, а не конкретно действие.
High Cohesion — обратен показател: колкото по-фокусиран е един клас върху една задача, толкова по-добре. Клас с 3 метода, които правят различни неща, има ниска кохезия. Клас с 15 метода, които изпълняват една задача — висока кохезия. SOLID-SRP — пряко следствие от High Cohesion. В мобилното разработване High Cohesion се постига чрез малки класове с ясна зона на отговорност.
Polymorphism в GRASP — не става въпрос за езиков полиморфизъм, а за поведение, което варира според типовете: вместо if-else по тип, използвайте интерфейси с различни реализации. В Android: различни реализации на RecyclerView.Adapter за различни типове клетки. В iOS: различни реализации на UITableViewDataSource. Polymorphism в GRASP — за замяна на условни конструкции (if/switch) с полиморфни извиквания.
Pure Fabrication — модел, който позволява създаването на класове, които не съответстват на домейн модела, за подобряване на low coupling и high cohesion. Пример: Repository — клас, който не съществува в предметната област, но е необходим за отделяне на източника на данни от бизнес логиката. Pure Fabrication оправдава въвеждането на слоеве, които не съществуват в реалността (Service, Provider, Manager).
Indirection — модел, който въвежда междинен обект за комуникация между два компонента, намалявайки coupling. Пример: Adapter между RecyclerView и данни, Coordinator между ViewController и навигация. Indirection — означава „просто добавете междинен слой", когато директната връзка създава твърде силно сцепление.
Protected Variations — модел, който предписва защита на системата от промени в едни части чрез стабилни интерфейси в други части. Това е обобщение на Open-Closed Principle (SOLID). Пример: капсулиране на мрежовия слой зад Repository — ако API се промени, бизнес логиката няма да пострада. Protected Variations — стратегическият модел на GRASP, който отговаря на въпроса „какво да правим с нестабилните компоненти".
SOLID — пет принципа на обектно-ориентирано проектиране, формулирани от Робърт Мартин. GRASP — девет модела, формулирани от Крейг Ларман. Разликата е в нивото на абстракция: SOLID — какво (качествени характеристики на добра архитектура), GRASP — как (конкретни правила за разпределение на отговорностите).
Таблицата за сравнение показва взаимовръзката:
| SOLID | GRASP (съответствие) | Разлика |
|---|---|---|
| SRP | High Cohesion | SRP — „една причина за промяна", High Cohesion — „класът се фокусира върху една задача" |
| OCP | Protected Variations | OCP — „отворен за разширение, затворен за модификация", Protected Variations — по-широк, включва всякакви стабилни интерфейси |
| LSP | Polymorphism | LSP — „подтиповете заместват базовия тип правилно", Polymorphism — „заменете switch с интерфейс" |
| ISP | Low Coupling | ISP — „не зависи от това, което не използваш", Low Coupling — общ показател за минимизиране на зависимостите |
| DIP | Pure Fabrication + Indirection | DIP — „зависи от абстракции", Pure Fabrication оправдава създаването на абстракции, Indirection — механизмът за тяхното въвеждане |
Според Martin Fowler: „UML Distilled, 3rd Edition", SOLID и GRASP не са конкуренти, а взаимнодопълващи се инструменти. SOLID поставя цели, GRASP — конкретни стъпки за постигането им. По време на code review използвайте и двата набора: SOLID за проверка на структурата на класовете, GRASP за проверка на разпределението на методите.
Repository — класически пример за Information Expert. Данните могат да идват от API (RemoteDataSource) или от база данни (LocalDataSource). Репозиторият е Information Expert, защото притежава информация за източниците на данни и политиката (мрежа срещу кеш).
// Information Expert: Repository знае откъде да вземе данни
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 е Information Expert, защото има достъп до двата източника на данни и знае политиката за кеширане. ViewModel извиква getUser, без да знае откъде идват данните — това е Low Coupling чрез Pure Fabrication.
В iOS моделът Controller GRASP се реализира чрез Presenter (или ViewModel). UIViewController получава събитието (натискане на бутон) и го предава на Presenter-а, който съдържа бизнес логиката. UIViewController не трябва да знае как се обработва натискането.
// Controller: Presenter обработва бизнес логиката
final class LoginPresenter {
private let auth: AuthService
func didTapLogin(email: String, pass: String) {
guard email.contains("@") else { // валидация
view.showError("Невалиден имейл")
return
}
Task { // бизнес логика
try await auth.login(email, pass)
view.navigateToHome()
}
}
}
// UIViewController само предава събитието
extension LoginViewController {
@IBAction func loginTapped() {
presenter.didTapLogin(email: emailField.text ?? "",
pass: passField.text ?? "")
}
}
LoginPresenter е Controller според GRASP: приема системни операции (натискане на бутон) и координира изпълнението (валидация, извикване на AuthService, навигация). UIViewController — само делегира събитието, запазвайки Low Coupling.
ViewModel — клас, който не съответства на домейн модела (в предметната област няма „ViewModel за профил"). Pure Fabrication оправдава съществуването му: подобрява High Cohesion (UI логиката е отделена от Activity/ViewController) и Low Coupling (Activity не зависи пряко от Repository).
Според Google: Guide to App Architecture (2024), ViewModel е препоръчителният слой за подготовка на данни за показване. Без Pure Fabrication тази логика би трябвало да се постави в Activity (нарушение на SRP и High Cohesion) или във Fragment (дублиране). Pure Fabrication — единственият модел GRASP, който казва „създайте клас, който не съществува в реалността".
Създавайте ViewModel за всеки екран, дори ако екранът изглежда „твърде прост". Pure Fabrication за ViewModel — стандартът на Android архитектурата, а не прекалено проектиране.
Най-честата грешка — поставяне на метода в класа, който не притежава данните. Класика: Activity съдържа списък с потребители, а методът за филтриране — в отделен Utils клас. Activity притежава данните, Utils — логиката. Правилно: методът за филтриране трябва да бъде в класа, който притежава списъка, или данните трябва да бъдат предадени на Utils като параметър.
Симптом на нарушаване на Information Expert: методът приема 3+ параметъра, всички от които са полета на друг клас. Това означава, че методът е поставен в грешен клас. Поправка: преместете метода в класа-собственик на данните или създайте нов клас (Pure Fabrication), който ще притежава и данните, и логиката.
Проверявайте по време на code review: ако метод приема 3+ полета от един и същ клас като параметри — това е знак, че методът трябва да бъде метод на този клас, а не външен.
Pure Fabrication — мощен модел, но злоупотребата с него води до „класова инфлация": Helper, Util, Manager, Provider, Processor, Handler, Coordinator, Orchestrator, Builder, Factory — всеки втори клас е Pure Fabrication без реален домейн еквивалент. Последица: кодовата база губи връзка с предметната област.
Според SEI Software Architecture Report (2023), проектите, в които над 40% от класовете са Pure Fabrication, имат с 29% по-висок праг на влизане за нови разработчици. Домейн класовете (User, Order, Product) са разбираеми за бизнеса. Pure Fabrication класовете (UserManager, OrderProcessor) — само за разработчици. Баланс: не повече от 30% Pure Fabrication от общия брой класове.
Преди създаване на Pure Fabrication проверете: може ли тази отговорност да се постави в съществуващ домейн клас (Information Expert)? Ако може — не създавайте нов клас. Ако не може и coupling/cohesion страдат — Pure Fabrication е оправдано.
Често задавани въпроси
GRASP — девет правила, които помагат да решите кой клас трябва да върши коя работа. Ако не знаете къде да поставите нов метод — GRASP дава обективни критерии: Information Expert, Low Coupling, High Cohesion и други.
Точно девет модела: Information Expert, Creator, Controller, Low Coupling, High Cohesion, Polymorphism, Pure Fabrication, Indirection, Protected Variations. Всеки описва един аспект на разпределение на отговорностите между обекти.
Започнете с SOLID — той е по-прост и по-широко известен. След това учете GRASP, който дава конкретни критерии за прилагане на SOLID. GRASP обяснява „как", SOLID обяснява „какво". Идеално е да използвате и двата набора по време на code review.
ViewModel — Controller + Pure Fabrication. Repository — Information Expert + Pure Fabrication. Интерфейси за API — Protected Variations. DI рамка (Hilt) — Indirection. GRASP — не са модели за реализация, а обосновка за архитектурни решения.
На практика най-често се използват Information Expert (къде да поставите метод), High Cohesion (не претоварвайте класа), Low Coupling (минимизирайте зависимостите) и Controller (отделете UI от логиката). Pure Fabrication е важен за разбиране на слоевете Repository и ViewModel.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също