1. Çoxsəviyyəli abstraksiya
Proqramlaşdırmaya yeni başlayanda hər şey sadə görünür: sinif yazdın, metodu çağırdın, nəticəni aldın. Amma real layihələrdə hər şey mürəkkəbləşir: onlarla sinif, yüzlərlə metod, minlərlə sətir kod... Üstəlik, layihə üzərində komanda işləyirsə — vəzifə daha da çətinləşir! Bu xaosda necə batmamaq olar?
Cavab — mürəkkəbi sadəyə bölmək, daha da yaxşısı — abstraksiya səviyyələrinə.
Abstraksiya səviyyələri nədir?
Bunu böyük binanın mərtəbələri kimi təsəvvür edin: hər mərtəbədə öz həyat var, amma bütün mərtəbələr bir-biri ilə əlaqəlidir. Proqramlaşdırmada belə qatları (səviyyələri) ayırmaq qəbul olunub:
- İstifadəçi interfeysi (UI) — istifadəçinin gördüyü hissə.
- Biznes məntiqi — tətbiqin mahiyyətini reallaşdıran qaydalar və proseslər.
- Məlumatlara çıxış (DAO, Repository) — verilənlər bazası və ya fayllarla iş.
Hər qat digər qatların detalları barədə bilmədən abstraksiyalarla işləyir. Məsələn, biznes məntiqi üçün istifadəçi interfeysinin necə reallaşdırıldığı və ya məlumatların necə saxlanıldığı vacib deyil — onun üçün vacib olan saveOrder() və ya findUserById() kimi metodların olmasıdır.
Həyatdan analoji
Restoranı təsəvvür edin. Qonaqlar (UI) sifarişi ofisiant vasitəsilə (interfeys abstraksiyası) edirlər, aşpaz (biznes məntiqi) yeməyi hazırlayır, anbarçı (məlumatlara çıxış) isə anbarda məhsulların mövcudluğuna nəzarət edir. Qonaqlar yeməyin necə hazırlandığını bilmirlər, aşpaz isə kartofun harada olduğunu araşdırmır — əsas odur ki, o, əl altında olsun.
2. Nümunə: çoxsəviyyəli arxitektura praktikada
Gəlin tədris layihəmizi inkişaf etdirək — məsələn, tapşırıqların uçotu üçün tətbiq (task manager). Artıq tapşırıqlar üçün siniflər yaratmağı bilirik, indi mənzərəni çətinləşdirək və tətbiqi qatlara bölək.
Abstraksiyaları ayırırıq
- Task — tapşırığın abstrakt təsviri: tapşırığın adı, statusu və icrası üçün metodlar var.
- TaskRepository — tapşırıqların saxlanması üçün abstraksiya (harada olmasının fərqi yoxdur — yaddaşda, faylda, verilənlər bazasında).
- TaskService — biznes məntiqi: tapşırıq əlavə etmə, axtarış, icra.
Abstrakt siniflər və interfeyslər
// Biznes məntiqi səviyyəsi
public abstract class Task {
private String title;
private boolean completed;
public Task(String title) {
this.title = title;
this.completed = false;
}
public abstract void complete();
public String getTitle() { return title; }
public boolean isCompleted() { return completed; }
protected void setCompleted(boolean completed) { this.completed = completed; }
}
// Məlumatların saxlanması səviyyəsi (abstraksiya)
public interface TaskRepository {
void save(Task task);
Task findByTitle(String title);
List<Task> findAll();
}
Qatların reallaşdırılması
Task reallaşdırması
public class WorkTask extends Task {
private String deadline;
public WorkTask(String title, String deadline) {
super(title);
this.deadline = deadline;
}
@Override
public void complete() {
setCompleted(true);
System.out.println("İş tapşırığı '" + getTitle() + "' son tarixədək yerinə yetirildi: " + deadline);
}
}
TaskRepository reallaşdırması
public class InMemoryTaskRepository implements TaskRepository {
private List<Task> tasks = new ArrayList<>();
@Override
public void save(Task task) {
tasks.add(task);
}
@Override
public Task findByTitle(String title) {
for (Task task : tasks) {
if (task.getTitle().equals(title)) {
return task;
}
}
return null;
}
@Override
public List<Task> findAll() {
return new ArrayList<>(tasks);
}
}
TaskService reallaşdırması
public class TaskService {
private TaskRepository repository;
public TaskService(TaskRepository repository) {
this.repository = repository;
}
public void addTask(Task task) {
repository.save(task);
}
public void completeTask(String title) {
Task task = repository.findByTitle(title);
if (task != null) {
task.complete();
} else {
System.out.println("Tapşırıq tapılmadı: " + title);
}
}
public void showAllTasks() {
for (Task task : repository.findAll()) {
System.out.println(task.getTitle() + " — " + (task.isCompleted() ? "yerinə yetirilib" : "yerinə yetirilməyib"));
}
}
}
Əsas sinifdə istifadə
public class Main {
public static void main(String[] args) {
TaskRepository repo = new InMemoryTaskRepository();
TaskService service = new TaskService(repo);
service.addTask(new WorkTask("Hesabatı hazırlamaq", "2025-07-15"));
service.addTask(new WorkTask("Təqdimatı hazırlamaq", "2025-07-16"));
service.showAllTasks();
service.completeTask("Hesabatı hazırlamaq");
service.showAllTasks();
}
}
Nə əldə etdik?
- Əsas sinif (Main) tapşırıqların saxlanmasının necə qurulduğunu bilmir — o, TaskRepository abstraksiyası ilə işləyir.
- TaskService hansı tapşırıq növlərinin olduğunu bilmir — o, Task abstrakt sinfi ilə işləyir.
- Əgər sabah tapşırıqları yaddaşda deyil, verilənlər bazasında saxlamaq istəsək — biznes məntiqini və UI-ni yenidən yazmadan sadəcə yeni DatabaseTaskRepository sinfini reallaşdırırıq.
- Əgər yeni tapşırıq tipi yaransa, məsələn, HomeTask, — sadəcə yeni alt sinif əlavə edirik.
3. Komanda işi üçün üstünlüklər
Böyük layihələrdə nadir hallarda bir nəfər hər şeyi yazır. Adətən komanda “front-endçilər”, “back-endçilər”, “saxlama üzrə tərtibatçılar” və s. kimi bölünür. Abstraksiyalar onların bir-birinin işinə mane olmamasına necə kömək edir?
Məsuliyyətin bölüşdürülməsi
Hər kəs öz abstraksiya səviyyəsində işləyir.
- Bir tərtibatçı verilənlər bazası ilə işləmək üçün TaskRepository reallaşdırmasını yazır.
- Başqası biznes məntiqi ilə məşğul olur (TaskService).
- Üçüncü şəxs istifadəçi interfeysini hazırlayır.
Qatlar arasındakı kontrakt abstraksiyalarla təsbit olunur.
Hamı TaskRepository-də save, findByTitle, findAll metodlarının olduğuna razıdırsa — reallaşdırma detalları vacib deyil.
Testləmənin və komponentləri əvəz etməyin asanlığı
- Bir reallaşdırmanı digərilə asanlıqla əvəz etmək olar (məsələn, testlər üçün InMemoryTaskRepository, production üçün isə verilənlər bazası ilə işi istifadə etmək).
- Testçi biznes məntiqini təcrid olunmuş şəkildə test etmək üçün məlumat qatını “stub” (mock) ilə əvəz edə bilər.
Müstəqil inkişaf
- Kimsə yeni tapşırıq tipi əlavə etmək istəsə, mövcud kodu sındırmır — sadəcə Task-in yeni alt sinfini reallaşdırır.
- Məlumatların saxlanmasının yeni üsulu meydana çıxarsa, yalnız interfeysin reallaşdırması dəyişir, qalan kod toxunulmaz qalır.
4. Best practices: abstraksiyalarla ifrata varmamaq üçün
Abstraksiya — yeməkdəki duz kimidir: onsuz dadsızdır, amma çox olsa — hər şeyi korlayar. Bir neçə məsləhət:
Abstraksiyalardan həqiqətən sistemi sadələşdirdiyi yerlərdə istifadə edin.
Sadəcə abstrakt sinif olsun deyə abstrakt sinif yaratmağa dəyməz. Yalnız bir tip tapşırığınız varsa, bəlkə də abstraksiyaya ehtiyac yoxdur.
Abstrakt sinifləri və metodları sənədləşdirin.
Yaxşı sənədləşmə varisin dəqiq nəyi reallaşdırmalı olduğunu və bunun nə üçün lazım olduğunu anlamağa kömək edir.
Abstraksiyaların mənalı olmasına çalışın.
Abstrakt sinif həqiqətən ümumi davranışı və/və ya vəziyyəti ifadə etməlidir.
Məsuliyyəti qarışdırmayın.
Abstrakt sinifə yalnız bir varisə lazım olan metodları əlavə etməyin.
5. Böyük sistemlərdə abstraksiya: həyatdan nümunə
Abstraksiyanın həqiqətən böyük bir layihədə necə işlədiyinə baxaq — məsələn, onlayn mağazada.
Sistemin səviyyələri
- Kontrollerlər (UI): istifadəçi sorğularını qəbul edir (məsələn, “sifarişi rəsmiləşdirmək”).
- Servislər (biznes məntiqi): məhsulun mövcudluğunu yoxlayır, endirimləri hesablayır, sifarişi rəsmiləşdirir.
- Repository-lər (məlumatlara çıxış): sifarişləri, məhsulları, istifadəçiləri verilənlər bazasına saxlayır.
Abstraksiyalar nümunəsi
// Sifarişlərin emalı servisi üçün abstraksiya
public interface OrderService {
void createOrder(Order order);
Order findOrderById(String id);
}
// Sifariş anbarı üçün abstraksiya
public interface OrderRepository {
void save(Order order);
Order findById(String id);
}
Hər qat yalnız öz abstraksiyasını tanıyır. Əgər sabah sifarişləri buludda saxlamağa qərar verilsə — yalnız OrderRepository-nin reallaşdırması dəyişir.
Qatların qarşılıqlı fəaliyyəti — sxem
[UI/Controller] <--> [OrderService (abstraksiya)] <--> [OrderRepository (abstraksiya)] <--> [Məlumat bazası]
- Hər qat alt qatın detalları barədə bilmədən abstraksiya ilə işləyir.
- Bu, hər qatı müstəqil şəkildə hazırlamağa, test etməyə və təkmilləşdirməyə imkan verir.
Abstraksiya və kodun dəstəklənməsi
- Yeni imkanları əlavə etmək asandır (tapşırıqların, ödənişlərin, daşımaların yeni növləri və s.).
- Xətaları düzəltmək asandır (bir yerdə bugu düzəltdiniz — bütün varislər yeniləməni aldı).
- Test etmək asandır (yunit-testlər üçün qatları “stub”larla əvəz etmək olar).
6. Abstraksiyaların layihələndirilməsində tipik səhvlər
Səhv №1: Artıq abstraksiya. Bəzən hər xırdalığa abstrakt sinif yaratmaq istəyirsən. Amma əgər sizdə yalnız bir növ kamera (süni varlıq) varsa, dəb xatirinə abstraksiya etməyə dəyməz — bu, yalnız kodu mürəkkəbləşdirəcək.
Səhv №2: Həddən artıq yayğın abstraksiya. Əgər abstrakt sinfiniz çox şeyi təsvir edirsə və aydın məsuliyyət sahəsi yoxdursa, varislər lazımsız metodları reallaşdırmağa və ya “ölü” sahələr saxlamağa məcbur olacaqlar.
Səhv №3: Tək məsuliyyət prinsipinin pozulması. Abstrakt sinif yalnız bir davranış sahəsinə cavabdeh olmalıdır. Məsələn, eyni abstrakt sinifdə saxlama metodlarını biznes məntiqi metodları ilə qarışdırmayın.
Səhv №4: Qatlar arasında sərt əlaqə. Əgər biznes məntiqi qatı saxlanmanın konkret reallaşdırmasından birbaşa asılıdırsa (məsələn, öz daxilində new InMemoryTaskRepository() istifadə edirsə), saxlanmanı dəyişəndə bütün kodu yenidən yazmaq lazım gələcək. Əlaqələri zəiflətmək üçün abstraksiyalardan (interfeyslərdən, abstrakt siniflərdən) istifadə edin.
Səhv №5: Yetərsiz sənədləşdirmə. Abstraksiya — kontraktdır və onu aydın təsvir etmək lazımdır. Varisin nə etməli olduğunu yazmasanız, gözlənilməz xətalar və ya həmkarların “yaradıcılığı” ilə üzləşmək asandır.
GO TO FULL VERSION