Йерархична декомпозиция
Никога не трябва да започвате да пишете курсове за вашето приложение веднага. Първо трябва да се проектира. Дизайнът трябва да завърши с обмислена архитектура. И за да получите тази архитектура, трябва последователно да разлагате системата.
Декомпозицията трябва да се извърши йерархично – първо системата се разделя на големи функционални модули/подсистеми, които описват нейната работа в най-общ вид. След това получените модули се анализират по-подробно и се разделят на подмодули or обекти.
Преди да изберете обекти, разделете системата на основни семантични блокове, поне психически. В малки applications това обикновено е много лесно да се направи: няколко нива на йерархия са напълно достатъчни, тъй като системата първо е разделена на подсистеми / пакети, а пакетите са разделени на класове.
Тази идея не е толкова тривиална, колкото изглежда. Например, Howва е същността на такъв общ „архитектурен модел“ като Model-View-Controller (MVC)?
Всичко е свързано с отделянето на презентацията от бизнес логиката . Първо, всяко потребителско приложение е разделено на два модула - единият отговаря за внедряването на самата бизнес логика (модел), а вторият е отговорен за взаимодействието с потребителя (потребителски интерфейс or изглед).
Тогава се оказва, че модулите трябва по няHowъв начин да си взаимодействат, за това добавят контролер, чиято задача е да управлява взаимодействието на модулите. Също така в мобилната (класическа) version на MVC моделът Observer е добавен към него, така че View да може да получава събития от модела и да променя показаните данни в реално време.
Типичните модули от най-високо ниво, получени в резултат на първото разделяне на системата на най-големите компоненти, са именно:
- Бизнес логика;
- Потребителски интерфейс;
- База данни;
- Система за съобщения;
- Контейнер за обекти.
Първото разделяне обикновено разделя цялото приложение на 2-7 (максимум 10 части). Ако го разделим на повече части, тогава ще има желание да ги групираме и отново ще получим 2-7 модула от най-високо ниво.
Функционално разграждане
Разделянето на модули/подсистеми е най-добре на базата на задачите, които системата решава . Основната задача е разделена на съставните й подзадачи, които могат да се решават/изпълняват автономно, независимо една от друга.
Всеки модул трябва да отговаря за решаването на няHowва подзадача и да изпълнява съответната функция . В допълнение към функционалното преднаmeaning, модулът се характеризира и с набор от данни, необходими за изпълнението на функцията му, а именно:
Модул = Функция + Данни, необходими за нейното изпълнение.
Ако декомпозицията на модули е напequalsа правилно, тогава взаимодействието с други модули (отговорни за други функции) ще бъде минимално. Може да е така, но липсата му не трябва да е критична за вашия модул.
Модулът не е произволна част от codeа, а отделна функционално значима и завършена програмна единица (подпрограма), която предоставя решение на определена задача и в идеалния случай може да работи самостоятелно or в друга среда и да се използва повторно. Модулът трябва да бъде един вид "интегритет, способен на относителна независимост в поведението и развитието". (Кристофър Александър)
По този начин компетентното разлагане се основава преди всичко на анализа на системните функции и данните, необходими за изпълнението на тези функции. Функциите в този случай не са функции от класове и модули, защото не са обекти. Ако имате само няколко часа в модул, значи сте прекалor.
Силна и слаба свързаност
Много е важно да не прекалявате с модулирането. Ако дадете на начинаещ монолитно Spring приложение и го помолите да го раздели на модули, тогава той ще извади всеки Spring Bean в отделен модул и ще счита, че работата му е завършена. Но не е.
Основният критерий за качеството на декомпозицията е доколко модулите са фокусирани върху решаването на задачите си и са независими.
Това обикновено се формулира по следния начин: "Модулите, получени в резултат на разлагане, трябва да бъдат максимално спрегнати вътрешно (висока вътрешна кохезия) и минимално свързани помежду си (ниско външно свързване)."
Висока кохезия, висока кохезия or „кохезия“ в рамките на модула, показва, че модулът е фокусиран върху решаването на един тесен проблем и не е ангажиран с изпълнение на разнородни функции or несвързани отговорности.
Кохезията характеризира степента, в която задачите, изпълнявани от модула, са свързани една с друга.
Следствие от High Cohesion е Принципът на единната отговорност - първият от петте принципа на SOLID , според който всеки обект/модул трябва да има само една отговорност и не трябва да има повече от една причина за промяната му.
Low Coupling , слабо свързване, означава, че модулите, на които е разделена системата, трябва да бъдат, ако е възможно, независими or слабо свързани един с друг. Те трябва да могат да си взаимодействат, но в същото време да знаят възможно най-малко един за друг.
Не е необходимо всеки модул да знае How работи другият модул, на Howъв език е написан и How работи. Често за организиране на взаимодействието на такива модули се използва определен контейнер, в който се зареждат тези модули.
При правилен дизайн, ако промените един модул, няма да се налага да редактирате други or тези промени ще бъдат минимални. Колкото по-хлабава е връзката, толкова по-лесно е да напишете/разберете/разширите/поправите програмата.
Смята се, че добре проектираните модули трябва да имат следните свойства:
- Функционална цялост и пълнота - всеки модул изпълнява една функция, но я изпълнява добре и напълно, модулът самостоятелно изпълнява пълен набор от операции за изпълнение на своята функция.
- Един вход и един изход - на входа програмният модул получава определен набор от първоначални данни, извършва смислена обработка и връща един набор от резултатни данни, т.е. прилага се стандартният IPO принцип - вход -\u003e процес -\u003e изход.
- Логическа независимост - резултатът от работата на програмния модул зависи само от първоначалните данни, но не зависи от работата на други модули.
- Слаби информационни връзки с други модули - обменът на информация между модулите трябва да бъде сведен до минимум, ако е възможно.
Много е трудно за начинаещ да разбере How да намали още повече свързаността на модулите. Отчасти това знание идва с опит, отчасти - след четене на умни книги. Но най-добре е да анализирате архитектурите на съществуващите applications.
Композиция instead of наследство
Компетентното разлагане е вид изкуство и трудна задача за повечето програмисти. Тук простотата е измамна, а грешките струват скъпо.
Случва се специални модули да са силно свързани помежду си и да не могат да бъдат разработени независимо. Или не е ясно за Howва функция отговаря всеки от тях. Ако срещнете подобен проблем, тогава най-вероятно разделянето на модули е извършено неправилно.
Винаги трябва да е ясно Howва е ролята на всеки модул . Най-надеждният критерий, че декомпозицията е извършена правилно, е дали модулите са независими и ценни подпрограми, които могат да се използват изолирано от останалата част от приложението (и следователно могат да се използват повторно).
При декомпозиране на система е желателно да проверите нейното качество, като си зададете въпросите: „Каква задача изпълнява всеки модул?“, „Колко лесни са модулите за тестване?“, „Възможно ли е да използвате модулите самостоятелно or в друга среда?" да повлияят на другите?"
Трябва да се опитате да запазите модулите възможно най- автономни . Както бе споменато по-горе, това е ключов параметър за правилното разлагане . Следователно трябва да се извърши по такъв начин, че модулите първоначално да са слабо зависими един от друг. Ако сте успели, значи сте страхотни.
Ако не, тогава и тук не всичко е загубено. Има редица специални техники и модели, които ви позволяват допълнително да минимизирате и отслабите връзките между подсистемите. Например в случая на MVC моделът Observer е използван за тази цел, но са възможни и други решения.
Може да се каже, че техниките за отделяне представляват основния „инструмент на архитекта“. Необходимо е само да се разбере, че говорим за всички подсистеми и е необходимо да се отслаби връзката на всички нива на йерархията , тоест не само между класовете, но и между модулите на всяко йерархично ниво.
GO TO FULL VERSION