Coupling (bağlılıq) mobil inkişafda — əsas anlayışlar, növlər və necə azaltmaq olar

Müəllif: IT Sectr Dərc olunub: 2026-05-13 Oxuma vaxtı: 9 dəq

Coupling (bağlılıq) — bir tətbiq modulunun digərindən nə dərəcədə asılı olduğunu göstərən metrikadır. Wikipedia-ya görə, zəif bağlılıq (low coupling) yaxşı dizayn edilmiş sistemin əlamətidir, burada modulları qonşuları pozmadan dəyişmək olar. Couplingin idarə edilməsi mobil tətbiqlərin layihələndirilməsində memarın əsas vəzifələrindən biridir.

Əsas məqamlar

  • Coupling — modullar arasında asılılıq dərəcəsi: yüksək = sərt bağlılıq, aşağı = zəif
  • Content coupling — ən pis növ, modul digər modulun daxili məlumatlarını dəyişdikdə
  • Data coupling — ən yaxşı növ, modullar yalnız parametrlər vasitəsilə sadə məlumat mübadiləsi aparır
  • Dependency Injection — mobil inkişafda couplingi azaltmaq üçün əsas vasitə
  • İnterfeyslər və abstraksiyalar — tətbiq təbəqələri arasında bağlılığı zəiflətmək üçün əsas mexanizm

Coupling nədir

Coupling (bağlılıq) — bir modulun və ya sinfin digəri ilə nə dərəcədə əlaqəli olduğunu müəyyən edən metrikadır. Bir modul digərinin daxili quruluşu haqqında nə qədər çox bilirsə, coupling bir o qədər yüksəkdir və sistemi dəyişmək bir o qədər çətinləşir. Yaxşı dizayn edilmiş arxitekturada coupling minimal olmalıdır — modullar yalnız ciddi müəyyən edilmiş interfeyslər vasitəsilə qarşılıqlı əlaqədə olurlar.

Couplingin iki tərəfi fərqləndirilir: afferent (daxil olan asılılıqlar — neçə modul veriləndən asılıdır) və efferent (çıxan asılılıqlar — verilən modul neçə moduldan asılıdır). Bu metrikaların təhlili arxitekturada "isti nöqtələri" müəyyən etməyə imkan verir, burada bir modulun dəyişməsi çoxlu digərlərinə təsir edəcək. IntelliJ Dependency Analyzer və Xcode Graph kimi alətlər bu əlaqələri vizuallaşdırır.

Anlamaq vacibdir ki, sıfır coupling mümkün deyil — modullar bir şəkildə qarşılıqlı əlaqədə olmalıdır, əks halda bu sistem deyil, təcrid olunmuş proqramlar toplusudur. Memarın vəzifəsi couplingi idarə edilə bilən və şəffaf etməkdir. İdeal: modullar yalnız interfeyslər vasitəsilə qarşılıqlı əlaqədə olur və yalnız sadə məlumatlar ötürür, bir-birinin daxili quruluşu haqqında bilmədən. Buna loose coupling (zəif bağlılıq) deyilir.

Bağlılıq növləri zəifdən güclüyə

Altı növ coupling ən yaxşıdan ən pisə qədər miqyas təşkil edir. Bu miqyası başa düşmək mövcud kodu qiymətləndirməyə və refaktoring istiqamətini seçməyə kömək edir. Əksər mobil layihələr qarışıq coupling növlərinə malikdir və memarın vəzifəsi güclü növləri ardıcıl olaraq zəiflərlə əvəz etməkdir.

Data coupling — ən yaxşı növ

Data coupling (məlumat bağlılığı) — modullar yalnız metod parametrləri vasitəsilə sadə məlumat mübadiləsi aparır. A modulu B modulunun metodunu çağırır, primitivlər və ya sadə strukturlar ötürür və nəticə alır. A modulu B-nin daxildə necə reallaşdırıldığını bilmir. Bu ən arzuolunan coupling növüdür: dəyişikliklərin nəticələrini minimallaşdırır.

Nümunə: EmailValidator.isValid(email: String): Boolean. İstehlakçı sinif sətir ötürür və Boolean alır, validator daxilində müntəzəm ifadələr və ya validasiya qaydaları haqqında təsəvvürü olmadan. Validasiya məntiqinin dəyişməsi istehlakçının dəyişməsini tələb etmir — coupling minimaldır. Data coupling tətbiqdəki bütün ictimai interfeyslər üçün hədəfdir.

Stamp coupling — məqbul, lakin ideal deyil

Stamp coupling (struktur bağlılığı) — modullar mürəkkəb obyektlər mübadiləsi aparır, lakin onların sahələrinin yalnız bir hissəsini istifadə edir. A modulu calculateDiscount metoduna User obyektini ötürür, o isə yalnız user.status istifadə edir. Problem: User strukturu dəyişərsə (məcburi sahə əlavə olunarsa), calculateDiscount modulu dəyişməyəcək, lakin User obyektini yaradan istehlakçı dəyişəcək.

Təcrübədə stamp coupling qaçılmazdır və ötürülən obyekt standart məlumat modelidirsə (Entity), məqbuldur. Problem o zaman yaranır ki, modul yalnız bir sahə üçün bütöv obyekt alır. Belə hallarda konkret dəyəri birbaşa ötürmək daha yaxşıdır (data coupling). Həll yolu — qəbul edən tərəfin sahələrdən istifadəsini təhlil etməkdir.

Control, External, Common və Content coupling

Control coupling — bir modul digərinə davranışını idarə edən flag ötürür (calculate(useNewAlgorithm: Boolean)). Bu stamp couplingdən pisdir, çünki istehlakçı modul çağırılan modulun daxili iş variantlarını bilməlidir. Həll: metodu iki yerə bölmək — calculateWithNewAlgorithm() və calculateWithLegacyAlgorithm().

External coupling — modullar xarici protokoldan, məlumat formatından və ya API-dən asılıdır. Bir JSON-u pars edən və ya bir verilənlər bazası ilə işləyən bütün modullar external couplingə malikdir. Tamamilə qarşısını almaq mümkün deyil, lakin təcrid etmək olar: xarici format ilə daxili modellər arasında mapinq təbəqəsi yaratmaq. Common coupling — modullar ümumi qlobal vəziyyəti bölüşür. Content coupling — ən pis növ, modul birbaşa digər modulun daxili məlumatlarını dəyişdikdə.

Coupling növüSəviyyəTəsvir
DataƏn yaxşıParametrlər vasitəsilə sadə məlumat ötürülməsi
StampMəqbulQismən istifadə ilə obyekt ötürülməsi
ControlOrtaFlavlarla davranışın idarə edilməsi
ExternalYüksəkXarici protokol/formatdan asılılıq
CommonÇox yüksəkQlobal vəziyyətin bölüşdürülməsi
ContentYolverilməzModulun daxili məlumatlarının birbaşa dəyişdirilməsi

Coupling miqyası data (ideal) contentə (fəlakət) qədər — kod-revyu üçün praktik vasitədir. Layihədə common və ya content coupling görürsünüzsə — bu, refaktoringin prioritet hədəfidir. Data və stamp coupling məqbuldur və istənilən layihədə mövcuddur, lakin onların sayı nəzarət edilməlidir.

Niyə coupling mobil inkişafda kritikdir

Yüksək coupling inkişafı ləngidilmiş prosesə çevirir, burada hər dəyişiklik onlarla potensial pozulmuş modulun yoxlanılmasını tələb edir. Mobil inkişafda bu xüsusilə kritikdir: platformalar hər il yenilənir (Android API Level, iOS SDK), kitabxanalar — rüblük, biznes tələbləri isə fasiləsiz. Zəif bağlılıq — davamlı reqressiyalar olmadan bu dəyişiklik axınının öhdəsindən gəlməyin yeganə yoludur.

Təcrübədən nümunə: bütün ekranların birbaşa NetworkingManager və DatabaseManager-i idxal etdiyi mobil tətbiq. HTTP müştərisinin Retrofitdən Ktor-a (Android) və ya URLSession-dan Alamofire-ə (iOS) dəyişdirilməsi zamanı proqramçı hər ekranı düzəltməli olacaq. Aşağı couplingdə NetworkDataSource interfeysi arxasında gizlənmiş bir implementasiyanı dəyişmək kifayətdir — istehlakçılar dəyişikliyi hiss etməyəcək.

Couplingin vahid testlərə təsiri də böyükdür. Yüksək couplingə malik sinif (konstruktor vasitəsilə birbaşa asılılıqların yaradılması) təcrid olunmuş şəkildə test edilə bilməz — o, verilənlər bazası, şəbəkə və UI-ni özü ilə çəkir. Belə sinfi test etmək üçün emulatoru işə salmaq və inteqrasiya testlərini gözləmək lazımdır. Aşağı couplingə malik sinif asılılıqları constructor injection vasitəsilə qəbul edir və asanlıqla mock edilir.

kotlin
// Yüksək coupling — sinif öz asılılıqlarını özü yaradır
class ProfileViewModelHigh {
    private val api = RetrofitApi()
    private val db = RoomDatabase.getInstance()
    private val cache = MemoryCache()
}

// Aşağı coupling — asılılıqlar konstruktor vasitəsilə ötürülür
class ProfileViewModelLow(
    private val api: ApiService,
    private val db: DatabaseService,
    private val cache: CacheService
)

Birinci halda ProfileViewModelHigh konkret implementasiyalara sərt bağlıdır — Retrofitin Ktor ilə əvəz edilməsi ViewModel kodunun dəyişdirilməsini tələb edir. İkinci halda ProfileViewModelLow yalnız interfeyslərdən asılıdır, onların implementasiyaları kənardan təmin edilir. İkinci sinfin test edilməsi sadədir: mock-implementasiyalar ötürürük və emulator olmadan məntiqi yoxlayırıq.

Couplingi azaltmaq üçün patternlər

Dependency Inversion Principle (SOLID-də D) — couplingin azaldılması üçün əsasdır. Prinsip abstraksiyalardan asılı olmağı, konkret implementasiyalardan yox, tələb edir. Sinif birbaşa RetrofitApi obyekti yaratmaq əvəzinə, ApiService interfeysini almalıdır. Bu, əlaqəni konkret kitabxanadan istehlakçını dəyişmədən dəyişdirilə bilən abstraksiya səviyyəsinə köçürür.

Observer pattern (və ya onun reaktiv versiyaları — StateFlow, Combine Publishers) məlumat mənbəyi ilə abunəçilər arasında couplingi azaldır. Abunəçi məlumatların haradan gəldiyini bilmir — o, sadəcə dəyişikliklərə reaksiya verir. Bu, göndərəni və qəbul edəni ayırır: mövcud abunəçiləri dəyişmədən yeni məlumat mənbəyi əlavə etmək olar. EventBus və SharedFlow eyni prinsiplə işləyir.

Bridge pattern abstraksiyanı və implementasiyanı ayırır, onların müstəqil dəyişməsinə imkan verir. Mobil inkişafda Bridge, məsələn, platformadan asılı modullar üçün tətbiq edilir: iOS (Kingfisher, Nuke) və Android (Glide, Coil) üçün müxtəlif implementasiyaları olan ümumi ImageLoader interfeysi. ImageLoader ilə işləyən kod seçilmiş kitabxanadan asılı deyil və implementasiyanı sadəcə dəyişməklə onu əvəz edə bilər.

Dependency Injection couplingin idarə edilməsi vasitəsi kimi

Dependency Injection (DI) — mobil inkişafda couplingi azaltmaq üçün ən praktik vasitədir. Sinif öz asılılıqlarını müstəqil yaratmaq əvəzinə, DI-konteyner (Android üçün Hilt, Koin, Dagger; iOS üçün Swinject, Factory) onları kənardan təmin edir. Sinif asılılıqları constructor, method və ya property injection vasitəsilə alır, konkret implementasiyalardan xəbərsiz qalır.

DI sinfin asılılıqlarını açıq şəkildə sənədləşdirir: sinfin hansı modullarla qarşılıqlı əlaqədə olduğunu başa düşmək üçün konstruktora baxmaq kifayətdir. Konstruktor müxtəlif təbəqələrdən 8 parametr qəbul edərsə — bu, həddindən artıq couplingin siqnalıdır, refaktoring tələb edir. Yaxşı təcrübə — sinfə 3-4-dən çox olmayan asılılıq. Daha çox say Single Responsibility pozuntusunu və həddindən artıq couplingi göstərir.

DI həmçinin testləri sadələşdirir: hər test üçün sinfi mock-asılılıqlarla yaradırsınız, real verilənlər bazası və ya şəbəkə tələb olunmur. Flutter-də DI Provider, Riverpod və ya GetIt vasitəsilə reallaşdırılır. Freymvorkdan asılı olmayaraq məqsəd birdir: modullar arasında bağlılığı zəiflətmək, asılılıqları aşkar və dəyişdirilə bilən etmək. Mobil layihədə DI tətbiqi 2020-ci illərdən etibarən de-fakto standartdır.

swift
// DI-konteyner asılılıq qrafını qurur
protocol AuthServiceProtocol {
    func login(email: String, password: String) async throws -> User
}

final class AuthService: AuthServiceProtocol {
    func login(email: String, password: String) async throws -> User {
        // reallaşdırma
    }
}

// ViewModel konkret servis haqqında bilmir — yalnız protokol
final class LoginViewModel {
    private let auth: AuthServiceProtocol

    init(auth: AuthServiceProtocol) {
        self.auth = auth
    }
}

// DI Container — konkret tiplərin yaradıldığı yeganə yer
final class DIContainer {
    lazy var authService: AuthServiceProtocol = AuthService()
    lazy var loginViewModel: LoginViewModel {
        LoginViewModel(auth: self.authService)
    }
}

Burada LoginViewModel yalnız AuthServiceProtocol protokolundan asılıdır, konkret AuthService-dən yox. Implementasiyanın dəyişdirilməsi (məsələn, Firebase Auth-dan öz serverinə keçid) yalnız DIContainer-də dəyişiklik tələb edir. AuthServiceProtocol-un bütün istehlakçıları toxunulmaz qalır — coupling abstraksiya və DI vasitəsilə minimuma endirilmişdir.

Tez-tez verilən suallar

Coupling cohesion-dan nə ilə fərqlənir?

Cohesion modulun daxili uyğunluğunu ölçür, coupling — modullar arasında xarici bağlılığı. Yaxşı arxitektura yüksək cohesion və aşağı couplingə can atır. Bu metrikalar tərs mütənasibdir: cohesionin artması adətən couplingi azaldır və əksinə.

Hansı coupling növü istehsalat kodunda məqbuldur?

Data və stamp — norma və istənilən layihədə mövcuddur. Control coupling məhdud ssenarilərdə (məsələn, strategy pattern) məqbuldur. External coupling xarici API-lərlə işləyərkən qaçılmazdır, lakin mapinq təbəqəsi arxasında təcrid edilməlidir. Common və content coupling — dərhal refaktoring tələb edən arxitektura problemlərinin əlamətləridir.

Layihədə couplingi necə ölçmək olar?

Statik analiz alətləri: IntelliJ IDEA Dependency Matrix, Xcode Graph, Gradle Dependencies report, SonarQube. Metrikalar: afferent coupling (Ca), efferent coupling (Ce), Instability (Ce/(Ca+Ce)). Yüksək Instability (1-ə yaxın) o deməkdir ki, modul asanlıqla dəyişdirilir və ona az istinad edilir — bu yaxşıdır.

Aşağı coupling zərərli ola bilərmi?

Həddindən artıq aşağı coupling kodu gəzməyi çətinləşdirən həddindən artıq abstraksiya və interfeyslər mənasına gələ bilər. Hər sinif üçün ayrı interfeys yaradılıbsa, proqramçı fayllar arasında tullanmağa vaxt itirir. Balans: modulun xarici API-si üçün interfeyslər, lakin hər daxili köməkçi sinif üçün yox.

Legacy kodla işləyərkən couplingi necə azaltmaq olar?

Strangler Fig texnikasından istifadə edin — birbaşa çağırışları interfeyslər vasitəsilə tədricən əvəz edin. Ən çox müraciət edilən siniflər üçün extract interface ilə başlayın. Sonra DI-konteyner tətbiq edin. Təcrid olunan kodu Characterisation-testlərlə əhatə edin ki, refaktoringin sistem davranışını dəyişmədiyinə əmin olun.

Nəticə

  • Coupling — modullar arasında asılılıq metrikası: zəif bağlılıq yaxşı arxitekturanın məqsədidir
  • Data coupling — ən yaxşı növ, content coupling — ən pis, istehsalat kodunda yolverilməz
  • Dependency Inversion və interfeyslər — bağlılığı zəiflətməyin əsas mexanizmləri
  • Dependency Injection — asılılıqları aşkar və dəyişdirilə bilən edən praktik vasitə
  • Yüksək coupling kodu kövrək edir: bir dəyişiklik çoxlu modulları pozur
  • Aşağı coupling testləri sadələşdirir: hər modul emulator olmadan müstəqil mock edilir
  • Balanslayın coupling və abstraksiyalar arasında — həddindən artıq interfeyslər kodu çətinləşdirir

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.

Layihəni müzakirə et

Həm də oxuyun