Cohesion (bağlılıq) — bir modul və ya sinif daxilində elementlərin nə qədər sıx əlaqəli olduğunu göstərən metrikadır. Wikipedia-ya görə, yüksək bağlılıq yaxşı dizayn edilmiş modulun əlamətidir, burada bütün metodlar və sahələr bir tapşırıq üzərində işləyir. Cohesion birbaşa kodun davamlılığına təsir edir və coupling — modullar arasında bağlılıq — anlayışına qarşı durur.
Əsas məqamlar
Cohesion (bağlılıq) — bir sinif və ya modul daxilində metodların, sahələrin və xüsusiyyətlərin məntiqi olaraq nə qədər əlaqəli olduğunu qiymətləndirən metrikadır. Yüksək bağlı modul bir tapşırığı yerinə yetirir və onun icrası üçün lazım olan elementləri ehtiva edir. Aşağı bağlı modul eyni anda bir neçə iş görməyə çalışır — metodlar mənaca görə zəif əlaqəlidir.
Obyekt yönümlü proqramlaşdırmada cohesion Single Responsibility Principle (S) ilə sıx əlaqəlidir. Bir sinifin aydın bir məsuliyyəti varsa, onun cohesion adətən yüksək olur. Sinif həm UI, həm biznes məntiqi, həm də şəbəkə işi ilə məşğul olarsa — cohesion aşağıdır və belə bir sinif daha dar məsuliyyətli bir neçə ayrı sinifə bölünməlidir.
Cohesion anlayışı tərtibatçıya refaktorinq qərarları qəbul etməkə kömək edir. Sinifdə sinif sahələrini istifadə etməyən bir metod gördükdə, bu aşağı bağlılıq siqnalıdır. Belə bir metod ya sinifdə artıqdır, ya da sinif səhv dizayn edilmişdir. Yüksək bağlılığa can atmaq — kodun hər səviyyəsində arxitekturanı yaxşılaşdırmaq üzərində davamlı işdir.
Proqram mühəndisliyində yeddi cohesion səviyyəsi mövcuddur, ən pisdən ən yaxşıya doğru sıralanmışdır. Bu şkalayı başa düşmək modulun keyfiyyətini obyektiv qiymətləndirməyə və refaktorinq zamanı hansı istiqamətdə hərəkət edəcəyini müyyənləşdirməyə imkan verir. Səviyyə nə qədər yüksəkdirsə, kod bir o qədər davamlı və başa düşülən olacaq.
Təsadüfi (coincidental) — ən pis səviyyə, elementlər modulda təsadüfi olaraq, heç bir məntiqi əlaqə olmadan qruplaşdırılıb. Nümunə: tarix formatlama, email göndərmə və endirim hesablama metodlarının toplandığı Utilities sinfi. Belə bir sinfi bütün metodları oxumadan başa düşmək mümkün deyil və bir metodun dəyişdirilməsi digərlərini sındıra bilər.
Məntiqi (logical) bağlılıq — elementlər məntiqi cəhətdən əlaqəli, lakin mahiyyətcə fərqli tapşırıqları yerinə yetirir. parseJSON, parseXML və parseCSV metodları olan sinif „parsing“ mövzusu ilə məntiqi olaraq bağlıdır, lakin hər bir metod prinsipcə fərqli iş görür. Problem: yeni format (YAML) əlavə edildikdə sinif böyüyür və interfeysi şişir.
Zamansal (temporal) bağlılıq — elementlər icra vaxtına görə qruplaşdırılıb. Verilənlər bazasını konfiqurasiya edən, konfiqi yükləyən, analitikanı işə salan AppInitializer sinfi — bütün bunlar tətbiq işə düşərkən baş verir, lakin tapşırıqların özü əlaqəli deyil. Onları hər məsuliyyət sahəsi üçün ayrı Initializer-lərə bölmək daha yaxşıdır.
Prosessual (procedural) bağlılıq elementlərin icra ardıcıllığı ilə birləşdirilməsi ilə yaranır. „Sifarişin işlɘnməsi” modulu validateCart, processPayment, sendConfirmation metodlarını ehtiva edir — hər bir metod ciddi şəkildə əvvəlkindən sonra çağrılır. Bu təsadüfi və ya məntiqi bağlılıqdan yaxşıdır, lakin yenə də ideal deyil: hər addım ayrı modula çıxarıla bilər.
Kommunikativ (communicational) bağlılıq — elementlər eyni məlumatlarla işləyir. getUser, updateUser, deleteUser metodları olan UserService sinfi ümumi User varlığı ilə birləşdirilib. Bu prosessual bağlılıqdan əhəmiyyətli dərəcədə yaxşıdır: sinfin aydın mövzu sahəsi var. Mobil layihələrdə Repository siniflərinin əksəriyyəti kommunikativ bağlılığa malikdir.
Funksional (functional) bağlılıq — ən yüksək səviyyə, modulun hər bir elementi bir tapşırığın yerinə yetirilməsində iştirak edir. Uzunluğu, simvolların mövcudluğunu və parolun mürəkkəbliyini yoxlayan tək validate metodu olan PasswordValidator sinfi — funksional bağlılıq nümunəsidir. Belə bir sinif yalnız parol doğrulama qaydaları dəyişdiyi üçün dəyişir.
Funksional bağlılığa nail olmaq — arxitektura refaktorinqinin əsas məqsədidir. Hər bir sinifin dəyişmək üçün dəqiq bir səbəbi olmalıdır. Mobil inkişafda funksional bağlılıq ayrı Use Case-lərin, xüsusi View-lərin, formatlayıcıların və doğrulayıcıların ayrılması ilə əldə edilir. Hər bir belə sinif aydın məsuliyyət zonası olan tam tikinti blokudur.
Cohesion və coupling — eyni keyfiyyətin iki tərəfidir. Modul daxilində cohesion nə qədər yüksəkdirsə, modullar arasında coupling adətən bir o qədər aşağı olur. Yaxşı dizayn edilmiş sistem eyni anda daxildə yüksək bağlılığa və xaricdə zəif bağlılığa can atır. Bu qayda 1970-ci illərdən bəri proqram mühəndisliyində fundamental hesab olunur.
Cohesion-coupling nisbəti tarazlıq kimi təsvir edilə bilər. Tərtibatçı bir sinifdə bir neçə tapşırığı birləşdirərək cohesionu qurban verirsə, qonşu modullar daha çox asılılıq əldə edir — onlar müxtəlif məqsədlər üçün bu yüklü sinfə müraciət etməli olur, bu da couplingi artırır. Və əksinə, kiçik yüksək bağlı siniflərə bölmək modullar arasında qarşılıqlı təsir nöqtələrinin sayını azaldır.
Praktikada bu o deməkdir: funksional bağlılığa malik yeni bir sinif ayırdıqda, digər modulları onun tətbiq detallarını bilmək zörrurətindən azad edirsiniz. Məsələn, EncryptionManager-i funksional bağlılığa malik ayrı sinifə ayıraraq, digər modullara şifrələmə alqoritminin detallarını anlamaq zörrurəti olmadan sadə encrypt/decrypt interfeysi verirsiniz.
// Aşağı bağlılıq — sinif hər şeyi birdən edir
class UserManager {
fun fetchAndSaveUser(id: String) { }
fun parseUserJson(json: String): User { }
fun displayUserName(user: User): String { }
fun validateEmail(email: String): Boolean { }
}
// Yüksək bağlılıq — hər sinif bir tapşırığı həll edir
class UserRepository {
fun fetchUser(id: String): User { }
}
class UserJsonParser {
fun parse(json: String): User { }
}
class UserNameFormatter {
fun format(user: User): String { }
}
class EmailValidator {
fun isValid(email: String): Boolean { }
}
Nümunə fərqi göstərir: UserManager məntiqi bağlılığa malikdir — bütün metodlar istifadəçilərlə bağlıdır, lakin hər biri prinsipcə fərqli iş görür. Refaktorinqdən sonra hər sinif funksional bağlılığa malikdir və coupling azalır, çünki digər modullar yalnız ehtiyac duyduqları sinifdən asılıdır, bütöv UserManager-dən yox.
LCOM (Lack of Cohesion of Methods) — sinif bağlılığını ölçmək üçün ən tanınmış metrikadır. LCOM ümumi sahələri istifadə etməyən metod cütlərinin sayını hesablayır. 0 dəyəri ideal bağlılıq deməkdir (bütün metodlar eyni sahələrlə işləyir), yüksək dəyər — aşağı bağlılıqdır. LCOM4 (təkmilləşdirilmiş versiya) digər metodlar vasitəsilə ötümlü əlaqələri nəzərə alır.
Android inkişafında bağlılıq metrikalarını TooManyFunctions qaydası ilə Detekt vasitəsilə əldə etmək olar. Müxtəlif sahə qruplarından istifadə edən onlarla metodu olan siniflər çox gözəl aşağı bağlılığa malikdir. iOS-da SwiftLint-in file_length və function_body_length qaydaları var — dolayı göstəricilər: uzun fayllar və metodlar tez-tez aşağı cohesionu göstərir.
Əl qiymətləndirmə üsulu: „Bu sinif bir səbəbdən və ya bir neçə səbəbdən dəyişəcək?” sualını verin. Birdən çox müstəqil səbəb adlandıra bilirsinizsə — sinif aşağı bağlılığa malikdir. İkinci test: „Bu sinifi iki müstəqil sinifə bölmək olarmı?” Əgər bəli — edin. Code review zamanı cohesionun mütəzəmi yoxlanılması God siniflərinin yaranmasının qarşısını alır və texniki borcu azaldır.
İlk addım — Single Responsibility Principle tətbiq edin. Hər bir sinifin bir aydın məsuliyyəti olmalıdır. Sinifdə onun əsas tapşırığına aid olmayan bir metod varsa, onu ayrı sinifə çıxarın. IDE-də Extract Class və ya Extract Delegate texnikası bu prosesi avtomatlaşdırır. Ayırdıqdan sonra orijinal sinfin daha fokuslanmış olub-olmadığını yoxlayın.
İkinci addım — interfeysi sadələşdirmək üçün Facade naxışından istifadə edin. Sinif 20 metod təqdim edirsə və müştərilər yalnız 3-4-dünü istifadə edirsə, çox gözəl sinif aşağı bağlılığa malikdir — o, çox müxtəlif funksionallıq təklif edir. Metodları mövzulara görə qruplaşdırın və hər qrup üçün ayrı siniflər ayırın, orijinal sinfi isə fasad edin və ya silin.
Üçüncü addım — sahə qruplarına diqqət yetirin. Sinifdə yalnız metodların bir hissəsi tərəfindən istifadə edilən sahələr varsa — bu aşağı cohesion göstəricisidir. Sinifi sahə qruplarına görə bölün. Məsələn, sinifdə userRepository, networkClient və analyticsTracker sahələri varsa, lakin birinci qrup metodlar yalnız userRepository, ikincisi isə networkClient istifadə edirsə — bunlar iki fərqli sinifdir.
Dördüncü addım — təsadüfi static metodları olan „utility” sinifləri yaratmaqdan çəkinin. Utils və ya Helpers sinifindəki hər bir static metod ixtisaslaşdırılmış sinifə ayrılmağa namizəddir. FormatUtils.dateToString-i DateFormatter-ə, ValidationUtils.isValidEmail-i isə EmailValidator-ə köçürmək daha yaxşıdır. Bu, hər sinifin cohesionunu artırır və kodu özünə sənədləşdirən edir.
Tez-tez verilən suallar
Demək olar ki, həmişə. Funksional bağlılıq kodu başa düşülən və proqnozlaşdırıla bilən edir. Lakin həddən artıq ifrata varmaq həddindən artıq parçalanmaya səbəb ola bilər: hər əməliyyat üçün ayrı sinif yaradılır və arxitektura lazımsız dərəcədə mürəkkəbləşir. Tarazlıq — hər biri funksional bağlılığa malik bir neçə sinif.
Cohesion — bir modul və ya sinifin daxili uyğunluğunun metrikasıdır. Modularity — tətbiqin fiziki modullara bölündüyü arxitektura prinsipidir. Yüksək cohesion həm ayrı-ayrı siniflərin, həm də bütöv modulların layihələndirilməsində məqsəddir.
Detekt Android üçün və Xcode Analyzer iOS üçün şûbhəli dərəcədə çox metod və ya sahəsi olan sinifləri vurğulayır. IntelliJ IDEA və AppCode asılılıq vizuallaşdırmasına malikdir — əlaqələr qrafikini görə və aşağı bağlılığa malik sinifləri aşkar edə bilərsiniz. SonarQube LCOM metrikalarını avtomatik hesablayır.
Bəli. connect, disconnect və isConnected metodları olan interfeys yüksək bağlılığa malikdir — bütün metodlar əlaqə idarəetməsinə aiddir. connect, parseData və renderUI metodları olan interfeys aşağı bağlılığa malikdir. Interface Segregation (SOLID) prinsipi yüksək bağlılığa malik ixtisaslaşdırılmış interfeyslər yaratmağı tələb edir.
Üç sual verin: sinfin məqsədini bir cümle ilə təsvir etmək olarmı? Bütün metodlar bu məqsədi dəstəkləyirmi? Sinifdə metodların bir hissəsi tərəfindən istifadə edilməyən sahələr varmı? Hər hansı suala cavab mənfi olarsa — cohesion aşağıdır və sinif bölünməlidir.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun