Siniflər proqramın qurulduğu bloklardır. Binadakı kərpiclər kimi. Zəif yazılmış dərslər bir gün problemlər yarada bilər. Java-da Sinif Dizaynının (SOLID) Beş Əsas Prinsipləri - 1Bir sinfin düzgün yazıldığını anlamaq üçün “keyfiyyət standartlarını” yoxlaya bilərsiniz. Java-da bunlar SOLID prinsipləri adlanır. Gəlin onlar haqqında danışaq.

Java-da SOLID Prinsipləri

SOLID, OOP və dizaynın ilk beş prinsipinin böyük hərflərindən əmələ gələn qısaltmadır. Prinsiplər 2000-ci illərin əvvəllərində Robert Martin tərəfindən icad edilmiş və qısaltma daha sonra Michael Feathers tərəfindən yaradılmışdır. SOLID prinsiplərinə aşağıdakılar daxildir:
  1. Vahid Məsuliyyət Prinsipi.
  2. Açıq Qapalı Prinsip.
  3. Liskovun əvəzetmə prinsipi.
  4. İnterfeys Seqreqasiya Prinsipi.
  5. Asılılığın inversiya prinsipi.

Vahid Məsuliyyət Prinsipi (SRP)

Bu prinsip bildirir ki, sinfi dəyişmək üçün heç vaxt birdən çox səbəb olmamalıdır. Hər bir obyektin bir sinifdə tamamilə əhatə olunmuş bir məsuliyyəti var. Bütün sinif xidmətləri bu məsuliyyəti təmin etməyə yönəlmişdir. Lazım gələrsə, belə sinifləri dəyişdirmək həmişə asan olacaq, çünki sinfin nəyə görə məsuliyyət daşıdığı və nəyin olmadığı aydındır. Yəni, dəyişikliklər etmək və nəticələrdən qorxmamaq mümkün olacaq - digər obyektlərə təsir. Və belə kodu yoxlamaq daha asandır, çünki siz bir funksionallığı bütün digərlərindən təcrid olunmuş testlərlə əhatə edirsiniz. Sifarişləri emal edən modulu təsəvvür edin. Sifariş düzgün formalaşdırılıbsa, onu verilənlər bazasında saxlayır və sifarişi təsdiqləmək üçün e-poçt göndərir:
public class OrderProcessor {

    public void process(Order order){
        if (order.isValid() && save(order)) {
            sendConfirmationEmail(order);
        }
    }

    private boolean save(Order order) {
        MySqlConnection connection = new MySqlConnection("database.url");
        // save the order to the database

        return true;
    }

    private void sendConfirmationEmail(Order order) {
        String name = order.getCustomerName();
        String email = order.getCustomerEmail();

        // Sending a letter to the client
    }
}
Belə bir modul üç səbəbdən dəyişə bilər. Birincisi, sifarişin işlənməsi məntiqi fərqli ola bilər, ikincisi, onu saxlama üsulu (verilənlər bazası növü), üçüncüsü, təsdiq məktubu göndərmə üsulu (məsələn, e-poçt əvəzinə SMS göndərmək lazımdır). Vahid Məsuliyyət Prinsipi bu problemin üç aspektinin əslində üç fərqli məsuliyyət olduğunu nəzərdə tutur. Bu o deməkdir ki, onlar müxtəlif siniflərdə və ya modullarda olmalıdırlar. Müxtəlif vaxtlarda və müxtəlif səbəblərdən dəyişə bilən bir neçə obyekti birləşdirmək pis dizayn qərarı hesab olunur. Modulu hər biri bir funksiyanı yerinə yetirəcək üç ayrı modula bölmək daha yaxşıdır:
public class MySQLOrderRepository {
    public boolean save(Order order) {
        MySqlConnection connection = new MySqlConnection("database.url");
        // save the order to the database

        return true;
    }
}

public class ConfirmationEmailSender {
    public void sendConfirmationEmail(Order order) {
        String name = order.getCustomerName();
        String email = order.getCustomerEmail();

        // Sending a letter to the client
    }
}

public class OrderProcessor {
    public void process(Order order){

        MySQLOrderRepository repository = new MySQLOrderRepository();
        ConfirmationEmailSender mailSender = new ConfirmationEmailSender();

        if (order.isValid() && repository.save(order)) {
            mailSender.sendConfirmationEmail(order);
        }
    }

}

Açıq/Qapalı Prinsip (OCP)

Bu prinsip qısa şəkildə aşağıdakı kimi təsvir edilmişdir: proqram obyektləri (siniflər, modullar, funksiyalar və s.) genişləndirmək üçün açıq, lakin dəyişiklik üçün qapalı olmalıdır . Bu o deməkdir ki, sinfin özündə fiziki dəyişikliklər etmədən sinfin xarici davranışını dəyişmək mümkün olmalıdır. Bu prinsipə əməl edərək, siniflər hazırlanır ki, sinfi xüsusi tətbiq şərtlərinə uyğunlaşdırmaq üçün onu genişləndirmək və bəzi funksiyaları yenidən müəyyənləşdirmək kifayətdir. Buna görə də sistem çevik olmalı, mənbə kodunu dəyişmədən dəyişən şəraitdə işləməyi bacarmalıdır. Sifariş nümunəmizə davam edərək, deyək ki, sifariş emal edilməzdən əvvəl və təsdiq e-poçtu göndərildikdən sonra bəzi hərəkətləri yerinə yetirməliyik. Sinfin özünü dəyişdirmək əvəzinə OrderProcessor, biz onu genişləndirəcəyik və OCP prinsipini pozmadan mövcud problemin həllinə nail olacağıq:
public class OrderProcessorWithPreAndPostProcessing extends OrderProcessor {

    @Override
    public void process(Order order) {
        beforeProcessing();
        super.process(order);
        afterProcessing();
    }

    private void beforeProcessing() {
        // Perform some actions before processing the order
    }

    private void afterProcessing() {
        // Perform some actions after order processing
    }
}

Barbara Liskov Əvəzetmə Prinsipi (LSP)

Bu, əvvəllər müzakirə edilən açıq/qapalı prinsipin bir variantıdır. Bunu aşağıdakı kimi təsvir etmək olar: proqramdakı obyektlər proqramın xassələrini dəyişdirmədən onların varisləri ilə əvəz edilə bilər. Bu o deməkdir ki, baza sinfini genişləndirməklə inkişaf etdirilən bir sinif müştərinin nöqteyi-nəzərindən funksionallığı pozmayacaq şəkildə öz metodlarını ləğv etməlidir. Yəni bir tərtibatçı sinifinizi genişləndirirsə və onu tətbiqdə istifadə edirsə, ləğv edilmiş metodların gözlənilən davranışını dəyişməməlidir. Alt siniflər müştərinin nöqteyi-nəzərindən funksionallığı pozmayacaq şəkildə əsas sinif metodlarını ləğv etməlidir. Bunu aşağıdakı nümunədən istifadə edərək ətraflı şəkildə araşdırmaq olar. Fərz edək ki, sifarişin yoxlanılmasına cavabdeh olan və bütün sifarişlərin anbarda olub-olmadığını yoxlayan bir sinifimiz var. Bu sinifin doğru və ya yalanıisValid qaytaran metodu var :
public class OrderStockValidator {

    public boolean isValid(Order order) {
        for (Item item : order.getItems()) {
            if (! item.isInStock()) {
                return false;
            }
        }

        return true;
    }
}
Həmçinin fərz edək ki, bəzi sifarişlər fərqli şəkildə təsdiq edilməlidir: sifarişdəki bütün malların anbarda olub-olmadığını və bütün malların qablaşdırılıb-qablaşdırılmadığını yoxlayın. OrderStockValidatorBunun üçün sinfi siniflə genişləndirdik OrderStockAndPackValidator:
public class OrderStockAndPackValidator extends OrderStockValidator {

    @Override
    public boolean isValid(Order order) {
        for (Item item : order.getItems()) {
            if ( !item.isInStock() || !item.isPacked() ){
                throw new IllegalStateException(
                     String.format("Order %d is not valid!", order.getId())
                );
            }
        }

        return true;
    }
}
Bununla belə, bu sinifdə biz LSP prinsipini pozduq, çünki sifariş doğrulamadan keçməyibsə, yalanı qaytarmaq əvəzinə , metodumuz bir istisna atır IllegalStateException. Bu kodun müştəriləri bunu gözləmirlər: onlar doğru və ya yalanın qaytarılmasını gözləyirlər . Bu, proqramda səhvlərə səbəb ola bilər.

Interface Split Principle (ISP)

Aşağıdakı ifadə ilə xarakterizə olunur: Müştərilər istifadə etməyəcəkləri metodları tətbiq etməyə məcbur edilməməlidirlər . İnterfeyslərin ayrılması prinsipi, çox "qalın" olan interfeyslərin daha kiçik və daha spesifik olanlara bölünməsini təklif edir ki, kiçik interfeyslərin müştəriləri yalnız öz işləri üçün lazım olan üsulları bilsinlər. Nəticədə, interfeys metodunu dəyişdirərkən, bu metoddan istifadə etməyən müştərilər dəyişməməlidir. Bir nümunəyə baxaq. Tərtibatçı Alex "hesabat" interfeysini yaratdı və iki üsul əlavə etdi: generateExcel()generatedPdf(). İndi A Müştərisi bu interfeysdən istifadə etmək istəyir, lakin o, Excel-dən deyil, yalnız PDF hesabatlarından istifadə etmək niyyətindədir. O, bu funksionallıqdan razı qalacaqmı? Yox. O, iki metodu həyata keçirməli olacaq, bunlardan biri böyük ölçüdə lazımsızdır və yalnız proqram təminatı dizayneri Aleksin sayəsində mövcuddur. Müştəri ya fərqli interfeysdən istifadə edəcək, ya da Excel sahəsini boş qoyacaq. Bəs həll yolu nədir? Mövcud interfeysi iki kiçik hissəyə bölməkdən ibarətdir. Biri PDF formatında hesabat, ikincisi Excel formatında hesabatdır. Bu, istifadəçiyə yalnız onun üçün lazım olan funksionallıqdan istifadə etmək imkanı verəcək.

Asılılığın inversiya prinsipi (DIP)

Java-da bu SOLID prinsipi aşağıdakı kimi təsvir edilmişdir: sistem daxilində asılılıqlar abstraksiyalar əsasında qurulur . Üst səviyyə modulları aşağı səviyyəli modullardan müstəqildir. Abstraksiyalar detallardan asılı olmamalıdır. Detallar abstraksiyalardan asılı olmalıdır. Proqram təminatı elə tərtib edilməlidir ki, müxtəlif modullar avtonom olsun və abstraksiyadan istifadə edərək bir-birinə qoşulsun. Bu prinsipin klassik tətbiqi Bahar çərçivəsidir. Bahar çərçivəsində bütün modullar birlikdə işləyə bilən ayrıca komponentlər kimi həyata keçirilir. Onlar o qədər müstəqildirlər ki, Spring framework-dən başqa digər proqram modullarında da asanlıqla istifadə oluna bilərlər. Bu, qapalı və açıq prinsiplərin asılılığı ilə əldə edilir. Bütün modullar yalnız başqa modulda istifadə oluna bilən abstraksiyaya girişi təmin edir. Bunu bir nümunə ilə nümayiş etdirməyə çalışaq. Yeganə məsuliyyət prinsipindən danışarkən bəzilərini nəzərdən keçirdik OrderProcessor. Bu sinfin koduna bir daha nəzər salaq:
public class OrderProcessor {
    public void process(Order order){

        MySQLOrderRepository repository = new MySQLOrderRepository();
        ConfirmationEmailSender mailSender = new ConfirmationEmailSender();

        if (order.isValid() && repository.save(order)) {
            mailSender.sendConfirmationEmail(order);
        }
    }

}
Bu nümunədə bizimki OrderProcessoriki xüsusi sinifdən asılıdır MySQLOrderRepositoryConfirmationEmailSender. Bu siniflər üçün kodu da təqdim edirik:
public class MySQLOrderRepository {
    public boolean save(Order order) {
        MySqlConnection connection = new MySqlConnection("database.url");
        // save the order to the database

        return true;
    }
}

public class ConfirmationEmailSender {
    public void sendConfirmationEmail(Order order) {
        String name = order.getCustomerName();
        String email = order.getCustomerEmail();

        // Sending a letter to the client
    }
}
Bu siniflər abstraksiya adlandırmaqdan çox uzaqdır. Və DIP prinsipi nöqteyi-nəzərindən, konkret tətbiqlərlə deyil, gələcəkdə onlarla işləməyə imkan verəcək bəzi abstraksiyalar yaratmaqla başlamaq daha düzgün olardı. Gəlin abstraksiyalarımıza çevriləcək iki interfeys yaradaq MailSendervə :OrderRepository
public interface MailSender {
    void sendConfirmationEmail(Order order);
}

public interface OrderRepository {
    boolean save(Order order);
}
İndi bu interfeysləri artıq buna hazır olan siniflərdə tətbiq edək:
public class ConfirmationEmailSender implements MailSender {

    @Override
    public void sendConfirmationEmail(Order order) {
        String name = order.getCustomerName();
        String email = order.getCustomerEmail();

        // Sending a letter to the client
    }

}

public class MySQLOrderRepository implements OrderRepository {

    @Override
    public boolean save(Order order) {
        MySqlConnection connection = new MySqlConnection("database.url");
        // save the order to the database

        return true;
    }
}
Hazırlıq işlərini elə etdik ki, sinfimiz OrderProcessorkonkret detallardan yox, abstraksiyalardan asılı olsun. Sinif konstruktorunda asılılıqlarımızı təqdim edərək ona dəyişikliklər edək:
public class OrderProcessor {

    private MailSender mailSender;
    private OrderRepository repository;

    public OrderProcessor(MailSender mailSender, OrderRepository repository) {
        this.mailSender = mailSender;
        this.repository = repository;
    }

    public void process(Order order){
        if (order.isValid() && repository.save(order)) {
            mailSender.sendConfirmationEmail(order);
        }
    }
}
Sinifimiz indi konkret tətbiqlərdən daha çox abstraksiyalardan asılıdır. Nümunənin yaradıldığı anda istədiyiniz asılılığı daxil etməklə onun davranışını asanlıqla dəyişə bilərsiniz OrderProcessor. Java-da SOLID - dizayn prinsiplərinə baxdıq. Ümumiyyətlə, OOP haqqında daha çox, bu proqramlaşdırma dilinin əsasları - darıxdırıcı deyil və yüzlərlə saat təcrübə ilə - JavaRush kursunda. Bəzi problemləri həll etmək vaxtıdır :) Java-da Sinif Dizaynının (SOLID) Beş Əsas Prinsipləri - 2