JavaRush /Java Blog /Random-ID /Lima Prinsip Dasar Desain Kelas (SOLID) di Java
Ve4niY
Level 14

Lima Prinsip Dasar Desain Kelas (SOLID) di Java

Dipublikasikan di grup Random-ID
Kelas adalah blok tempat aplikasi dibangun. Seperti halnya batu bata pada sebuah bangunan. Kelas yang ditulis dengan buruk dapat menyebabkan masalah suatu hari nanti. Lima Prinsip Dasar Desain Kelas (SOLID) di Java - 1Untuk memahami apakah suatu kelas ditulis dengan benar, Anda dapat memeriksa “standar kualitas”. Di Java, inilah yang disebut prinsip SOLID. Mari kita bicara tentang mereka.

Prinsip SOLID di Java

SOLID merupakan akronim yang dibentuk dari huruf kapital dari lima prinsip pertama OOP dan desain. Prinsip-prinsip tersebut ditemukan oleh Robert Martin pada awal tahun 2000-an, dan akronimnya kemudian diciptakan oleh Michael Feathers. Inilah yang termasuk dalam prinsip SOLID:
  1. Prinsip Tanggung Jawab Tunggal.
  2. Prinsip Terbuka Tertutup.
  3. Prinsip Substitusi Liskov.
  4. Prinsip Pemisahan Antarmuka.
  5. Prinsip Pembalikan Ketergantungan.

Prinsip Tanggung Jawab Tunggal (SRP)

Prinsip ini menyatakan bahwa tidak boleh ada lebih dari satu alasan untuk mengubah suatu kelas. Setiap objek memiliki satu tanggung jawab, yang sepenuhnya dikemas dalam sebuah kelas. Semua layanan kelas ditujukan untuk memastikan tanggung jawab ini. Kelas-kelas seperti itu akan selalu mudah diubah jika diperlukan, karena jelas apa yang menjadi tanggung jawab kelas dan apa yang bukan. Artinya, akan memungkinkan untuk melakukan perubahan dan tidak takut akan akibat – dampaknya terhadap objek lain. Dan kode seperti itu jauh lebih mudah untuk diuji, karena Anda mencakup satu fungsi dengan pengujian yang terpisah dari fungsi lainnya. Bayangkan sebuah modul yang memproses pesanan. Jika pesanan dibuat dengan benar, pesanan akan disimpan di database dan mengirimkan email untuk mengonfirmasi pesanan:
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
    }
}
Modul tersebut dapat berubah karena tiga alasan. Pertama, logika pemrosesan pesanan mungkin berbeda, kedua, metode penyimpanannya (tipe database), dan ketiga, metode pengiriman surat konfirmasi (misalnya, alih-alih email, Anda perlu mengirim SMS). Prinsip Tanggung Jawab Tunggal mengandung arti bahwa ketiga aspek permasalahan ini sebenarnya merupakan tiga tanggung jawab yang berbeda. Artinya mereka harus berada di kelas atau modul yang berbeda. Menggabungkan beberapa entitas yang mungkin berubah pada waktu berbeda dan karena alasan berbeda dianggap sebagai keputusan desain yang buruk. Jauh lebih baik untuk membagi modul menjadi tiga modul terpisah, yang masing-masing akan menjalankan satu fungsi:
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);
        }
    }

}

Prinsip Terbuka/Tertutup (OCP)

Prinsip ini secara ringkas dijelaskan sebagai berikut: entitas perangkat lunak (kelas, modul, fungsi, dll.) harus terbuka untuk perluasan, tetapi tertutup untuk perubahan . Artinya, perilaku eksternal suatu kelas dapat diubah tanpa membuat perubahan fisik pada kelas itu sendiri. Mengikuti prinsip ini, kelas dikembangkan sehingga untuk menyesuaikan kelas dengan kondisi aplikasi tertentu, cukup dengan memperluasnya dan mendefinisikan ulang beberapa fungsi. Oleh karena itu, sistem harus fleksibel, mampu bekerja dalam kondisi yang bervariasi tanpa mengubah kode sumbernya. Melanjutkan contoh pesanan kita, misalkan kita perlu melakukan beberapa tindakan sebelum pesanan diproses dan setelah email konfirmasi dikirim. Daripada mengubah kelas itu sendiri OrderProcessor, kami akan memperluasnya dan mencapai solusi terhadap masalah yang ada tanpa melanggar prinsip OCP:
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
    }
}

Prinsip Substitusi Barbara Liskov (LSP)

Ini adalah variasi dari prinsip terbuka/tertutup yang dibahas sebelumnya. Dapat digambarkan sebagai berikut: objek-objek dalam suatu program dapat digantikan oleh ahli warisnya tanpa mengubah sifat-sifat program tersebut. Ini berarti bahwa kelas yang dikembangkan dengan memperluas kelas dasar harus mengesampingkan metodenya sedemikian rupa sehingga tidak merusak fungsionalitas dari sudut pandang klien. Artinya, jika pengembang memperluas kelas Anda dan menggunakannya dalam suatu aplikasi, ia tidak boleh mengubah perilaku yang diharapkan dari metode yang diganti. Subkelas harus mengesampingkan metode kelas dasar dengan cara yang tidak merusak fungsionalitas dari sudut pandang klien. Hal ini dapat diperiksa secara rinci menggunakan contoh berikut. Anggaplah kita memiliki kelas yang bertanggung jawab untuk validasi pesanan dan memeriksa apakah semua item pesanan tersedia. Kelas ini memiliki metode isValidyang mengembalikan nilai true atau false :
public class OrderStockValidator {

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

        return true;
    }
}
Mari kita asumsikan juga bahwa beberapa pesanan perlu divalidasi secara berbeda: periksa apakah semua barang dalam pesanan tersedia dan apakah semua barang sudah dikemas. Untuk melakukan ini, kami memperluas kelas OrderStockValidatordengan class 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;
    }
}
Namun, di kelas ini kami melanggar prinsip LSP, karena alih-alih mengembalikan false jika pesanan tidak lolos validasi, metode kami malah memunculkan pengecualian IllegalStateException. Klien kode ini tidak mengharapkan ini: mereka mengharapkan true atau false dikembalikan . Hal ini dapat menyebabkan kesalahan dalam program.

Prinsip Pemisahan Antarmuka (ISP)

Ditandai dengan pernyataan berikut: Klien tidak boleh dipaksa untuk menerapkan metode yang tidak akan mereka gunakan . Prinsip pemisahan antarmuka menunjukkan bahwa antarmuka yang terlalu "tebal" perlu dibagi menjadi lebih kecil dan lebih spesifik, sehingga klien antarmuka kecil hanya mengetahui metode yang diperlukan untuk pekerjaan mereka. Akibatnya, ketika mengubah metode antarmuka, klien yang tidak menggunakan metode ini tidak akan berubah. Mari kita lihat sebuah contoh. Pengembang Alex membuat antarmuka "laporan" dan menambahkan dua metode: generateExcel()dan generatedPdf(). Sekarang Klien A ingin menggunakan antarmuka ini, tetapi dia hanya bermaksud menggunakan laporan PDF dan bukan Excel. Apakah dia akan puas dengan fungsi ini? TIDAK. Dia harus menerapkan dua metode, salah satunya sebagian besar tidak diperlukan dan hanya ada berkat Alex, perancang perangkat lunak. Klien akan menggunakan antarmuka yang berbeda atau membiarkan kolom Excel kosong. Jadi apa solusinya? Ini terdiri dari membagi antarmuka yang ada menjadi dua antarmuka yang lebih kecil. Yang pertama adalah laporan dalam format PDF, yang kedua adalah laporan dalam format Excel. Ini akan memberi pengguna kesempatan untuk hanya menggunakan fungsionalitas yang diperlukannya.

Prinsip Pembalikan Ketergantungan (DIP)

Prinsip SOLID di Java dijelaskan sebagai berikut: ketergantungan dalam sistem dibangun berdasarkan abstraksi . Modul tingkat atas tidak bergantung pada modul tingkat bawah. Abstraksi tidak boleh bergantung pada detail. Detailnya harus bergantung pada abstraksi. Perangkat lunak perlu dirancang sedemikian rupa sehingga berbagai modul bersifat otonom dan terhubung satu sama lain menggunakan abstraksi. Penerapan klasik dari prinsip ini adalah kerangka Spring. Dalam kerangka Spring, semua modul diimplementasikan sebagai komponen terpisah yang dapat bekerja sama. Mereka sangat mandiri sehingga dapat digunakan dengan mudah di modul perangkat lunak lain selain kerangka Spring. Hal ini dicapai melalui ketergantungan prinsip tertutup dan terbuka. Semua modul hanya menyediakan akses ke abstraksi yang dapat digunakan di modul lain. Mari kita coba mendemonstrasikannya dengan sebuah contoh. Berbicara tentang prinsip tanggung jawab tunggal, kami mempertimbangkan beberapa hal OrderProcessor. Mari kita lihat lagi kode kelas ini:
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);
        }
    }

}
Dalam contoh ini, kelas kita OrderProcessorbergantung pada dua kelas tertentu MySQLOrderRepositorydan ConfirmationEmailSender. Kami juga menyajikan kode untuk kelas-kelas ini:
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
    }
}
Kelas-kelas ini jauh dari disebut abstraksi. Dan dari sudut pandang prinsip DIP, akan lebih tepat untuk memulai dengan membuat beberapa abstraksi yang memungkinkan kita untuk mengoperasikannya di masa depan, daripada dengan implementasi spesifik. Mari buat dua antarmuka MailSenderdan OrderRepository, yang akan menjadi abstraksi kita:
public interface MailSender {
    void sendConfirmationEmail(Order order);
}

public interface OrderRepository {
    boolean save(Order order);
}
Sekarang mari kita implementasi antarmuka ini di kelas yang sudah siap untuk ini:
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;
    }
}
Kami telah melakukan pekerjaan persiapan sehingga kelas kami OrderProcessortidak bergantung pada detail konkrit, namun pada abstraksi. Mari kita buat perubahan dengan memperkenalkan dependensi kita pada konstruktor kelas:
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);
        }
    }
}
Kelas kita sekarang bergantung pada abstraksi daripada implementasi konkrit. Anda dapat dengan mudah mengubah perilakunya dengan memasukkan ketergantungan yang diinginkan pada saat instance dibuat OrderProcessor. Kami melihat SOLID - prinsip desain di Java. Lebih lanjut tentang OOP secara umum, dasar-dasar bahasa pemrograman ini - tidak membosankan dan dengan latihan ratusan jam - dalam kursus JavaRush. Saatnya menyelesaikan beberapa masalah :) Lima Prinsip Dasar Desain Kelas (SOLID) di Java - 2
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION