Abstraksi: Perang antara penyelesaian masalah dan solusi umum [tertutup]

33

Sebagai seorang programmer, saya menemukan diri saya dalam dilema di mana saya ingin membuat program saya sebagai abstrak dan umum mungkin.

Melakukan hal itu biasanya akan memungkinkan saya untuk menggunakan kembali kode saya dan memiliki solusi yang lebih umum untuk masalah yang mungkin (atau mungkin tidak) muncul lagi.

Lalu suara ini di kepalaku berkata, pecahkan saja masalah dummy-nya semudah itu! Mengapa menghabiskan lebih banyak waktu dari yang seharusnya?

Kita semua memang menghadapi pertanyaan ini di mana Abstraksi ada di bahu kanan Anda dan Solve-it-bodoh duduk di sebelah kiri.

Yang harus didengarkan dan seberapa sering? Apa strategi Anda untuk ini? Haruskah Anda abstrak semuanya?

Bryan Harrington
sumber
1
"As abstrak dan umum" secara umum dikenal sebagai hubris programmer :) Namun, karena hukum Murphy, satu tingkat adaptasi yang Anda perlukan ketika mengerjakan tugas berikutnya adalah yang Anda belum sediakan.
Matthieu M.
1
Salah satu pertanyaan terbaik yang pernah ada!
explorest
1
Anda selalu salah menebak sehingga meresapnya sederhana. Ketika Anda telah memperluas kasus sederhana "cukup kali" Anda mulai melihat sebuah pola. Sampai saat itu, jangan.

Jawaban:

27

Yang harus didengarkan dan seberapa sering?

Jangan pernah abstrak sampai Anda harus.

Di Java, misalnya, Anda harus menggunakan antarmuka. Mereka adalah abstraksi.

Dalam Python Anda tidak memiliki antarmuka, Anda memiliki Bebek Mengetik, dan Anda tidak perlu tingkat abstraksi yang tinggi. Jadi kamu tidak.

Apa strategi Anda untuk ini?

Jangan abstrak sampai Anda sudah menulisnya tiga kali.

Sekali - baik - sekali. Pecahkan saja dan lanjutkan.

Dua kali adalah indikasi bahwa mungkin ada pola di sini. Atau mungkin tidak. Itu bisa saja kebetulan.

Tiga kali adalah awal dari suatu pola. Sekarang itu melampaui kebetulan. Sekarang Anda dapat abstrak.

Haruskah Anda abstrak semuanya?

Tidak.

Memang, Anda tidak boleh mengabstraksikan apa pun sampai Anda memiliki bukti absolut bahwa Anda melakukan abstraksi yang tepat. Tanpa "Aturan Tiga Pengulangan", Anda akan menulis hal-hal yang tidak berguna yang diabstraksikan dengan cara yang tidak membantu.

Melakukan hal itu biasanya akan memungkinkan saya untuk menggunakan kembali kode saya

Ini adalah asumsi yang sering salah. Abstraksi mungkin tidak membantu sama sekali. Itu bisa dilakukan dengan buruk. Karena itu, jangan lakukan itu sampai Anda harus.

S.Lott
sumber
1
"Jangan pernah abstrak sampai kamu harus." - Saya kira Anda menghindari penggunaan kelas, metode, loop, atau jika pernyataan? Semua ini adalah abstraksi. Jika Anda memikirkannya, kode yang ditulis dalam bahasa tingkat tinggi sangat abstrak apakah Anda suka atau tidak. Pertanyaannya bukan apakah abstrak. Itu abstraksi yang digunakan.
Jason Baker
9
@Jason Baker: "Saya kira Anda menghindari penggunaan kelas, metode, loop, atau pernyataan jika". Sepertinya bukan itu pertanyaannya. Mengapa membuat klaim yang absurd bahwa semua pemrograman tidak mungkin jika kita menghindari terlalu banyak abstrak desain kita?
S.Lott
1
Saya teringat akan lelucon lama di mana seorang pria bertanya kepada seorang wanita apakah dia akan tidur dengannya sejuta dolar. Ketika dia mengatakan ya, dia bertanya apakah dia akan tidur dengannya selama lima dolar. Ketika dia berkata, "Wanita seperti apa yang kamu pilih untukku?" jawabannya adalah "Ya, kami sudah menetapkan bahwa Anda akan tidur dengan seseorang untuk berhubungan seks. Sekarang kami hanya tawar-menawar soal harganya." Maksud saya adalah bahwa ini tidak pernah tentang keputusan untuk abstrak atau tidak. Ini tentang berapa banyak untuk abstrak dan abstraksi apa yang harus dipilih. Anda tidak dapat memilih untuk menunda abstraksi kecuali jika Anda menulis perakitan murni.
Jason Baker
9
@Jason Baker: "Anda tidak dapat memilih untuk menunda abstraksi kecuali jika Anda menulis perakitan murni" Itu sepele. Assembly adalah abstraksi atas bahasa mesin. Yang merupakan abstraksi atas sirkuit perangkat keras. Tolong berhenti membaca terlalu banyak ke jawaban yang tidak ada dalam pertanyaan di tempat pertama. Pertanyaannya bukan tentang "Abstraksi" sebagai konsep atau alat intelektual. Itu tentang abstraksi sebagai teknik desain OO - salah diterapkan - terlalu sering. Jawaban saya bukanlah bahwa abstraksi tidak mungkin. Tetapi "abstraksi berlebihan" itu buruk.
S.Lott
1
@JasonBaker itu bukan alasan yang belum pernah terjadi untuk tidur dengan seseorang. Mungkin Anda berpikir tentang "uang"?
19

Ah, YAGNI. Konsep pemrograman yang paling disalahgunakan.

Ada perbedaan antara membuat kode Anda generik dan melakukan pekerjaan ekstra. Haruskah Anda menghabiskan waktu ekstra untuk membuat kode Anda digabungkan secara longgar dan mudah diadaptasi untuk melakukan hal-hal lain? Benar. Haruskah Anda menghabiskan waktu mengimplementasikan fungsi yang tidak dibutuhkan? Tidak. Haruskah Anda menghabiskan waktu agar kode Anda berfungsi dengan kode lain yang belum diimplikasikan? Tidak.

Seperti kata pepatah "Lakukan hal paling sederhana yang mungkin bisa berhasil". Masalahnya adalah bahwa orang selalu bingung sederhana dengan mudah . Kesederhanaan butuh kerja. Tapi itu sepadan dengan usaha.

Bisakah Anda berlebihan? Tentu saja. Tetapi pengalaman saya adalah bahwa beberapa perusahaan membutuhkan lebih banyak sikap "menyelesaikannya sekarang" daripada yang sudah mereka miliki. Sebagian besar perusahaan membutuhkan lebih banyak orang yang akan memikirkan segalanya terlebih dahulu. Kalau tidak, mereka selalu berakhir dengan masalah mandiri di mana tidak ada yang punya waktu untuk apa pun, semua orang selalu terburu-buru, dan tidak ada yang menyelesaikan sesuatu.

Pikirkan seperti ini: kemungkinan bagus bahwa kode Anda akan digunakan lagi entah bagaimana. Tapi Anda tidak akan memprediksi dengan benar seperti itu. Jadi buat kode Anda bersih, abstrak, dan digabungkan secara longgar. Tapi jangan coba-coba membuat kode Anda berfungsi dengan fungsionalitas spesifik apa pun yang belum ada. Bahkan jika Anda tahu itu akan ada di masa depan.

Jason Baker
sumber
Perbedaan yang bagus antara simpledan easy!
Matthieu M.
"Tetapi pengalaman saya adalah bahwa hanya sedikit perusahaan" - yang menarik, kebalikannya mungkin benar di dunia akademis.
Steve Bennett
12

Silogisme:

  1. Generalitas itu mahal.

  2. Anda menghabiskan uang orang lain.

  3. Karena itu biaya generalisasi harus dibenarkan kepada para pemangku kepentingan.

Tanyakan pada diri Anda apakah Anda benar - benar menyelesaikan masalah yang lebih umum untuk menghemat uang para pemangku kepentingan nanti, atau jika Anda hanya menemukan itu tantangan intelektual untuk membuat solusi umum yang tidak perlu.

Jika sifat umum ditentukan untuk diinginkan maka harus dirancang dan diuji seperti fitur lainnya . Jika Anda tidak dapat menulis tes yang menunjukkan bagaimana generalitas yang Anda terapkan menyelesaikan masalah yang diperlukan oleh spesifikasi maka jangan repot-repot melakukannya! Fitur yang tidak memenuhi kriteria desain dan tidak dapat diuji adalah fitur yang tidak dapat diandalkan oleh siapa pun.

Dan akhirnya, tidak ada yang namanya "seumum mungkin". Misalkan Anda menulis perangkat lunak dalam C #, hanya demi argumen. Apakah Anda akan membuat setiap kelas mengimplementasikan antarmuka? Setiap kelas kelas dasar abstrak dengan setiap metode metode abstrak? Itu cukup umum, tetapi tidak ada yang mendekati "seumum mungkin". Itu hanya memungkinkan orang untuk mengubah implementasi metode apa pun melalui subclassing. Bagaimana jika mereka ingin mengubah implementasi metode tanpa subklas? Anda bisa membuat setiap metode sebenarnya properti tipe delegasi, dengan setter sehingga orang bisa mengubah setiap metode menjadi sesuatu yang lain. Tetapi bagaimana jika seseorang ingin menambahkan lebih banyak metode? Sekarang setiap objek harus diperluas.

Pada titik ini Anda harus meninggalkan C # dan merangkul JavaScript. Tetapi Anda masih belum mendekati cukup umum; bagaimana jika seseorang ingin mengubah cara pencarian anggota bekerja untuk objek khusus ini? Mungkin Anda harus menulis semuanya dengan Python.

Meningkatnya generalitas seringkali berarti mengabaikan prediktabilitas dan meningkatkan biaya pengujian secara besar-besaran untuk memastikan bahwa generalisasi yang diterapkan benar-benar berhasil dalam memenuhi kebutuhan pengguna yang sebenarnya . Apakah biaya-biaya itu dibenarkan dengan manfaatnya bagi para pemangku kepentingan yang membayarnya? Mungkin mereka; terserah Anda untuk berdiskusi dengan para pemangku kepentingan. Stakeholder saya sama sekali tidak bersedia bagi saya untuk meninggalkan pengetikan statis dalam mengejar tingkat generalisasi yang sama sekali tidak perlu dan sangat mahal, tetapi mungkin milik Anda.

Eric Lippert
sumber
2
+1 untuk prinsip-prinsip yang bermanfaat, -1 untuk membuat semuanya tentang uang.
Mason Wheeler
4
@Mason: Ini bukan tentang uang , ini tentang usaha . Uang adalah ukuran praktis dari upaya . Efisiensi adalah manfaat yang diperoleh per upaya yang dihasilkan; lagi, laba dalam uang adalah ukuran praktis dari manfaat yang diperoleh . Lebih mudah untuk membahas abstraksi uang daripada menghasilkan beberapa cara untuk mengukur upaya, biaya, dan manfaat. Apakah Anda lebih suka mengukur upaya, biaya, dan manfaat dengan cara lain? Jangan ragu untuk mengajukan proposal dengan cara yang lebih baik.
Eric Lippert
2
Saya setuju dengan poin @Mason Wheeler. Saya menemukan solusinya di sini adalah tidak melibatkan para pemangku kepentingan di tingkat proyek tersebut. Ini jelas sangat tergantung pada industri, tetapi klien biasanya tidak melihat kode. Juga, semua jawaban ini tampaknya anti-usaha, tampaknya malas.
Orbling
2
@Orbling: Saya sangat menentang upaya yang tidak perlu, mahal, boros yang mengambil sumber daya dari proyek-proyek yang berharga dan penting. Jika Anda menyarankan bahwa ini membuat saya "malas" maka saya sampaikan bahwa Anda menggunakan kata "malas" dengan cara yang tidak biasa.
Eric Lippert
1
Dalam pengalaman saya, proyek-proyek lain cenderung tidak hilang, sehingga biasanya bernilai investasi sebanyak waktu yang diperlukan dalam proyek saat ini sebelum melanjutkan.
Orbling
5

Hanya untuk memperjelas, membuat sesuatu digeneralisasi dan mengimplementasikan abstraksi adalah dua hal yang sepenuhnya berbeda.

Misalnya, pertimbangkan fungsi yang menyalin memori.

Fungsi ini adalah abstraksi yang menyembunyikan bagaimana 4 byte disalin.

int copy4Bytes (char * pSrc, char * pDest)

Generalisasi akan membuat fungsi ini menyalin sejumlah byte.

int copyBytes (char * pSrc, char * pDest, int numBytesToCopy)

Abstraksi cenderung digunakan kembali, sedangkan generalisasi hanya membuat abstraksi berguna dalam lebih banyak kasus.

Lebih khusus terkait dengan pertanyaan Anda, Abstraksi tidak hanya berguna dari perspektif penggunaan kembali kode. Seringkali jika dilakukan dengan benar akan membuat kode Anda lebih mudah dibaca dan dipelihara. Dengan menggunakan contoh di atas, apa yang lebih mudah dibaca dan dipahami jika Anda membaca sekilas kode copyBytes () atau loop untuk iterasi melalui array yang memindahkan data satu indeks pada suatu waktu? Abstraksi dapat memberikan semacam dokumentasi mandiri yang menurut saya membuat kode lebih mudah untuk dikerjakan.

Sebagai aturan praktis saya sendiri, jika saya bisa menghasilkan nama fungsi yang bagus yang menggambarkan dengan tepat apa yang saya ingin lakukan, maka saya menulis sebuah fungsi untuk itu terlepas dari apakah saya pikir saya akan menggunakannya lagi atau tidak.

Pemda
sumber
+1 untuk membuat perbedaan antara abstraksi dan generalisasi.
Joris Meys
4

Aturan umum yang baik untuk hal semacam ini adalah Zero, One, Infinity. Artinya, jika Anda membutuhkan apa yang Anda tulis lebih dari sekali, Anda dapat menganggap Anda akan membutuhkannya lebih banyak dan menggeneralisasi. Aturan ini menyiratkan bahwa Anda tidak repot-repot dengan abstraksi saat pertama kali Anda menulis sesuatu.

Alasan bagus lainnya untuk aturan ini adalah bahwa saat pertama kali Anda menulis kode, Anda tidak perlu tahu apa yang harus diabstraksi karena Anda hanya memiliki satu contoh. Hukum Murphy menyiratkan bahwa jika Anda menulis kode abstrak pertama kali, contoh kedua akan memiliki perbedaan yang tidak Anda antisipasi.

Larry Coleman
sumber
1
Kedengarannya sangat mirip dengan versi saya dari Aturan Refactoring: pukul dua! refactor!
Frank Shearar
@ Frank: Mungkin itu adalah Aturan Refactoring Anda, tetapi dengan paragraf kedua ditambahkan dari pengalaman pribadi.
Larry Coleman
+1: Saya percaya ini juga dicatat cukup awal di The Pragmatic Programmer hampir kata demi kata IIRC.
Steven Evers
oh tentu saja. Tidak ada yang bisa diabstraksi dengan hanya satu contoh: segera setelah Anda memiliki contoh kedua, Anda dapat melihat apa yang umum (dibagi) dan apa yang tidak, dan Anda dapat mengabstraksi!
Frank Shearar
Sampel dua menurut saya terlalu kecil untuk digeneralisasi hingga tak terbatas. Dengan kata lain, terlalu dini.
2

Eric Lippert menunjukkan tiga hal yang menurut saya berlaku dalam artikelnya di masa depan yang membuktikan sebuah desain . Saya pikir jika Anda mengikutinya Anda akan berada dalam kondisi yang baik.

Pertama: Generalitas prematur itu mahal.

Kedua: Mewakili dalam model Anda hanya hal-hal yang selalu ada dalam domain masalah dan hubungan kelasnya tidak berubah.

Ketiga: Jauhkan kebijakan Anda dari mekanisme Anda.

Conrad Frix
sumber
1

Itu tergantung pada mengapa Anda mengkode, tujuan dari proyek Anda. Jika nilai kode Anda adalah yang memecahkan masalah nyata, maka keinginan Anda menyelesaikannya dan beralih ke masalah berikutnya. Jika ada hal-hal cepat dan mudah yang dapat Anda lakukan untuk membuat segalanya lebih mudah bagi pengguna kode masa depan (termasuk diri Anda sendiri) maka dengan segala cara, buatlah akomodasi yang masuk akal.

Di sisi lain, ada beberapa kasus di mana kode yang Anda tulis memiliki tujuan yang lebih umum. Misalnya, ketika Anda menulis perpustakaan untuk programmer lain, yang akan menggunakannya dalam berbagai aplikasi. Calon pengguna tidak diketahui dan Anda tidak bisa menanyakan apa yang mereka inginkan. Tetapi Anda ingin menjadikan perpustakaan Anda bermanfaat secara luas. Dalam kasus seperti itu, saya akan menghabiskan lebih banyak waktu untuk mencoba mendukung solusi umum.

Rob Weir
sumber
0

Saya penggemar berat prinsip KISS.

Saya fokus pada memberikan apa yang saya diminta untuk lakukan, dan bukan pada apa solusi terbaik. Saya harus melepaskan perfeksionisme (dan OCD), karena itu membuat saya sengsara.

Pablo
sumber
Mengapa tidak menyukai perfeksionisme? (
Omong
Tidak mengincar kesempurnaan bekerja untuk saya. Mengapa? Karena saya tahu program saya tidak akan pernah sempurna. Tidak ada definisi standar tentang kesempurnaan, jadi saya hanya memilih untuk memberikan sesuatu yang berfungsi dengan baik.
Pablo
-1

di mana Abstraksi ada di bahu kanan Anda dan Solve-it-stupid duduk di sebelah kiri.

Saya tidak setuju dengan "selesaikan itu bodoh" , saya pikir itu bisa lebih "selesaikan itu pintar" .

Apa yang lebih pintar:

  • Menulis solusi umum yang kompleks yang dapat mendukung banyak kasus
  • Menulis kode pendek dan efisien yang memecahkan masalah di tangan dan mudah untuk mempertahankan, dan yang dapat menjadi diperpanjang di masa depan JIKA diperlukan.

Pilihan default harus yang kedua. Kecuali Anda dapat menunjukkan perlunya generasi.

Solusi umum harus hanya ketika Anda tahu bahwa itu adalah sesuatu yang akan digunakan dalam berbagai proyek / kasus.

Biasanya saya menemukan solusi umum yang terbaik dilayani sebagai "Core Library Code"

Malam gelap
sumber
Jika Anda bisa membuat semua asumsi di sini, Anda tidak akan punya masalah. Pendek dan mudah dirawat saling eksklusif. Jika Anda tahu sesuatu akan digunakan kembali, solusi umum akan mengatasinya.
JeffO
@ Jeff, aku tidak yakin aku mengerti komentarmu ..
Darknight
Anda mengambil "Solve it Stupid" di luar konteks.
Bryan Harrington