Cara menentukan tingkat abstraksi

35

Saya membaca sebuah buku hari ini yang disebut "Clean code" dan saya menemukan sebuah paragraf ketika penulis berbicara tentang level abstraksi per fungsi, ia mengklasifikasikan beberapa kode sebagai abstraksi level rendah / menengah / tinggi.

Pertanyaan saya adalah apa kriteria untuk menentukan tingkat abstraksi?

Saya mengutip paragraf dari buku:

Untuk memastikan fungsi kita melakukan "satu hal," kita perlu memastikan bahwa pernyataan dalam fungsi kita semua pada tingkat abstraksi yang sama. Sangat mudah untuk melihat bagaimana Listing 3-1 melanggar aturan ini. Ada konsep di sana yang berada pada tingkat abstraksi yang sangat tinggi, seperti getHtml (); yang lain yang berada pada tingkat menengah abstraksi, seperti: String pagePathName = PathParser.render (pagePath); dan yang lainnya yang levelnya sangat rendah, seperti: .append ("\ n").

OKAN
sumber
2
Terkait: Apa itu abstraksi? .
Adam Lear

Jawaban:

27

Penulis menjelaskan bahwa dalam subbagian "Membaca Kode dari Atas ke Bawah" pada bagian yang membahas tentang abstraksi (tambang indentasi hierarkis):

[...] kami ingin dapat membaca program seolah-olah itu adalah set paragraf TO , masing-masing menggambarkan tingkat abstraksi saat ini dan referensi paragraf TO berikutnya pada tingkat berikutnya ke bawah.

  • Untuk menyertakan pengaturan dan teardown, kami memasukkan setup, lalu kami memasukkan konten halaman pengujian, dan kemudian kami menyertakan teardown.
    • Untuk memasukkan pengaturan, kami menyertakan pengaturan suite jika ini adalah suite, maka kami menyertakan pengaturan reguler.
      • Untuk memasukkan pengaturan suite, kami mencari hierarki induk untuk halaman "SuiteSetUp" dan menambahkan pernyataan sertakan dengan jalur halaman itu.
        • Untuk mencari orang tua ...

Kode yang sejalan dengan ini akan menjadi seperti ini:

public void CreateTestPage()
{
    IncludeSetups();
    IncludeTestPageContent();
    IncludeTeardowns();
}

public void IncludeSetups()
{
    if(this.IsSuite())
    {
        IncludeSuiteSetup();
    }

    IncludeRegularSetup();
}

public void IncludeSuiteSetup()
{
    var parentPage = FindParentSuitePage();

    // add include statement with the path of the parentPage
}

Dan seterusnya. Setiap kali Anda masuk lebih dalam ke hierarki fungsi, Anda harus mengubah level abstraksi. Dalam contoh di atas, IncludeSetups, IncludeTestPageContentdan IncludeTeardownssemua pada tingkat yang sama abstraksi.

Dalam contoh yang diberikan dalam buku ini, penulis menyarankan bahwa fungsi besar harus dipecah menjadi yang lebih kecil yang sangat spesifik dan melakukan satu hal saja. Jika dilakukan dengan benar, fungsi refactored akan terlihat mirip dengan contoh di sini. (Versi refactored diberikan pada Listing 3-7 di buku.)

Adam Lear
sumber
10

Saya pikir untuk memahami pertanyaan ini, Anda perlu memahami apa itu abstraksi. (Saya terlalu malas untuk menemukan definisi formal, jadi saya yakin saya akan mendapatkan dinged, tapi begini ...) Sebuah abstraksi adalah ketika Anda mengambil subjek yang kompleks, atau entitas dan menyembunyikan sebagian besar detailnya sambil mengekspos fungsi yang masih mendefinisikan esensi objek itu.

Saya percaya contoh yang diberikan buku itu adalah sebuah rumah. Jika Anda melihat dengan sangat teliti pada rumah itu, Anda akan melihat bahwa itu terbuat dari papan, paku, jendela, pintu ... Tetapi gambar kartun sebuah rumah di sebelah sebuah foto masih berupa sebuah rumah, meskipun rumah itu hilang. banyak dari detail itu.

Hal yang sama dengan perangkat lunak. Setiap kali Anda memprogram, seperti yang disarankan buku ini, Anda perlu memikirkan peranti lunak Anda sebagai layer. Suatu program tertentu dapat dengan mudah memiliki lebih dari seratus lapisan. Di bagian bawah, Anda mungkin memiliki instruksi perakitan yang berjalan pada CPU, pada level yang lebih tinggi instruksi ini dapat digabungkan untuk membentuk rutinitas I / O disk, pada level yang lebih tinggi, Anda tidak perlu bekerja dengan Disk I / O secara langsung karena Anda dapat menggunakan fungsi Windows hanya dengan Buka / Baca / Tulis / Cari / Tutup file. Ini semua adalah abstraksi bahkan sebelum Anda mendapatkan kode aplikasi Anda sendiri.

Di dalam kode Anda, lapisan abstraksi berlanjut. Anda mungkin memiliki rutinitas manipulasi string / jaringan / data yang lebih rendah. Pada level yang lebih tinggi, Anda dapat menggabungkan rutinitas tersebut ke dalam subsistem yang menentukan manajemen pengguna, lapisan UI, akses basis data. Namun lapisan lain subsistem ini dapat digabungkan menjadi komponen server yang bergabung untuk menjadi bagian dari sistem perusahaan yang lebih besar.

Kunci untuk masing-masing lapisan abstraksi ini adalah bahwa masing-masing menyembunyikan detail yang diekspos oleh lapisan sebelumnya dan menyajikan antarmuka yang sangat bersih untuk dikonsumsi oleh lapisan berikutnya. Untuk membuka file, Anda tidak perlu tahu cara menulis sektor individual atau hardware apa yang terputus untuk diproses. Tetapi jika Anda mulai melakukan perjalanan menuruni rantai lapisan abstraksi, Anda pasti akan dapat melacak dari panggilan fungsi Write (), sampai ke instruksi yang tepat yang dikirim ke pengontrol hard drive.

Apa yang penulis katakan harus Anda lakukan adalah ketika Anda mendefinisikan kelas atau fungsi, pikirkan tentang apa lapisan Anda. Jika Anda memiliki kelas yang mengelola subsistem dan objek pengguna, kelas yang sama tidak boleh melakukan manipulasi string tingkat rendah atau berisi sejumlah variabel hanya untuk melakukan panggilan soket. Itu akan menjadi pelanggaran melintasi lapisan abstraksi dan juga memiliki satu kelas / fungsi hanya melakukan satu hal (SRP - Prinsip Tanggung Jawab Tunggal).

DXM
sumber
2

Pertanyaan saya adalah apa kriteria untuk menentukan tingkat abstraksi?

Level abstraksi seharusnya jelas. Ini abstrak jika itu bagian dari domain masalah - bukan bagian dari bahasa pemrograman. Sulit untuk menjadi lebih jelas daripada "sangat abstrak" == "tidak nyata" == "domain masalah". Dan "tidak abstrak == konkret == bagian dari bahasa". Seharusnya sepele untuk memutuskan tingkat abstraksi. Seharusnya tidak ada kehalusan sama sekali.

.append("\n")tidak abstrak. Itu hanya menempatkan karakter ke string. Itu akan konkret. Bukan abstrak.

String pagePathName = PathParser.render(pagePath);berurusan dengan Strings. Hal-hal konkret. Sebagian pada fitur bahasa pemrograman konkret. Sebagian bekerja dengan konsep "path" dan "parser" abstrak.

getHtml(); Abstrak. Berurusan dengan "Markup" dan hal-hal yang tidak sepele, konkret, fitur bahasa.

Abstrak == bukan fitur bahasa.

Beton == fitur bahasa.

S.Lott
sumber
1
Jika Anda mendefinisikan abstrak sebagai kualitas yang menjelaskan hal-hal yang dibuat oleh seorang programmer, yaitu abstraksi yang dihasilkan oleh programmer, maka saya cenderung setuju dengan definisi kata Anda. Tetapi semua bahasa pemrograman adalah abstraksi atas sesuatu yang lebih konkret. Pilihan bahasa pemrograman menentukan, sebagian besar, tingkat abstraksi yang Anda mulai.
Robert Harvey
1

Saya pikir tingkat abstraksi sederhana ... jika garis kode tidak secara langsung menerapkan tanggung jawab tunggal dari metode ini, itu adalah tingkat abstraksi lain. Misalnya, jika nama metode saya adalah SaveChangedCustomers () dan mengambil daftar SEMUA pelanggan sebagai parameter, satu-satunya tanggung jawab adalah menyimpan setiap pelanggan dalam daftar yang telah diubah:

foreach(var customer in allCustomers)
{
    if (CustomerIsChanged(customer)
        customer.Save();
}

Seringkali, alih-alih memanggil metode CustomerIsChanged (), Anda akan menemukan logika untuk menentukan apakah pelanggan telah berubah tertanam di loop foreach. Menentukan apakah catatan pelanggan telah berubah TIDAK menjadi tanggung jawab metode ini! Ini adalah tingkat abstraksi yang berbeda. Tetapi bagaimana jika logika untuk membuat penentuan itu hanya satu baris kode? Tidak masalah !!! Ini adalah tingkat abstraksi yang berbeda dan perlu berada di luar metode ini.

pengguna7542047
sumber