Templat GRASP: Pakar Informasi

Hai, Habrovsk. Dalam sentuhan Vladislav Rodin. Saat ini saya mengajar kursus arsitektur perangkat lunak dan arsitektur perangkat lunak beban tinggi di portal OTUS. Kali ini saya memutuskan untuk menulis sedikit materi hak cipta untuk mengantisipasi dimulainya kursus baru "Pola Arsitektur dan Desain" . Selamat membaca.





pengantar


Dijelaskan dalam buku Craig Larman Menerapkan UML dan pola, edisi ke-3, pola GRASP merupakan generalisasi dari pola GoF, serta konsekuensi langsung dari prinsip-prinsip OOP. Mereka melengkapi langkah yang hilang di tangga logis, yang memungkinkan Anda untuk mendapatkan pola GoF dari prinsip-prinsip OOP. Templat GRASP lebih mungkin bukan pola desain (seperti GoF), tetapi prinsip dasar untuk distribusi tanggung jawab antar kelas. Praktek menunjukkan bahwa mereka tidak terlalu populer, namun, analisis kelas yang dirancang menggunakan set lengkap pola GRASP adalah prasyarat untuk menulis kode yang baik.

Daftar lengkap template GRASP terdiri dari 9 elemen:

  • Pakar informasi
  • Pencipta
  • Pengendali
  • Kopling rendah
  • Kohesi tinggi
  • Polimorfisme
  • Fabrikasi murni
  • Tipuan
  • Variasi yang dilindungi

Saya sarankan mempertimbangkan pola yang paling jelas dan paling penting dari daftar: Pakar Informasi.

Pakar informasi


Susunan kata


Menghindari formulasi ilmiah, esensi dari pola ini dapat dinyatakan sebagai berikut: informasi harus diproses di mana ia terkandung.

Contoh pelanggaran


Terlepas dari kesederhanaan dan kejelasan yang tampak, saya yakin bahwa dalam kode proyek apa pun Anda dapat menemukan banyak pelanggaran prinsip ini.

Pertimbangkan sistem kelas yang paling sederhana: Order (pesanan), yang berisi daftar OrderItem'ov (baris pesanan), elemen-elemen yang pada gilirannya mengandung Good (produk) dan kuantitasnya, dan produk tersebut mungkin mengandung, misalnya, harga, nama, dll .:

@Getter
@AllArgsConstructor
public class Order {
    private List<OrderItem> orderItems;
    private String destinationAddress;
}

@Getter
@AllArgsConstructor
public class OrderItem {
    private Good good;
    private int amount;
}

@Getter
@AllArgsConstructor
public class Good {
    private String name;
    private int price;
}


Kami memiliki tugas sederhana: untuk menghitung jumlah pesanan. Jika Anda mendekati solusi untuk masalah ini dengan tidak terlalu serius, Anda dapat langsung menulis sesuatu seperti ini dalam kode klien yang berfungsi dengan objek dari kelas Order:

public class Client {
    public void doSmth() {
        
    }
    
    private int getOrderPrice(Order order) {
        List<OrderItem> orderItems = order.getOrderItems();
        
        int result = 0;
        
        for (OrderItem orderItem : orderItems) {
            int amount = orderItem.getAmount();
            
            Good good = orderItem.getGood();
            int price = good.getPrice();
            
            result += price * amount;
        }
        
        return result;
    }
}


Mari kita menganalisis solusi ini.

Pertama, jika kita mulai menambahkan logika bisnis terkait dengan penetapan harga, kode metode Client :: getOrderPrice tidak hanya akan tumbuh, tetapi juga dikelilingi oleh semua jenis if-s (diskon untuk pensiunan, diskon untuk liburan, diskon karena pembelian grosir), yang pada akhirnya akan mengarah pada fakta bahwa kode ini tidak mungkin dibaca, apalagi perubahan.

Kedua, jika Anda membangun diagram UML, Anda dapat menemukan bahwa ada ketergantungan kelas Klien pada sebanyak 3 kelas: Order, OrderItem dan Good. Ini menarik semua logika bisnis untuk bekerja dengan kelas-kelas ini. Ini berarti bahwa jika kita ingin menggunakan kembali OrderItem atau Good secara terpisah dari Order (misalnya, untuk menghitung harga barang yang tersisa di gudang), kita tidak dapat melakukan ini, karena logika bisnis terletak pada kode klien, yang akan mengarah pada duplikasi kode yang tak terhindarkan.

Dalam contoh ini, seperti hampir di mana-mana di mana ada rantai get'ov, prinsip Pakar Informasi dilanggar, karena kode klien memproses informasi dan berisi Pesanannya.

Contoh aplikasi


Mari kita coba mendistribusikan tanggung jawab sesuai dengan prinsip:

@Getter
@AllArgsConstructor
public class Order {
    private List<OrderItem> orderItems;
    private String destinationAddress;
    
    public int getPrice() {
        int result = 0;
        
        for(OrderItem orderItem : orderItems) {
            result += orderItem.getPrice();
        }
        
        return result;
    }
}

@Getter
@AllArgsConstructor
public class OrderItem {
    private Good good;
    private int amount;

    public int getPrice() {
        return amount * good.getPrice();
    }
}

@Getter
@AllArgsConstructor
public class Good {
    private String name;
    private int price;
}

public class Client {
    public void doSmth() {
        Order order = new Order(new ArrayList<>(), "");
        order.getPrice();
    }
}


Sekarang informasi diproses dalam kelas yang berisi itu, kode klien hanya bergantung pada Order, tidak mencurigai apa pun tentang struktur internal, dan kelas Order, OrderItem dan Good, atau OrderItem dan Good dapat dirakit menjadi perpustakaan terpisah yang dapat digunakan di berbagai bagian proyek.

Kesimpulan


Pakar Informasi, yang dihasilkan dari enkapsulasi, adalah salah satu prinsip paling mendasar dari pembagian tanggung jawab GRASP. Pelanggarannya dapat dengan mudah ditentukan dan dihilangkan dengan meningkatkan kesederhanaan persepsi kode (prinsip paling tidak mengejutkan), menambah kemungkinan penggunaan kembali dan mengurangi jumlah koneksi antar kelas.

Kami mengundang Anda ke webinar gratis dalam kerangka yang memungkinkan mempelajari fitur aplikasi monolitik, arsitektur multi-level, dan tanpa server. Lihatlah lebih dekat pada sistem yang digerakkan oleh peristiwa, sistem yang berorientasi layanan, dan arsitektur layanan mikro.

All Articles