CodeGym /Kurslar /JAVA 25 SELF /İnkapsulyasiya prinsipləri: niyə lazımdır

İnkapsulyasiya prinsipləri: niyə lazımdır

JAVA 25 SELF
Səviyyə , Dərs
Mövcuddur

1. İnkapsulyasiyanın tərifi

İnkapsulyasiya — obyekt-yönümlü proqramlaşdırmanın (OOP) fundamental prinsiplərindən biridir. Sadə dillə desək, inkapsulyasiya — obyektin daxili hissələrini “gizlətmək” və onlara yalnız xüsusi nəzərdə tutulmuş “qapılar” — publik metodlar vasitəsilə giriş vermək bacarığıdır.

Müasir bir qəhvə maşınını təsəvvür edin. İstifadəçi yalnız düymələri və displeyi görür — onun üçün içəridə boilerin, pompanın və boruların necə qurulduğunu bilmək lazım deyil. “Kapuçino” düyməsini basır — və nəticəni alır. İçəridə nə varsa, gizlidir. Bax, bu — inkapsulyasiyadır!

Java-da (və digər OOP dillərində) inkapsulyasiya aşağıdakılar hesabına əldə edilir:

  • Məlumatların gizlədilməsi — sinfin sahələri private kimi elan olunur (heç olmasa public olmur).
  • Publik interfeys — kənara yalnız obyekt istifadəçisinə həqiqətən lazım olan metodlar “çıxarılır”.

Sxem: inkapsulyasiya necə görünür

+-------------------------------+
|         Sinif Student         |
|-------------------------------|
| - name: String                |  // private sahə
| - age: int                    |  // private sahə
|-------------------------------|
| + getName(): String           |  // public metod
| + setName(String): void       |  // public metod
| + getAge(): int               |  // public metod
| + setAge(int): void           |  // public metod
+-------------------------------+

Burada - işarəsi private (gizli), + isə public (kənardan əlçatan) deməkdir.

Getter və setter nədir?

İnkapsulyasiyanın niyə lazım olduğunu izah etməzdən əvvəl, tez bir baxaq: gettersetter — sinfin private sahələri ilə “ünsiyyət” qurmağa kömək edən xüsusi metodlardır.

Getter (getter) — private sahənin dəyərini alan metoddur. Adətən getFieldName() kimi adlandırılır.

Setter (setter) — private sahənin dəyərini təyin edən metoddur. Adətən setFieldName(value) kimi adlandırılır.

Sadə nümunə:

public class Student {
    private String name; // private sahə — çöldən görünmür
    
    // Getter — "mənə tələbənin adını ver"
    public String getName() {
        return name;
    }
    
    // Setter — "tələbənin adını təyin et"
    public void setName(String name) {
        this.name = name;
    }
}

Bu necə işləyir:

Student student = new Student();
student.setName("Vasya");           // adı setter vasitəsilə təyin edirik
String name = student.getName();   // adı getter vasitəsilə alırıq

Getter və setterləri obyektə “nəzakətli xahişlər” kimi düşünün: obyektin “cibinə” əl atmaq (student.name = "Vasya") əvəzinə nəzakətlə xahiş edirik: “Zəhmət olmasa, adı təyin et” (student.setName("Vasya")).

Bir az qabağa gedərək: bir neçə mühazirədən sonra getter və setterləri ətraflı öyrənəcək, onların sirlərini biləcək və tam gücü ilə istifadə etməyi bacaracağıq. Hələlik əsas ideyanı anlamaq kifayətdir!

2. İnkapsulyasiya niyə lazımdır?

Məlumatları səhv istifadədən qorumaq

Əgər sinfin bütün sahələri publik (public) olsaydı, istənilən xarici kod onların dəyərlərini istədiyi kimi birbaşa dəyişə bilərdi:

Student s = new Student();
s.age = -1000; // Ay, vampir-tələbə!

Bu təhlükəlidir! Proqramınız gözlənilməz davranmağa başlaya bilər və xətalar ən ummadığınız yerlərdə üzə çıxar.

Daxili reallaşdırmanı xarici koda təsir etmədən dəyişmək imkanı

İnkapsulyasiya sinfin daxili quruluşunu pozmadan ondan istifadə edən kodu sındırmadan dəyişməyə imkan verir. Məsələn, məlumatın saxlanma üsulunu dəyişə və ya metodlarda yoxlama (validasiya) əlavə edə bilərsiniz və sinfin istifadəçiləri bunu hiss etməyəcəklər — onlar yenə də eyni metodları çağıracaqlar.

Kodun oxunaqlılığını və dəstəyini yaxşılaşdırmaq

Daxili detalları gizlədərkən, xarici interfeys daha səliqəli və anlaşılan olur. Sizin sinfinizi istifadə edən proqramçı onun içəridə necə işlədiyini anlamağa məcbur deyil — hansı metodların əlçatan olduğunu və nə etdiklərini bilməsi kifayətdir.

Həyatdan nümunə

Smartfondan necə istifadə etdiyinizi xatırlayın. Ekrana toxunuşların necə emal olunması, akkumulyatorun quruluşu və ya rabitə modulunun necə işləməsi barədə düşünmürsünüz. Siz sadəcə aydın interfeys vasitəsilə (ikonlar, düymələr) lazım olan funksiyaları çağırırsınız. İstehsalçı daxili reallaşdırmanı dəyişsə belə, bunu hiss etməyəcəksiniz.

Koddakı real nümunə

Tutaq ki, bizdə BankAccount sinfi var. Proqramın köhnə versiyasında balans nöqtə-ayırıcılarla sətir kimi saxlanırdı, məsələn "1.000.50". Sonra proqramçılar balansı double tipli ədəd kimi saxlamağa qərar verdilər. Əgər sahə publik olsaydı, birbaşa account.balance-a müraciət edən bütün köhnə kod xarab olardı.

Amma inkapsulyasiyadan istifadə edib sahəni gizlədir və yalnız deposit()getBalance() metodlarını təqdim ediriksə, xarici kod dəyişikliklərdən xəbərsiz qalacaq:

public class BankAccount {
    private double balance; // sahə gizlidir

    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    public double getBalance() {
        return balance;
    }
}

İndi əgər sabah balansı, məsələn, sentlərlə (long) saxlamaq istəsək, sinfin yalnız daxili reallaşdırmasını dəyişmək kifayətdir, deposit()getBalance() çağıran qalan bütün kod əvvəlki kimi işləməyə davam edəcək.

3. Yaxşı və pis inkapsulyasiya nümunələri

Pis nümunə: publik sahələr

public class Student {
    public String name;
    public int age;
}

Bu yanaşmanın problemləri:

  • İstənilən kod sahələrə istənilən, hətta yalnış dəyərlər təyin edə bilər.
  • Məlumatı yoxlamaq (validasiya) imkanı yoxdur.
  • Sahənin tipini və ya strukturunu dəyişməyə qərar versəniz, ondan istifadə edən bütün kodu dəyişməli olacaqsınız.

Yaxşı nümunə: private sahələr və public metodlar

public class Student {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        // Burada yoxlama əlavə etmək olar!
        if (name == null || name.isEmpty()) {
            throw new IllegalArgumentException("Ad boş ola bilməz");
        }
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        // Yoxlayırıq ki, yaş mənfi olmasın
        if (age < 0) {
            throw new IllegalArgumentException("Yaş mənfi ola bilməz");
        }
        this.age = age;
    }
}

Üstünlüklər:

  • Xarici kod sahələri birbaşa dəyişə bilmir — yalnız metodlar vasitəsilə.
  • Yoxlamalar, loglar, avtomatik hərəkətlər (məsələn, statistikanın yenilənməsi) əlavə etmək olar.
  • Daxili təqdimat dəyişsə (məsələn, yaş başqa formatda saxlanılsa belə), xarici interfeys eyni qalacaq.

İstifadədə bu necə görünür

Student s = new Student();
s.setName("Alisa");
s.setAge(20);

System.out.println(s.getName() + ", yaş: " + s.getAge());

Mənfi yaş verməyə cəhd edin — icra mərhələsində elə oradaca xəta alacaqsınız! Proqramınız yersiz səhvlərdən qorunur.

4. OOP-nin digər prinsipləri ilə əlaqə

İnkapsulyasiya OOP-nin digər bütün prinsiplərinin “anası”dır. Onsuz nə irsiyyət (inheritance), nə polimorfizm, nə də abstraksiya olardı. Biz onları bir az sonra öyrənəcəyik, hələlik isə qısaca qeyd edək:

  • İrsiyyət (extends) mövcud siniflər əsasında yeni siniflər yaratmağa, onların davranışını genişləndirməyə və ya dəyişməyə imkan verir. Əgər sinfin daxili hissələri açıq olsaydı, törəmə sinif təsadüfən vacib bir şeyi poza bilərdi.
  • Polimorfizm (fərqli siniflərin obyektlərinin eyni mesajlara fərqli reaksiya verməsi) daxili reallaşdırma ilə xarici interfeys arasında dəqiq sərhəd olmadan mümkün deyil.
  • Abstraksiya — obyektin yalnız mühüm xüsusiyyətlərini ayırmaq və detalları gizlətməkdir. İnkapsulyasiya abstraksiyanı praktikada həyata keçirməyə kömək edir.

Oxşatma

Avtomobili təsəvvür edin. Sürücü üçün yalnız sükan, pedallar və qollar əlçatandır — bu interfeysdir. Qalan hər şey (mühərrik, sürətlər qutusu, elektronika) — kapotun altındadır. Sürücü mühərrikin hər vintini birbaşa idarə edə bilsəydi, qəzalar çox daha tez-tez baş verərdi!

5. Praktik nümunə: tətbiqimizdə inkapsulyasiya

Tədris tətbiqimizi inkişaf etdirək — məsələn, “Ünvan kitabı”. Qoy bizdə ad və telefonu saxlayan Contact sinfi olsun.

İnkapsulyasiyasız (anti-nümunə):

public class Contact {
    public String name;
    public String phone;
}

İstifadə:

Contact contact = new Contact();
contact.name = ""; // Ay! Ad boşdur
contact.phone = null; // Telefon verilməyib

İnkapsulyasiya ilə (düzgün yanaşma):

public class Contact {
    private String name;
    private String phone;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("Kontaktın adı boş ola bilməz");
        }
        this.name = name;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        if (phone == null || phone.isBlank()) {
            throw new IllegalArgumentException("Telefon boş ola bilməz");
        }
        this.phone = phone;
    }
}

İndi xarici kod boş ad və ya telefon qoya bilməz:

Contact contact = new Contact();
contact.setName("Ivan");
contact.setPhone("+1-999-123-45-67");

Boş ad təyin etməyə cəhd etsəniz — proqram xəta atacaq.

6. Faydalı incəliklər

İnkapsulyasiya və kodun uzunmüddətli dəstəyi

Kiçik tədris layihəsində işləyərkən hər şeyi “şərəf sözü” ilə edə biləcəyiniz düşünülə bilər: axı kim mənfi yaş və ya boş ad təyin edər? Amma layihə böyüyən kimi, başqa proqramçılar qoşulan kimi, hətta siz özünüz də bir neçə ay sonra reallaşdırma detallarını unudanda — bax onda inkapsulyasiya xaosdan xilas edir.

  • Sinfin daxili hissələrini dəyişmək asandır — məsələn, telefonu sətir kimi deyil, PhoneNumber tipli obyekt kimi saxlamaq lazım gələrsə, reallaşdırmanı dəyişirsiniz və xarici koda toxunmursunuz.
  • Test etmək daha asandır — hamı yalnız metodlar vasitəsilə dəyişəndə, hansı məlumatların nə zaman dəyişdiyini izləmək rahatdır.
  • Daha az xəta — yalnış dəyərlərdən və təsadüfi dəyişikliklərdən qorunma.

Sual: getter və setter hər vaxt lazımdırmı?

Tez-tez yeni başlayanlar belə düşünür: “İnkapsulyasiya — private sahələr və publik getter/setterlərdirsə, deməli, hər sahə üçün getter və setter yazmaq lazımdır!”. Bu, tam olaraq belə deyil.

  • Bəzən sahə yalnız oxumaq üçündür (məsələn, obyektin unikal identifikatoru). O zaman yalnız getter yazın.
  • Bəzən sahəni ümumiyyətlə “kənara çıxarmağa” ehtiyac yoxdur — o halda nə getter, nə setter yazmayın.
  • Əgər sahənin dəyərini yalnız sinfin içində dəyişmək olmalıdırsa, setter-i private etmək olar.

Qızıl qayda: yalnız həqiqətən xarici koda lazım olan məlumatları və metodları açın.

Vizualizasiya: yanaşmaların müqayisəsi

Yanaşma Sahəyə çıxış nümunəsi Nəzarət imkanı Təhlükəsizlik
public sahələr
obj.field = value;
Xeyr Aşağı
private sahələr + metodlar
obj.setField(value);
Bəli Yüksək

7. İnkapsulyasiya ilə işləyərkən tipik səhvlər

Səhv № 1: Sinfin bütün sahələri public elan olunub. Bu, yeni başlayanlarda ən çox rast gəlinən xətadır. Belə kod tez bir zamanda idarəolunmaz olur: istənilən şəxs icazəniz olmadan istənilən məlumatı dəyişə bilər. Belə etməyin — vaxtınıza qənaət etmək istəsəniz belə!

Səhv № 2: Yoxlama və məntiq olmadan getter və setterlər. Əgər giriş metodları yaradırsınızsa, onları validasiya üçün istifadə edin: yalnış dəyərlərə icazə verməyin. Sadəcə parametrin dəyərini sahəyə “köçürmək” həmişə ən yaxşı seçim deyil.

Səhv № 3: Daxili strukturu vaxtından əvvəl açmaq. Əgər “birdən lazım olar” deyə bütün sahələr üçün qabaqcadan getter/setter yazırsınızsa, sonradan dəyişməsi çətin olacaq çox detalı açma riski daşıyırsınız.

Səhv № 4: Dəyişən obyektləri birbaşa qaytarmaq. Əgər sahə dəyişən obyektirsə (məsələn, siyahı), onu getter vasitəsilə birbaşa qaytarmayın. Daha yaxşısı, onun surətini qaytarın və ya onu dəyişməz edin.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION