Bagaimana saya tahu bagaimana metode saya bisa digunakan kembali? [Tutup]


133

Saya mengurus bisnis saya sendiri di rumah dan istri saya mendatangi saya dan berkata

Sayang .. Bisakah Anda mencetak semua Day Light Savings di seluruh dunia untuk tahun 2018 di konsol? Saya perlu memeriksa sesuatu.

Dan saya sangat senang karena itulah yang telah saya tunggu sepanjang hidup saya dengan pengalaman Java saya dan datang dengan:

import java.time.*;
import java.util.Set;

class App {
    void dayLightSavings() {
        final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        availableZoneIds.forEach(
            zoneId -> {
                LocalDateTime dateTime = LocalDateTime.of(
                    LocalDate.of(2018, 1, 1), 
                    LocalTime.of(0, 0, 0)
                );
                ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
                while (2018 == now.getYear()) {
                    int hour = now.getHour();
                    now = now.plusHours(1);
                    if (now.getHour() == hour) {
                        System.out.println(now);
                    }
                }
            }
        );
    }
}

Tapi kemudian dia bilang dia baru saja menguji saya apakah saya seorang insinyur perangkat lunak yang terlatih secara etis, dan mengatakan kepada saya sepertinya saya tidak sejak itu (diambil dari sini ) ..

Perlu dicatat bahwa tidak ada insinyur perangkat lunak yang terlatih secara etis yang akan menyetujui untuk menulis prosedur DestroyBaghdad. Etika profesional dasar malah mengharuskannya untuk menulis prosedur DestroyCity, yang dapat diberikan Baghdad sebagai parameter.

Dan saya seperti, baik, ok, Anda punya saya .. Lewati setiap tahun yang Anda suka, ini dia:

import java.time.*;
import java.util.Set;

class App {
    void dayLightSavings(int year) {
        final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        availableZoneIds.forEach(
            zoneId -> {
                LocalDateTime dateTime = LocalDateTime.of(
                    LocalDate.of(year, 1, 1), 
                    LocalTime.of(0, 0, 0)
                );
                ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
                while (year == now.getYear()) {
                    // rest is same..

Tetapi bagaimana saya tahu berapa banyak (dan apa) yang harus diukur? Bagaimanapun, dia mungkin berkata ..

  • dia ingin melewati pemformat string khusus, mungkin dia tidak suka format yang sudah saya cetak: 2018-10-28T02:00+01:00[Arctic/Longyearbyen]

void dayLightSavings(int year, DateTimeFormatter dtf)

  • dia hanya tertarik pada periode bulan tertentu

void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd)

  • dia tertarik pada periode jam tertentu

void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd, int hourStart, int hourend)

Jika Anda mencari pertanyaan konkret:

Jika destroyCity(City city)lebih baik daripada destroyBaghdad(), apakah takeActionOnCity(Action action, City city)lebih baik? Kenapa / mengapa tidak?

Setelah semua, saya pertama kali bisa menyebutnya dengan Action.DESTROYitu Action.REBUILD, bukan?

Tetapi mengambil tindakan di kota tidak cukup bagi saya, bagaimana takeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)? Lagi pula, saya tidak ingin menelepon:

takeActionOnCity(Action.DESTORY, City.BAGHDAD);

kemudian

takeActionOnCity(Action.DESTORY, City.ERBIL);

dan seterusnya ketika saya bisa melakukan:

takeActionOnGeographicArea(Action.DESTORY, Country.IRAQ);

ps Saya hanya membangun pertanyaan saya berdasarkan kutipan yang saya sebutkan, saya tidak menentang negara, agama, ras atau apa pun di dunia. Saya hanya mencoba membuat suatu poin.



71
Poin yang Anda sampaikan di sini adalah yang telah saya coba ungkapkan berkali-kali: sifat umum itu mahal, dan karenanya harus dibenarkan oleh manfaat yang spesifik dan jelas . Tapi itu lebih dalam dari itu; bahasa pemrograman dibuat oleh desainer mereka untuk membuat beberapa jenis generalisasi lebih mudah daripada yang lain, dan itu mempengaruhi pilihan kita sebagai pengembang. Adalah mudah untuk membuat parameter suatu metode berdasarkan suatu nilai, dan ketika itu adalah alat termudah yang Anda miliki di kotak alat Anda, godaan adalah untuk menggunakannya terlepas dari apakah itu masuk akal bagi pengguna.
Eric Lippert

30
Penggunaan kembali bukanlah sesuatu yang Anda inginkan untuk kepentingannya sendiri. Kami memprioritaskan penggunaan kembali karena kami memiliki keyakinan bahwa artefak kode mahal untuk dibangun dan oleh karena itu harus dapat digunakan dalam skenario sebanyak mungkin, untuk mengamortisasi biaya tersebut di seluruh skenario tersebut. Keyakinan ini sering tidak dibenarkan oleh pengamatan, dan saran untuk merancang untuk digunakan kembali karena itu sering salah diterapkan . Rancang kode Anda untuk menurunkan total biaya aplikasi .
Eric Lippert

7
Istri Anda adalah orang yang tidak etis karena membuang-buang waktu dengan membohongi Anda. Dia meminta jawaban, dan memberikan media yang disarankan; Dengan kontrak itu, bagaimana Anda mendapatkan output hanya antara Anda dan diri Anda sendiri. Juga, destroyCity(target)jauh lebih tidak etis daripada destroyBagdad()! Monster apa yang menulis program untuk memusnahkan sebuah kota, apalagi kota di dunia? Bagaimana jika sistem itu dikompromikan? Juga, apa hubungan manajemen waktu / sumber daya (upaya yang diinvestasikan) dengan etika? Selama kontrak lisan / tertulis diselesaikan sesuai kesepakatan.
Tezra

25
Saya pikir Anda mungkin terlalu banyak membaca lelucon ini. Ini adalah lelucon tentang bagaimana programmer komputer datang untuk membuat keputusan etis yang buruk, karena mereka memprioritaskan pertimbangan teknis atas efek dari pekerjaan mereka pada manusia. Itu tidak dimaksudkan untuk menjadi nasihat yang baik tentang desain program.
Eric Lippert

Jawaban:


114

Ini kura-kura sepanjang jalan.

Atau abstraksi dalam hal ini.

Pengkodean praktik yang baik adalah sesuatu yang dapat diterapkan tanpa batas, dan pada titik tertentu Anda melakukan abstraksi untuk mengabstraksi, yang berarti Anda telah mengambilnya terlalu jauh. Menemukan garis itu bukanlah sesuatu yang mudah untuk diterapkan, karena sangat tergantung pada lingkungan Anda.

Sebagai contoh, kami memiliki pelanggan yang dikenal untuk meminta aplikasi sederhana terlebih dahulu tetapi kemudian meminta ekspansi. Kami juga memiliki pelanggan yang menanyakan apa yang mereka inginkan dan umumnya tidak pernah kembali kepada kami untuk ekspansi.
Pendekatan Anda akan bervariasi per pelanggan. Untuk pelanggan pertama, itu akan membayar untuk pra-emptively abstrak kode karena Anda cukup yakin bahwa Anda harus meninjau kembali kode ini di masa depan. Untuk pelanggan kedua, Anda mungkin tidak ingin menginvestasikan upaya ekstra jika Anda mengharapkan mereka untuk tidak ingin memperluas aplikasi pada titik mana pun (catatan: ini tidak berarti bahwa Anda tidak mengikuti praktik yang baik, tetapi hanya bahwa Anda menghindari melakukan lebih dari yang diperlukan saat ini .

Bagaimana saya tahu fitur mana yang harus diterapkan?

Alasan saya menyebutkan hal di atas adalah karena Anda sudah jatuh dalam perangkap ini:

Tetapi bagaimana saya tahu berapa banyak (dan apa) yang harus diukur? Bagaimanapun, dia mungkin berkata .

"Dia mungkin berkata" bukan persyaratan bisnis saat ini. Ini dugaan pada kebutuhan bisnis di masa depan. Sebagai aturan umum, jangan mendasarkan diri pada dugaan, hanya mengembangkan apa yang saat ini diperlukan.

Namun, konteks berlaku di sini. Saya tidak kenal istrimu. Mungkin Anda secara akurat mengukur bahwa dia sebenarnya menginginkan ini. Tetapi Anda harus tetap mengkonfirmasi dengan pelanggan bahwa ini memang yang mereka inginkan, karena jika tidak, Anda akan menghabiskan waktu mengembangkan fitur yang pada akhirnya tidak akan Anda gunakan.

Bagaimana saya tahu arsitektur mana yang harus diimplementasikan?

Ini lebih sulit. Pelanggan tidak peduli dengan kode internal, jadi Anda tidak bisa bertanya apakah mereka membutuhkannya. Pendapat mereka tentang masalah ini sebagian besar tidak relevan.

Namun, Anda masih dapat mengonfirmasi perlunya melakukannya dengan mengajukan pertanyaan yang tepat kepada pelanggan. Alih-alih bertanya tentang arsitektur, tanyakan pada mereka tentang ekspektasi mereka akan pengembangan di masa depan atau perluasan basis kode. Anda juga dapat menanyakan apakah sasaran saat ini memiliki tenggat waktu, karena Anda mungkin tidak dapat mengimplementasikan arsitektur mewah Anda dalam jangka waktu yang diperlukan.

Bagaimana saya tahu kapan harus mengabstraksi kode saya lebih lanjut?

Saya tidak tahu di mana saya membacanya (jika ada yang tahu, beri tahu saya dan saya akan memberikan kredit), tetapi aturan praktis yang baik adalah bahwa pengembang harus menghitung seperti manusia gua: satu, dua banyak .

masukkan deskripsi gambar di sini XKCD # 764

Dengan kata lain, ketika algoritma / pola tertentu sedang digunakan untuk ketiga kalinya, itu harus diabstraksikan sehingga dapat digunakan kembali (= dapat digunakan berkali- kali).

Untuk lebih jelasnya, saya tidak menyiratkan bahwa Anda tidak boleh menulis kode yang dapat digunakan kembali ketika hanya ada dua contoh algoritma yang digunakan. Tentu saja Anda dapat mengabstraksikan itu juga, tetapi aturannya adalah bahwa untuk tiga contoh Anda harus mengabstraksikan.

Sekali lagi, ini faktor dalam harapan Anda. Jika Anda sudah tahu bahwa Anda perlu tiga kali atau lebih, tentu saja Anda dapat langsung abstrak. Tetapi jika Anda hanya menebak bahwa Anda mungkin ingin menerapkannya lebih sering, kebenaran penerapan abstraksi sepenuhnya bergantung pada kebenaran dugaan Anda.
Jika Anda menebak dengan benar, Anda menghemat waktu. Jika Anda salah menebak, Anda menyia-nyiakan waktu dan tenaga Anda dan mungkin membahayakan arsitektur Anda untuk mengimplementasikan sesuatu yang pada akhirnya tidak Anda perlukan.

Jika destroyCity(City city)lebih baik daripada destroyBaghdad(), apakah takeActionOnCity(Action action, City city)lebih baik? Kenapa / mengapa tidak?

Itu sangat tergantung pada banyak hal:

  • Apakah ada beberapa tindakan yang dapat dilakukan di kota mana saja ?
  • Bisakah tindakan ini digunakan secara bergantian? Karena jika tindakan "hancurkan" dan "bangun kembali" memiliki eksekusi yang sama sekali berbeda, maka tidak ada gunanya menggabungkan keduanya dalam satu takeActionOnCitymetode tunggal .

Perlu diketahui juga bahwa jika Anda secara abstrak mengabstraksikan ini, Anda akan berakhir dengan metode yang sangat abstrak sehingga tidak lebih dari sebuah wadah untuk menjalankan metode lain, yang berarti Anda membuat metode Anda tidak relevan dan tidak berarti.
Jika seluruh takeActionOnCity(Action action, City city)badan metode Anda berakhir menjadi tidak lebih dari action.TakeOn(city);, Anda harus bertanya-tanya apakah takeActionOnCitymetode tersebut benar-benar memiliki tujuan atau bukan hanya lapisan tambahan yang tidak menambah nilai.

Tetapi mengambil tindakan di kota tidak cukup bagi saya, bagaimana takeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)?

Pertanyaan yang sama muncul di sini:

  • Apakah Anda memiliki kasus penggunaan untuk wilayah geografis?
  • Apakah eksekusi suatu tindakan pada kota dan wilayah sama?
  • Apakah tindakan apa pun dapat dilakukan pada kawasan / kota mana saja?

Jika Anda dapat secara pasti menjawab "ya" untuk ketiganya, maka abstraksi dijamin.


16
Saya tidak bisa cukup menekankan aturan "satu, dua, banyak". Ada kemungkinan tak terbatas untuk abstrak / parametrize sesuatu, tetapi bagian yang berguna kecil, sering nol. Mengetahui dengan pasti varian mana yang bernilai paling sering hanya dapat ditentukan dalam retrospeksi. Jadi tetap berpegang pada persyaratan langsung * dan tambahkan kompleksitas yang diperlukan oleh persyaratan atau tinjauan baru. * Terkadang Anda tahu ruang masalahnya dengan baik, maka mungkin tidak apa-apa untuk menambahkan sesuatu karena Anda tahu Anda membutuhkannya besok. Tapi gunakan kekuatan ini dengan bijak, itu juga bisa menyebabkan kehancuran.
Christian Sauer

2
> "Saya tidak tahu di mana saya membacanya [..]". Anda mungkin membaca Coding Horror: The Rule of Three .
Rune

10
"Satu, dua, banyak aturan" benar-benar ada untuk mencegah Anda membangun abstraksi yang salah dengan menerapkan KERING secara membabi buta. Masalahnya adalah, dua potong kode dapat mulai terlihat hampir persis sama, sehingga menggoda untuk memisahkan perbedaan; tetapi sejak awal, Anda tidak tahu bagian mana dari kode yang stabil, dan mana yang tidak; selain itu, ternyata mereka benar-benar perlu berevolusi secara independen (pola perubahan yang berbeda, serangkaian tanggung jawab yang berbeda). Dalam kedua kasus tersebut, abstraksi yang salah bekerja melawan Anda dan menghalangi.
Filip Milovanović

4
Menunggu lebih dari dua contoh "logika yang sama" memungkinkan Anda untuk menjadi hakim yang lebih baik tentang apa yang harus diabstraksikan, dan bagaimana (dan sungguh, ini tentang mengelola dependensi / penggabungan antara kode dengan pola perubahan yang berbeda).
Filip Milovanović

1
@kukis: Garis realistis harus ditarik pada 2 (sesuai komentar Baldrickk): nol-satu-banyak (seperti halnya untuk hubungan basis data). Namun, ini membuka pintu bagi perilaku mencari pola yang tidak perlu. Dua hal secara samar-samar terlihat sama tetapi itu tidak berarti keduanya sebenarnya sama. Namun, ketika instance ketiga memasuki pertarungan yang menyerupai instance pertama dan kedua, Anda dapat membuat penilaian yang lebih akurat bahwa kesamaan mereka memang merupakan pola yang dapat digunakan kembali. Jadi garis akal sehat digambarkan pada angka 3, yang merupakan faktor kesalahan manusia ketika menemukan "pola" antara hanya dua contoh.
Flater

44

Praktek

Ini adalah Rekayasa Perangkat Lunak SE, tetapi membuat perangkat lunak lebih banyak seni daripada rekayasa. Tidak ada algoritma universal untuk diikuti atau pengukuran untuk mengetahui berapa banyak reusability sudah cukup. Seperti halnya apa pun, semakin banyak latihan yang Anda dapatkan dalam merancang program, semakin baik Anda melakukannya. Anda akan mendapatkan perasaan yang lebih baik tentang apa yang "cukup" karena Anda akan melihat apa yang salah dan bagaimana kesalahannya ketika Anda mengukur terlalu banyak atau terlalu sedikit.

Itu tidak terlalu membantu sekarang , jadi bagaimana dengan beberapa panduan?

Lihat kembali pertanyaan Anda. Ada banyak "dia mungkin berkata" dan "aku bisa". Banyak pernyataan berteori tentang beberapa kebutuhan masa depan. Manusia malu memprediksi masa depan. Dan Anda (kemungkinan besar) adalah manusia. Masalah besar dari desain perangkat lunak adalah mencoba menjelaskan masa depan yang tidak Anda ketahui.

Pedoman 1: Anda Tidak Akan Membutuhkannya

Serius. Berhenti saja. Lebih sering daripada tidak, bahwa masalah masa depan yang dibayangkan tidak muncul - dan itu pasti tidak akan muncul seperti yang Anda bayangkan.

Pedoman 2: Biaya / Manfaat

Keren, program kecil itu butuh waktu beberapa jam untuk menulis, mungkin? Jadi bagaimana jika istri Anda tidak datang kembali dan meminta hal-hal? Kasus terburuk, Anda menghabiskan beberapa jam lagi untuk bergabung bersama program lain untuk melakukannya. Untuk kasus ini, tidak terlalu banyak waktu untuk membuat program ini lebih fleksibel. Dan itu tidak akan menambah banyak kecepatan runtime atau penggunaan memori. Tetapi program non-sepele memiliki jawaban yang berbeda. Skenario yang berbeda memiliki jawaban yang berbeda pula. Pada titik tertentu, biaya jelas tidak sepadan dengan manfaatnya bahkan dengan keterampilan menceritakan masa depan yang tidak sempurna.

Pedoman 3: Fokus pada konstanta

Lihat kembali pertanyaannya. Di kode asli Anda, ada banyak int konstan. 2018, 1. Int konstan, string konstan ... Mereka adalah hal yang paling mungkin perlu tidak konstan . Lebih baik lagi, mereka hanya membutuhkan sedikit waktu untuk membuat parameter (atau setidaknya mendefinisikan sebagai konstanta aktual). Tetapi hal lain yang harus diwaspadai adalah perilaku konstan . ItuSystem.out.printlnsebagai contoh. Asumsi semacam itu tentang penggunaan cenderung menjadi sesuatu yang berubah di masa depan dan cenderung sangat mahal untuk diperbaiki. Bukan hanya itu, tetapi IO seperti ini membuat fungsi tidak murni (bersama dengan zona waktu mengambil agak). Parameterisasi perilaku itu dapat membuat fungsi lebih murni yang mengarah pada peningkatan fleksibilitas dan pengujian. Manfaat besar dengan biaya minimal (terutama jika Anda membuat kelebihan yang digunakan System.outsecara default).


1
Itu hanya pedoman, angka 1 baik-baik saja tetapi Anda melihat mereka dan pergi "apakah ini akan pernah berubah?" Nah. Dan println dapat diparameterisasi dengan fungsi urutan yang lebih tinggi - meskipun Java tidak terlalu bagus.
Telastyn

5
@ KorayTugay: jika program itu benar-benar untuk istri Anda yang datang ke rumah, YAGNI akan memberi tahu Anda bahwa versi awal Anda sempurna, dan Anda tidak boleh menginvestasikan waktu lagi untuk memperkenalkan konstanta atau parameter. YAGNI membutuhkan konteks - apakah program Anda merupakan solusi yang dibuang, atau program migrasi hanya berjalan selama beberapa bulan, atau apakah itu bagian dari sistem ERP yang besar, yang dimaksudkan untuk digunakan dan dikelola selama beberapa dekade?
Doc Brown

8
@ KorayTugay: Memisahkan I / O dari perhitungan adalah teknik penataan program mendasar. Pisahkan pembuatan data dari penyaringan data dari transformasi data dari konsumsi data dari penyajian data. Anda harus mempelajari beberapa program fungsional, maka Anda akan melihat ini dengan lebih jelas. Dalam pemrograman fungsional, sangat umum untuk menghasilkan data dalam jumlah tak terbatas, memfilter hanya data yang Anda minati, mengubah data menjadi format yang Anda butuhkan, membuat string dari itu, dan mencetak string ini dalam 5 fungsi yang berbeda, satu untuk setiap langkah.
Jörg W Mittag

3
Sebagai seorang sidenote, sangat mengikuti YAGNI mengarah pada kebutuhan untuk terus-menerus refactor: "Digunakan tanpa refactoring terus-menerus, itu dapat menyebabkan kode tidak terorganisir dan pengerjaan ulang besar-besaran, yang dikenal sebagai utang teknis." Jadi walaupun YAGNI adalah hal yang baik secara umum, YAGNI hadir dengan tanggung jawab besar untuk meninjau kembali dan mengevaluasi kembali kode, yang bukan sesuatu yang bersedia dilakukan oleh setiap pengembang / perusahaan.
Flater

4
@Telastyn: Saya menyarankan untuk memperluas pertanyaan ke “apakah ini tidak akan pernah berubah dan apakah maksud dari kode tersebut dapat dibaca sepele tanpa menyebutkan konstanta ?” Bahkan untuk nilai yang tidak pernah berubah, mungkin relevan untuk memberi nama mereka hanya agar semuanya tetap dapat dibaca.
Flater

27

Pertama: Tidak ada pengembang perangkat lunak yang berpikiran keamanan akan menulis metode DestroyCity tanpa melewati Token Otorisasi untuk alasan apa pun.

Saya juga bisa menulis apa pun sebagai keharusan yang memiliki kebijaksanaan jelas tanpa itu dapat diterapkan dalam konteks lain. Mengapa perlu mengesahkan penggabungan string?

Kedua: Semua kode ketika dieksekusi harus ditentukan sepenuhnya .

Tidak masalah apakah keputusan itu dikodekan dengan keras, atau ditangguhkan ke lapisan lain. Pada titik tertentu ada sepotong kode dalam beberapa bahasa yang tahu apa yang harus dihancurkan dan bagaimana cara menginstruksikannya.

Itu bisa di file objek yang sama destroyCity(xyz), dan itu bisa dalam file konfigurasi:, destroy {"city": "XYZ"}"atau mungkin serangkaian klik dan penekanan tombol di UI.

Ketiga:

Sayang .. Bisakah Anda mencetak semua Day Light Savings di seluruh dunia untuk tahun 2018 di konsol? Saya perlu memeriksa sesuatu.

adalah serangkaian persyaratan yang sangat berbeda untuk:

dia ingin melewati pemformat string khusus, ... hanya tertarik pada periode bulan tertentu, ... [dan] tertarik pada periode jam tertentu ...

Sekarang persyaratan kedua jelas membuat alat yang lebih fleksibel. Ini memiliki target audiens yang lebih luas, dan ranah aplikasi yang lebih luas. Bahayanya di sini adalah bahwa aplikasi yang paling fleksibel di dunia sebenarnya adalah kompiler untuk kode mesin. Ini benar-benar sebuah program sehingga generik dapat membangun apa pun untuk membuat komputer apa pun yang Anda inginkan (dalam batasan perangkat kerasnya).

Secara umum orang yang membutuhkan perangkat lunak tidak menginginkan sesuatu yang generik; mereka menginginkan sesuatu yang spesifik. Dengan memberikan lebih banyak pilihan, Anda sebenarnya membuat hidup mereka lebih rumit. Jika mereka menginginkan kompleksitas itu, mereka malah akan menggunakan kompiler, bukan meminta Anda.

Istri Anda meminta fungsionalitas, dan persyaratannya tidak ditentukan untuk Anda. Dalam hal ini sepertinya sengaja, dan secara umum itu karena mereka tidak tahu apa-apa. Kalau tidak, mereka hanya akan menggunakan kompiler sendiri. Jadi masalah pertama adalah Anda tidak meminta rincian lebih lanjut tentang apa yang ingin ia lakukan. Apakah dia ingin menjalankan ini selama beberapa tahun yang berbeda? Apakah dia menginginkannya dalam file CSV? Anda tidak menemukan keputusan apa yang ingin dia buat sendiri, dan apa yang dia minta agar Anda pikirkan dan putuskan. Setelah Anda mengetahui keputusan apa yang perlu ditangguhkan, Anda dapat mengetahui bagaimana mengkomunikasikan keputusan tersebut melalui parameter (dan cara-cara lain yang dapat dikonfigurasi).

Yang sedang berkata, sebagian besar klien salah berkomunikasi, menganggap, atau tidak mengetahui detail tertentu (alias. Keputusan) bahwa mereka benar-benar ingin membuat diri mereka sendiri, atau bahwa mereka benar-benar tidak ingin membuat (tetapi kedengarannya luar biasa). Inilah sebabnya mengapa metode kerja seperti PDSA (rencana-kembangkan-studi-tindakan) penting. Anda telah merencanakan pekerjaan sesuai dengan persyaratan, dan kemudian Anda mengembangkan serangkaian keputusan (kode). Sekarang saatnya mempelajarinya, baik sendiri atau dengan klien Anda dan mempelajari hal-hal baru, dan ini menginformasikan pemikiran Anda ke depan. Terakhir, tindak lanjuti wawasan baru Anda - perbarui persyaratan, perbaiki proses, dapatkan alat baru, dll ... Kemudian mulai perencanaan lagi. Ini akan mengungkapkan persyaratan tersembunyi dari waktu ke waktu, dan membuktikan kemajuan bagi banyak klien.

Akhirnya. Waktu Anda penting ; ini sangat nyata dan sangat terbatas. Setiap keputusan yang Anda buat melibatkan banyak keputusan tersembunyi lainnya, dan ini adalah tentang pengembangan perangkat lunak. Menunda keputusan sebagai argumen dapat membuat fungsi saat ini lebih sederhana, tetapi itu membuat tempat lain lebih kompleks. Apakah keputusan itu relevan di lokasi lain itu? Apakah ini lebih relevan di sini? Keputusan siapa yang benar-benar dibuat? Anda memutuskan ini; ini coding. Jika Anda sering mengulangi set keputusan, ada manfaat yang sangat nyata dalam menyusunnya dalam beberapa abstraksi. XKCD memiliki perspektif yang bermanfaat di sini. Dan ini relevan pada level sistem baik itu fungsi, modul, program, dll.

Nasihat di awal menyiratkan bahwa keputusan yang tidak berhak dilakukan oleh fungsi Anda harus diajukan sebagai argumen. Masalahnya adalah suatu DestroyBaghdadfungsi sebenarnya adalah fungsi yang memiliki hak tersebut.


+1 suka bagian tentang kompiler!
Lee

4

Ada banyak jawaban panjang lebar di sini, tapi jujur ​​saya pikir itu sangat sederhana

Setiap informasi berkode keras yang Anda miliki di fungsi Anda yang bukan bagian dari nama fungsi harus menjadi parameter.

jadi dalam fungsi Anda

class App {
    void dayLightSavings() {
        final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        availableZoneIds.forEach(zoneId -> {
            LocalDateTime dateTime = LocalDateTime.of(LocalDate.of(2018, 1, 1), LocalTime.of(0, 0, 0));
            ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
            while (2018 == now.getYear()) {
                int hour = now.getHour();
                now = now.plusHours(1);
                if (now.getHour() == hour) {
                    System.out.println(now);
                }
            }
        });
    }
}

Kamu punya:

The zoneIds
2018, 1, 1
System.out

Jadi saya akan memindahkan semua ini ke parameter dalam satu bentuk atau lainnya. Anda bisa berpendapat bahwa zoneIds tersirat dalam nama fungsi, mungkin Anda ingin membuatnya lebih dengan mengubahnya menjadi "DaylightSavingsAroundTheWorld" atau sesuatu

Anda tidak memiliki string format, jadi menambahkan satu adalah permintaan fitur dan Anda harus merujuk istri Anda ke instance Jira keluarga Anda. Ini dapat dimasukkan ke dalam simpanan dan diprioritaskan pada pertemuan komite manajemen proyek yang sesuai.


1
Anda (mengarahkan diri ke OP) tentu saja tidak boleh menambahkan string format, karena Anda tidak boleh mencetak apa pun. Satu hal tentang kode ini yang benar-benar mencegah penggunaannya kembali adalah bahwa ia mencetak. Seharusnya mengembalikan zona, atau peta zona ketika mereka mematikan DST. (Meskipun mengapa itu hanya mengidentifikasi ketika DST pergi , dan bukan pada DST, saya tidak mengerti. Tampaknya tidak cocok dengan pernyataan masalah.)
David Conrad

persyaratannya adalah mencetak ke konsol. Anda dapat mitgate couplung ketat dengan melewatkan dalam aliran output sebagai parameter seperti yang saya sarankan
Ewan

1
Meski begitu, jika Anda ingin kode tersebut dapat digunakan kembali, Anda tidak harus mencetak ke konsol. Tulis metode yang mengembalikan hasilnya, lalu tulis pemanggil yang mendapatkannya dan cetak. Itu juga membuatnya dapat diuji. Jika Anda ingin itu menghasilkan output, saya tidak akan lulus dalam aliran output, saya akan lulus dalam Konsumen.
David Conrad

aliran ourput adalah konsumen
Ewan


4

Singkatnya, jangan merekayasa perangkat lunak Anda untuk dapat digunakan kembali karena tidak ada pengguna akhir yang peduli jika fungsi Anda dapat digunakan kembali. Sebaliknya, insinyur untuk kelengkapan desain - apakah kode saya mudah bagi orang lain atau masa depan saya yang pelupa untuk mengerti? - dan fleksibilitas desain- ketika saya mau tidak mau harus memperbaiki bug, menambah fitur, atau memodifikasi fungsionalitas, seberapa banyak kode saya akan menolak perubahan? Satu-satunya hal yang diperhatikan pelanggan Anda adalah seberapa cepat Anda dapat merespons ketika dia melaporkan bug atau meminta perubahan. Mengajukan pertanyaan ini tentang desain Anda secara tidak sengaja cenderung menghasilkan kode yang dapat digunakan kembali, tetapi pendekatan ini membuat Anda tetap fokus pada menghindari masalah nyata yang akan Anda hadapi sepanjang umur kode tersebut sehingga Anda dapat melayani pengguna akhir dengan lebih baik daripada mengejar yang tinggi, tidak praktis. "rekayasa" cita-cita untuk menyenangkan jenggot leher.

Untuk sesuatu yang sederhana seperti contoh yang Anda berikan, implementasi awal Anda baik-baik saja karena betapa kecilnya, tetapi desain langsung ini akan menjadi sulit untuk dipahami dan rapuh jika Anda mencoba memasukkan terlalu banyak fleksibilitas fungsional (sebagai lawan dari fleksibilitas desain) ke dalam satu prosedur. Di bawah ini adalah penjelasan saya tentang pendekatan yang saya sukai untuk merancang sistem yang kompleks untuk kelengkapan dan fleksibilitas yang saya harap akan menunjukkan apa yang saya maksud dengan mereka. Saya tidak akan menggunakan strategi ini untuk sesuatu yang dapat ditulis dalam kurang dari 20 baris dalam satu prosedur karena sesuatu yang sangat kecil sudah memenuhi kriteria saya untuk kelengkapan dan fleksibilitas.


Objek, bukan Prosedur

Daripada menggunakan kelas-kelas seperti modul old-school dengan banyak rutinitas yang Anda panggil untuk melakukan hal-hal yang seharusnya dilakukan perangkat lunak Anda, pertimbangkan untuk memodelkan domain sebagai objek yang berinteraksi dan bekerja sama untuk menyelesaikan tugas yang ada. Metode-metode dalam paradigma Berorientasi Objek pada awalnya diciptakan untuk menjadi sinyal di antara objek-objek sehingga Object1dapat Object2menentukan untuk melakukan hal tersebut, apa pun itu, dan mungkin menerima sinyal balik. Ini karena paradigma Berorientasi Objek secara inheren tentang pemodelan objek domain Anda dan interaksinya daripada cara mewah untuk mengatur fungsi dan prosedur lama yang sama dari paradigma Imperatif. Dalam halvoid destroyBaghdadmisalnya, alih-alih mencoba menulis metode yang kurang umum konteks untuk menangani penghancuran Baghdad atau hal lain (yang dapat dengan cepat tumbuh kompleks, sulit dipahami, dan rapuh), setiap hal yang dapat dihancurkan harus bertanggung jawab untuk memahami bagaimana untuk menghancurkan dirinya sendiri. Misalnya, Anda memiliki antarmuka yang menggambarkan perilaku hal-hal yang dapat dihancurkan:

interface Destroyable {
    void destroy();
}

Maka Anda memiliki kota yang mengimplementasikan antarmuka ini:

class City implements Destroyable {
    @Override
    public void destroy() {
        ...code that destroys the city
    }
}

Tidak ada yang menyerukan penghancuran instance Cityakan pernah peduli bagaimana itu terjadi, jadi tidak ada alasan untuk kode itu ada di mana pun di luar City::destroy, dan memang, pengetahuan intim tentang cara kerja bagian dalam Citydari dirinya sendiri akan menjadi kopling ketat yang mengurangi karena Anda harus mempertimbangkan elemen-elemen luar itu jika Anda perlu memodifikasi perilaku City. Ini adalah tujuan sebenarnya di balik enkapsulasi. Anggap saja seperti setiap objek memiliki API sendiri yang memungkinkan Anda untuk melakukan apa pun yang Anda butuhkan sehingga Anda dapat membiarkannya khawatir tentang memenuhi permintaan Anda.

Delegasi, bukan "Kontrol"

Sekarang, apakah kelas implementasi Anda Cityatau Baghdadtergantung pada seberapa umum proses menghancurkan kota itu ternyata. Dalam semua kemungkinan, a Cityakan terdiri dari potongan-potongan kecil yang perlu dihancurkan secara individual untuk mencapai kehancuran total kota, jadi dalam hal itu, masing-masing potongan juga akan diterapkan Destroyable, dan mereka masing-masing akan diperintahkan oleh Cityuntuk menghancurkan sendiri dengan cara yang sama seseorang dari luar meminta Cityuntuk menghancurkan dirinya sendiri.

interface Part extends Destroyable {
    ...part-specific methods
}

class Building implements Part {
    ...part-specific methods
    @Override
    public void destroy() {
       ...code to destroy a building
    }
}

class Street implements Part {
    ...part-specific methods
    @Override
    public void destroy() {
        ...code to destroy a building
    }
}

class City implements Destroyable {
    public List<Part> parts() {...}

    @Override
    public void destroy() {
        parts().forEach(Destroyable::destroy);            
    }
}

Jika Anda ingin menjadi benar-benar gila dan menerapkan gagasan Bombyang dijatuhkan di lokasi dan menghancurkan segala sesuatu dalam radius tertentu, itu mungkin terlihat seperti ini:

class Bomb {
    private final Integer radius;

    public Bomb(final Integer radius) {
        this.radius = radius;
    }

    public void drop(final Grid grid, final Coordinate target) {
        new ObjectsByRadius(
            grid,
            target,
            this.radius
        ).forEach(Destroyable::destroy);
    }
}

ObjectsByRadiusmewakili seperangkat objek yang dihitung untuk Bombdari input karena Bombtidak peduli bagaimana perhitungan itu dibuat selama ia dapat bekerja dengan objek. Ini dapat digunakan kembali secara tidak disengaja, tetapi tujuan utamanya adalah untuk mengisolasi perhitungan dari proses menjatuhkan Bombdan menghancurkan objek sehingga Anda dapat memahami setiap bagian dan bagaimana mereka cocok bersama-sama dan mengubah perilaku masing-masing bagian tanpa harus membentuk kembali seluruh algoritma .

Interaksi, bukan Algoritma

Alih-alih mencoba menebak pada jumlah parameter yang tepat untuk algoritma yang kompleks, lebih masuk akal untuk memodelkan proses sebagai satu set objek yang berinteraksi, masing-masing dengan peran yang sangat sempit, karena itu akan memberi Anda kemampuan untuk memodelkan kompleksitas kompleksitas Anda. proses melalui interaksi antara objek yang terdefinisi dengan baik, mudah dipahami, dan hampir tidak berubah ini. Ketika dilakukan dengan benar, ini membuat bahkan beberapa modifikasi paling kompleks sepele seperti mengimplementasikan satu atau dua antarmuka dan pengerjaan ulang objek mana yang dipakai dalam main()metode Anda .

Saya akan memberi Anda sesuatu untuk contoh asli Anda, tetapi saya jujur ​​tidak tahu apa artinya "mencetak ... Penghematan Day Light." Apa yang bisa saya katakan tentang kategori masalah itu adalah bahwa setiap kali Anda melakukan perhitungan, yang hasilnya dapat diformat dengan beberapa cara, cara saya yang lebih disukai untuk menguraikannya adalah seperti ini:

interface Result {
    String print();
}

class Caclulation {
    private final Parameter paramater1;

    private final Parameter parameter2;

    public Calculation(final Parameter parameter1, final Parameter parameter2) {
        this.parameter1 = parameter1;
        this.parameter2 = parameter2;
    }

    public Result calculate() {
        ...calculate the result
    }
}

class FormattedResult {
    private final Result result;

    public FormattedResult(final Result result) {
        this.result = result;
    }

    @Override
    public String print() {
        ...interact with this.result to format it and return the formatted String
    }
}

Karena contoh Anda menggunakan kelas dari perpustakaan Java yang tidak mendukung desain ini, Anda bisa menggunakan API ZonedDateTimesecara langsung. Idenya di sini adalah bahwa setiap perhitungan dirangkum dalam objeknya sendiri. Itu tidak membuat asumsi tentang berapa kali harus berjalan atau bagaimana harus memformat hasilnya. Ini secara khusus berkaitan dengan melakukan bentuk perhitungan yang paling sederhana. Ini membuatnya mudah dipahami dan fleksibel untuk diubah. Demikian juga, Resultsecara eksklusif berkaitan dengan merangkum hasil perhitungan, dan FormattedResultsecara eksklusif berkaitan dengan berinteraksi dengan Resultmemformatnya sesuai dengan aturan yang kami tetapkan. Lewat sini,kita dapat menemukan jumlah argumen yang sempurna untuk masing-masing metode kita karena masing-masing memiliki tugas yang jelas . Ini juga jauh lebih mudah untuk memodifikasi bergerak maju selama antarmuka tidak berubah (yang tidak mungkin dilakukan jika Anda meminimalkan tanggung jawab objek Anda dengan benar). main()Metodekamimungkin terlihat seperti ini:

class App {
    public static void main(String[] args) {
        final List<Set<Paramater>> parameters = ...instantiated from args
        parameters.forEach(set -> {
            System.out.println(
                new FormattedResult(
                    new Calculation(
                        set.get(0),
                        set.get(1)
                    ).calculate()
                ).print()
            );
        });
    }
}

Faktanya, Pemrograman Berorientasi Objek diciptakan khusus sebagai solusi untuk masalah kompleksitas / fleksibilitas paradigma Imperatif karena tidak ada jawaban yang baik (bahwa setiap orang dapat menyetujui atau tiba secara mandiri, bagaimanapun) untuk bagaimana mengoptimalkan secara optimal tentukan fungsi dan prosedur Imperatif dalam idiom.


Ini adalah jawaban yang sangat terperinci dan dipikirkan dengan matang, tapi sayangnya saya pikir itu meleset dari sasaran pada apa yang sebenarnya diminta OP. Dia tidak meminta pelajaran tentang praktik-praktik OOP yang baik untuk menyelesaikan contohnya yang penuh tipu daya, dia bertanya tentang kriteria di mana kita memutuskan investasi waktu dalam solusi vs generalisasi.
maple_shaft

@maple_shaft Mungkin saya melewatkan sasaran, tapi saya pikir Anda juga. OP tidak bertanya tentang investasi waktu vs. generalisasi. Dia bertanya, "Bagaimana saya tahu bagaimana metode saya bisa digunakan kembali?" Dia kemudian bertanya dalam tubuh pertanyaannya, "Jika destroyCity (City city) lebih baik daripada menghancurkanBaghdad (), apakah takeActionOnCity (Action action, City city) bahkan lebih baik? Mengapa / mengapa tidak?" Saya membuat alasan untuk pendekatan alternatif untuk solusi teknik yang saya percaya memecahkan masalah mencari tahu bagaimana generik untuk membuat metode dan memberikan contoh untuk mendukung klaim saya. Maaf kamu tidak suka itu.
Stuporman

@maple_shaft Sejujurnya, hanya OP yang dapat menentukan apakah jawaban saya relevan dengan pertanyaannya karena kita semua bisa berperang mempertahankan interpretasi kita tentang niatnya, yang semuanya bisa saja salah sama.
Stuporman

@maple_shaft Saya menambahkan intro untuk mencoba mengklarifikasi bagaimana hubungannya dengan pertanyaan dan memberikan gambaran yang jelas antara jawaban dan contoh implementasi. Apakah itu lebih baik?
Stuporman

1
Jujur, jika Anda menerapkan semua prinsip ini, jawabannya akan datang secara alami, halus, dan dapat dibaca secara besar-besaran. Plus, dapat diubah tanpa banyak keributan. Saya tidak tahu siapa Anda, tetapi saya berharap ada lebih banyak dari Anda! Saya terus mengeluarkan kode Reaktif untuk OO yang layak, dan itu SELALU setengah dari ukuran, lebih mudah dibaca, lebih dapat dikontrol, dan masih memiliki threading / pemisahan / pemetaan. Saya pikir React adalah untuk orang yang tidak memahami konsep "dasar" yang baru saja Anda daftarkan.
Stephen J

3

Pengalaman , Pengetahuan Domain , dan Ulasan Kode.

Dan, terlepas dari seberapa banyak atau sedikit pengalaman , pengetahuan domain , atau tim yang Anda miliki, Anda tidak dapat menghindari kebutuhan untuk refactor sesuai kebutuhan.


Dengan Pengalaman , Anda akan mulai mengenali pola dalam metode spesifik domain (dan kelas) yang Anda tulis. Dan, jika Anda benar-benar tertarik pada kode KERING, Anda akan merasakan perasaan buruk ketika Anda tentang menulis metode yang secara naluriah Anda tahu Anda akan menulis variasi di masa depan. Jadi, Anda secara intuitif akan menulis denominator paling umum yang parametrize sebagai gantinya.

(Pengalaman ini dapat mentransfer secara naluriah ke beberapa objek dan metode domain Anda juga.)

Dengan Pengetahuan Domain , Anda akan merasakan konsep bisnis yang terkait erat, konsep mana yang memiliki variabel, yang cukup statis, dll.

Dengan Ulasan Kode, parametriasi di bawah dan di atas akan lebih mungkin ditangkap sebelum menjadi kode produksi, karena rekan-rekan Anda (semoga) memiliki pengalaman dan perspektif yang unik, baik pada domain dan pengkodean secara umum.


Yang mengatakan, pengembang baru umumnya tidak akan memiliki Sense Spidey ini atau sekelompok teman sebaya berpengalaman untuk bersandar langsung. Dan, bahkan pengembang yang berpengalaman pun mendapat manfaat dari disiplin dasar untuk membimbing mereka melalui persyaratan baru - atau melalui hari yang berkabut di otak. Jadi, inilah yang saya sarankan sebagai permulaan :

  • Mulailah dengan implementasi naif, dengan parametriisasi minimal.
    (Sertakan parameter apa pun yang sudah Anda tahu Anda perlukan, jelas ...)
  • Hapus angka ajaib dan string, pindahkan ke konfigurasi dan / atau parameter
  • Faktor "besar" metode menjadi lebih kecil, metode yang dinamai dengan baik
  • Refactor metode yang sangat berlebihan (jika nyaman) menjadi penyebut umum, parametrizing perbedaan.

Langkah-langkah ini tidak harus terjadi dalam urutan yang ditentukan. Jika Anda duduk untuk menulis metode yang sudah Anda ketahui sangat berlebihan dengan metode yang sudah ada , lompat langsung ke refactoring jika itu nyaman. (Jika tidak akan membutuhkan waktu lebih lama untuk melakukan refactor daripada hanya menulis, menguji, dan memelihara dua metode.)

Tapi, selain hanya memiliki banyak pengalaman dan sebagainya, saya menyarankan kode yang cukup minimalis KERING. Tidak sulit untuk memperbaiki pelanggaran yang jelas. Dan, jika Anda terlalu bersemangat , Anda bisa berakhir dengan kode "terlalu KERING" yang bahkan lebih sulit untuk dibaca, dipahami, dan dipelihara daripada yang setara dengan "BASAH".


2
Jadi tidak ada jawaban yang tepat If destoryCity(City city) is better than destoryBaghdad(), is takeActionOnCity(Action action, City city) even better?? Itu adalah pertanyaan ya / tidak, tetapi tidak memiliki jawaban, bukan? Atau anggapan awal yang salah, destroyCity(City)mungkin tidak perlu lebih baik dan itu benar-benar tergantung ? Jadi bukan berarti saya bukan insinyur perangkat lunak yang tidak terlatih secara etis karena saya langsung mengimplementasikannya tanpa parameter? Maksud saya apa jawaban untuk pertanyaan konkret yang saya tanyakan?
Koray Tugay

Jenis pertanyaan Anda menanyakan beberapa pertanyaan. Jawaban atas pertanyaan judul adalah, "pengalaman, pengetahuan domain, ulasan kode ... dan ... jangan takut untuk melakukan refactor." Jawaban atas segala konkret "apakah ini parameter yang tepat untuk metode ABC" pertanyaannya adalah ... "Saya tidak tahu. Mengapa Anda bertanya? Apakah ada yang salah dengan jumlah parameter yang dimilikinya saat ini ??? Jika ya, jelaskan itu. Perbaiki. " ... Saya mungkin merujuk Anda ke "POAP" untuk panduan lebih lanjut: Anda perlu memahami mengapa Anda melakukan apa yang Anda lakukan!
svidgen

Maksudku ... mari kita mundur selangkah dari destroyBaghdad()metode. Apa konteksnya? Apakah ini video game di mana akhir pertandingan mengakibatkan Baghdad dihancurkan ??? Jika demikian ... destroyBaghdad()mungkin metode nama / tanda tangan yang masuk akal ...
svidgen

1
Jadi Anda tidak setuju dengan kutipan yang dikutip dalam pertanyaan saya, kan? It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure.Jika Anda berada di ruangan bersama Nathaniel Borenstein, Anda akan membantahnya bahwa itu benar-benar tergantung dan pernyataannya tidak benar? Maksud saya indah bahwa banyak orang yang menjawab, menghabiskan waktu dan energi mereka, tetapi saya tidak melihat satu jawaban konkret di mana pun. Spidey-indra, ulasan kode .. Tapi apa jawabannya is takeActionOnCity(Action action, City city) better? null?
Koray Tugay

1
@viden Tentu saja, cara lain untuk abstrak ini tanpa usaha ekstra adalah membalikkan ketergantungan - minta fungsi mengembalikan daftar kota, alih-alih melakukan tindakan apa pun pada mereka (seperti "println" dalam kode asli). Ini dapat diabstraksi lebih lanjut jika perlu, tetapi hanya satu perubahan ini saja yang menangani sekitar setengah dari persyaratan tambahan dalam pertanyaan awal - dan alih-alih satu fungsi tidak murni yang dapat melakukan segala macam hal buruk, Anda hanya memiliki fungsi yang mengembalikan daftar, dan penelepon melakukan hal-hal buruk.
Luaan

2

Jawaban yang sama dengan kualitas, kegunaan, hutang teknis dll:

Dapat digunakan kembali seperti Anda, pengguna, 1 membutuhkannya

Ini pada dasarnya panggilan penilaian - apakah biaya merancang dan memelihara abstraksi akan dibayar kembali dengan biaya (= waktu dan usaha) itu akan menghemat Anda.

  • Perhatikan frasa "down the line": ada mekanik hasil di sini, jadi itu akan tergantung pada seberapa banyak Anda akan bekerja dengan kode ini lebih lanjut. Misalnya:
    • Apakah ini proyek satu kali, atau apakah akan semakin meningkat dalam waktu yang lama?
    • Apakah Anda yakin dengan desain Anda, atau apakah Anda kemungkinan harus membuang atau mengubahnya secara drastis untuk proyek / tonggak berikutnya (misalnya coba kerangka kerja lain)?
  • Manfaat yang diproyeksikan juga tergantung pada kemampuan Anda untuk memprediksi masa depan (perubahan pada aplikasi). Terkadang, Anda dapat melihat secara wajar tempat yang akan diambil aplikasi Anda. Lebih sering, Anda pikir Anda bisa tetapi sebenarnya tidak bisa. Aturan praktis di sini adalah prinsip YAGNI dan aturan tiga - keduanya menekankan bekerja dari apa yang Anda tahu, sekarang.

1 Ini adalah konstruksi kode, jadi Anda adalah "pengguna" dalam hal ini - pengguna kode sumber


1

Ada proses yang jelas yang dapat Anda ikuti:

  • Tulis tes yang gagal untuk fitur tunggal yang dengan sendirinya merupakan "hal" (yaitu, tidak ada pemisahan sewenang-wenang dari fitur di mana tidak ada yang benar-benar masuk akal).
  • Tulis kode minimum absolut untuk membuatnya lulus hijau, bukan baris lagi.
  • Bilas dan ulangi.
  • (Refactor tanpa henti jika perlu, yang seharusnya mudah karena cakupan tes yang hebat.)

Ini muncul dengan - setidaknya menurut pendapat beberapa orang - kode yang cukup optimal, karena sekecil mungkin, setiap fitur selesai membutuhkan waktu sesedikit mungkin (yang mungkin atau mungkin tidak benar jika Anda melihat selesai) produk setelah refactoring), dan memiliki cakupan pengujian yang sangat baik. Ini juga secara nyata menghindari metode atau kelas yang terlalu umum-direkayasa.

Ini juga memberi Anda instruksi yang sangat jelas kapan membuat sesuatu menjadi generik dan kapan harus mengkhususkan diri.

Saya menemukan contoh kota Anda aneh; Saya kemungkinan besar tidak akan pernah meng-hardcode nama kota. Sangat jelas bahwa kota-kota tambahan akan dimasukkan nanti, apa pun yang Anda lakukan. Tapi contoh lain adalah warna. Dalam beberapa keadaan, hardcoding "merah" atau "hijau" akan menjadi suatu kemungkinan. Misalnya, lampu lalu lintas adalah warna di mana-mana sehingga Anda bisa lolos begitu saja (dan Anda selalu bisa refactor). Perbedaannya adalah bahwa "merah" dan "hijau" memiliki makna universal, "hardcoded" di dunia kita, sangat tidak mungkin hal itu akan pernah berubah, dan tidak ada alternatif lain juga.

Metode penghematan siang hari pertama Anda benar-benar rusak. Sementara itu sesuai dengan spesifikasi, hardcoded 2018 sangat buruk karena a) tidak disebutkan dalam "kontrak" teknis (dalam nama metode, dalam kasus ini), dan b) itu akan keluar dari tanggal segera, jadi kerusakan sudah termasuk sejak awal. Untuk hal-hal yang berkaitan dengan waktu / tanggal, sangat jarang masuk akal untuk meng-hardcode nilai tertentu karena, well, waktu terus berjalan. Namun terlepas dari itu, segala sesuatu yang lain untuk diskusi. Jika Anda memberikan tahun yang sederhana dan kemudian selalu menghitung tahun yang lengkap, silakan. Sebagian besar hal-hal yang Anda daftarkan (pemformatan, pilihan rentang yang lebih kecil, dll.) Berteriak bahwa metode Anda melakukan terlalu banyak, dan itu seharusnya mungkin mengembalikan daftar / array nilai sehingga pemanggil dapat melakukan pemformatan / pemfilteran sendiri.

Tetapi pada akhirnya, sebagian besar dari ini adalah opini, rasa, pengalaman dan bias pribadi, jadi jangan terlalu khawatir tentang hal itu.


Mengenai paragraf kedua hingga terakhir Anda - lihat "persyaratan" yang awalnya diberikan, yaitu yang menjadi dasar metode pertama. Ini menentukan 2018, sehingga kode secara teknis benar (dan mungkin akan cocok dengan pendekatan Anda yang digerakkan oleh fitur).
dwizum

@dwizum, sudah benar mengenai persyaratan, tapi ada nama metode yang menyesatkan. Pada 2019, setiap programmer yang hanya melihat nama metode akan menganggap bahwa ia melakukan apa saja (mungkin mengembalikan nilai untuk tahun ini), bukan 2018 ... Saya akan menambahkan kalimat pada jawaban untuk memperjelas apa yang saya maksud.
AnoE

1

Saya berpendapat bahwa ada dua jenis kode yang dapat digunakan kembali:

  • Kode yang dapat digunakan kembali karena itu hal yang mendasar dan mendasar.
  • Kode yang dapat digunakan kembali karena memiliki parameter, penggantian dan pengait untuk di mana saja.

Jenis usabilitas pertama seringkali merupakan ide yang bagus. Ini berlaku untuk hal-hal seperti daftar, hashmaps, key / value store, pencocokan string (mis. Regex, glob, ...), tuple, unifikasi, pohon pencarian (kedalaman-pertama, luas-pertama, iterasi-mendalam, ...) , pengurai parser, cache / memoiser, pembaca / penulis format data (s-ekspresi, XML, JSON, protobuf, ...), antrian tugas, dll.

Hal-hal ini sangat umum, dengan cara yang sangat abstrak, sehingga digunakan kembali di semua tempat dalam pemrograman sehari-hari. Jika Anda menemukan diri Anda menulis kode tujuan khusus yang akan lebih sederhana jika dibuat lebih abstrak / umum (misalnya jika kita memiliki "daftar pesanan pelanggan", kita dapat membuang barang-barang "pesanan pelanggan" untuk mendapatkan "daftar" ) maka mungkin ide yang bagus untuk menariknya keluar. Bahkan jika itu tidak digunakan kembali, itu memungkinkan kita memisahkan fungsi yang tidak terkait.

Jenis kedua adalah di mana kita memiliki beberapa kode konkret, yang memecahkan masalah nyata, tetapi melakukannya dengan membuat sejumlah besar keputusan. Kita dapat membuatnya lebih umum / dapat digunakan kembali dengan "soft-coding" keputusan-keputusan itu, misalnya mengubahnya menjadi parameter, menyulitkan implementasi dan membuat detail yang lebih konkret (yaitu pengetahuan tentang kait mana yang mungkin kita inginkan untuk ditimpa). Contoh Anda tampaknya seperti ini. Masalah dengan penggunaan kembali semacam ini adalah bahwa kita mungkin pada akhirnya mencoba untuk menebak kasus penggunaan orang lain, atau diri kita di masa depan. Akhirnya kita mungkin memiliki banyak parameter sehingga kode kita tidak dapat digunakan, apalagi bisa digunakan kembali! Dengan kata lain, saat menelepon dibutuhkan lebih banyak upaya daripada hanya menulis versi kita sendiri. Di sinilah YAGNI (Kamu Tidak Akan Membutuhkannya) penting. Sering kali, upaya seperti itu pada kode "dapat digunakan kembali" berakhir tidak digunakan kembali, karena mungkin tidak dapat dibandingkan dengan kasus-kasus penggunaan lebih mendasar daripada parameter dapat menjelaskan, atau para pengguna potensial lebih suka menggulung sendiri (sih, lihat semua standar dan perpustakaan di luar sana yang penulisnya diawali dengan kata "Sederhana", untuk membedakan mereka dari para pendahulu!).

Bentuk kedua "reusability" ini pada dasarnya harus dilakukan atas dasar yang diperlukan. Tentu, Anda dapat menempel beberapa parameter "jelas" di sana, tetapi jangan mulai mencoba memprediksi masa depan. YAGNI.


Bisakah kami mengatakan bahwa Anda setuju bahwa pengambilan pertama saya baik-baik saja, di mana bahkan tahun itu dikodekan? Atau jika Anda awalnya menerapkan persyaratan itu, apakah Anda akan menjadikan tahun ini sebagai parameter dalam pengambilan pertama Anda?
Koray Tugay

Pengambilan pertama Anda baik-baik saja, karena persyaratannya adalah skrip satu kali untuk 'memeriksa sesuatu'. Itu gagal dalam tes 'etis', tetapi dia gagal dalam tes 'tanpa dogma'. "Dia mungkin mengatakan ..." menciptakan persyaratan yang Kau Tidak Perlu.
Warbo

Kami tidak dapat mengatakan 'penghancuran kota' mana yang "lebih baik" tanpa info lebih lanjut: destroyBaghdadadalah skrip satu kali (atau setidaknya, ini idempoten). Mungkin menghancurkan kota mana pun akan menjadi peningkatan, tetapi bagaimana jika destroyBaghdadbekerja dengan membanjiri Tigris? Itu mungkin dapat digunakan kembali untuk Mosul dan Basra, tetapi tidak untuk Mekah atau Atlanta.
Warbo

Saya melihat Anda tidak setuju dengan Nathaniel Borenstein, pemilik kutipan. Saya mencoba memahami dengan lambat, saya pikir dengan membaca semua tanggapan ini dan diskusi.
Koray Tugay

1
Saya suka diferensiasi ini. Tidak selalu jelas, dan selalu ada "kasus perbatasan". Tetapi secara umum, saya juga penggemar "blok bangunan" (sering dalam bentuk staticmetode) yang murni fungsional dan tingkat rendah, dan berbeda dengan itu, memutuskan tentang "mengonfigurasi parameter dan kait" biasanya di mana Anda harus membangun beberapa struktur yang meminta dibenarkan.
Marco13

1

Sudah ada banyak jawaban yang bagus dan rumit. Beberapa dari mereka masuk ke dalam rincian spesifik, menjabarkan sudut pandang tertentu tentang metodologi pengembangan perangkat lunak secara umum, dan beberapa dari mereka tentu saja memiliki unsur kontroversial atau "pendapat".

The jawaban dengan Warbo sudah menunjukkan berbagai jenis usabilitas. Yaitu, apakah sesuatu dapat digunakan kembali karena itu adalah blok bangunan fundamental, atau apakah sesuatu dapat digunakan kembali karena itu "generik" dalam beberapa cara. Mengacu pada yang terakhir, ada sesuatu yang saya anggap sebagai semacam ukuran untuk digunakan kembali:

Apakah satu metode dapat meniru yang lain.

Mengenai contoh dari pertanyaan: Bayangkan metode itu

void dayLightSavings()

adalah implementasi dari fungsi yang diminta oleh pelanggan. Jadi itu akan menjadi sesuatu yang seharusnya digunakan oleh programmer lain , dan dengan demikian, menjadi metode publik , seperti pada

publicvoid dayLightSavings()

Ini bisa diterapkan seperti yang Anda tunjukkan dalam jawaban Anda. Sekarang, seseorang ingin mengukurnya dengan tahun. Jadi Anda bisa menambahkan metode

publicvoid dayLightSavings(int year)

dan mengubah implementasi asli menjadi adil

public void dayLightSavings() {
    dayLightSavings(2018);
}

"Permintaan fitur" selanjutnya dan generalisasi mengikuti pola yang sama. Jadi jika dan hanya jika ada permintaan untuk bentuk yang paling umum, Anda dapat mengimplementasikannya, mengetahui bahwa bentuk yang paling umum ini memungkinkan untuk implementasi sepele dari yang lebih spesifik:

public void dayLightSavings() {
    dayLightSavings(2018, 0, 12, 0, 12, new DateTimeFormatter(...));
}

Jika Anda telah mengantisipasi ekstensi mendatang dan permintaan fitur, dan memiliki waktu yang Anda inginkan dan ingin menghabiskan akhir pekan yang membosankan dengan generalisasi (berpotensi tidak berguna), Anda bisa mulai dengan yang paling umum sejak dari awal. Tetapi hanya sebagai metode pribadi . Selama Anda hanya mengekspos metode sederhana yang diminta oleh pelanggan sebagai metode publik , Anda aman.

tl; dr :

Pertanyaannya sebenarnya bukan "seberapa bisa digunakannya suatu metode". Pertanyaannya adalah seberapa besar reusability ini diekspos, dan seperti apa API itu. Membuat API andal yang tahan uji waktu (bahkan ketika persyaratan lebih lanjut muncul kemudian) adalah seni dan kerajinan, dan topiknya terlalu rumit untuk dibahas di sini. Lihatlah presentasi ini oleh Joshua Bloch atau wiki buku desain API untuk memulai.


dayLightSavings()menelepon dayLightSavings(2018)sepertinya bukan ide yang bagus untukku.
Koray Tugay

@ KorayTugay Ketika permintaan awal adalah bahwa ia harus mencetak "penghematan siang hari untuk 2018", maka itu baik-baik saja. Sebenarnya, ini adalah metode yang Anda terapkan pada awalnya. Jika harus mencetak "penghematan siang hari untuk tahun berjalan , maka tentu saja Anda akan menelepon dayLightSavings(computeCurrentYear());...
Marco13

0

Aturan praktis yang baik adalah: metode Anda harus dapat digunakan kembali seperti ... dapat digunakan kembali.

Jika Anda berharap bahwa Anda akan memanggil metode Anda hanya di satu tempat, seharusnya hanya memiliki parameter yang diketahui ke situs panggilan dan yang tidak tersedia untuk metode ini.

Jika Anda memiliki lebih banyak penelepon, Anda dapat memperkenalkan parameter baru selama penelepon lain dapat melewati parameter tersebut; jika tidak, Anda perlu metode baru.

Karena jumlah penelepon dapat bertambah seiring waktu, Anda harus bersiap untuk melakukan refactoring atau overloading. Dalam banyak kasus itu berarti Anda harus merasa aman untuk memilih ekspresi dan menjalankan tindakan "ekstrak parameter" dari IDE Anda.


0

Jawaban ultra singkat: Semakin sedikit sambungan atau ketergantungan pada kode lain, modul generik Anda akan semakin dapat digunakan kembali.

Contoh Anda hanya bergantung pada

import java.time.*;
import java.util.Set;

jadi secara teori itu bisa sangat dapat digunakan kembali.

Dalam praktiknya saya tidak berpikir bahwa Anda akan memiliki usecase kedua yang memerlukan kode ini sehingga mengikuti prinsip yagni saya tidak akan membuatnya dapat digunakan kembali jika tidak ada lebih dari 3 projets berbeda yang memerlukan kode ini.

Aspek lain yang dapat digunakan kembali adalah kemudahan penggunaan dan dokumentasi yang berhubungan dengan Test Driven Development : Sangat membantu jika Anda memiliki unit-test sederhana yang menunjukkan / mendokumentasikan penggunaan mudah modul generik Anda sebagai contoh pengkodean untuk pengguna lib Anda.


0

Ini adalah kesempatan yang baik untuk menyatakan aturan yang saya buat baru-baru ini:

Menjadi programmer yang baik berarti bisa memprediksi masa depan.

Tentu saja, ini sangat mustahil! Lagi pula, Anda tidak pernah tahu pasti generalisasi apa yang menurut Anda berguna nantinya, tugas terkait apa yang ingin Anda lakukan, fitur baru apa yang diinginkan pengguna Anda, & c. Tetapi pengalaman terkadang memberi Anda gambaran kasar tentang apa yang mungkin berguna.

Faktor-faktor lain yang harus Anda imbangi adalah berapa banyak waktu dan usaha ekstra yang akan terlibat, dan seberapa rumit hal itu akan membuat kode Anda. Terkadang Anda beruntung, dan menyelesaikan masalah yang lebih umum sebenarnya lebih sederhana! (Setidaknya secara konseptual, jika tidak dalam jumlah kode.) Tetapi lebih sering, ada biaya kerumitan serta salah satu waktu dan usaha.

Jadi, jika Anda berpikir generalisasi sangat mungkin diperlukan, sering kali layak dilakukan (kecuali jika itu menambah banyak pekerjaan atau kompleksitas); tetapi jika tampaknya jauh lebih kecil kemungkinannya, maka itu mungkin tidak (kecuali sangat mudah dan / atau menyederhanakan kode).

(Sebagai contoh baru-baru ini: minggu lalu saya diberikan spek. Untuk tindakan, sistem seharusnya memakan waktu tepat 2 hari setelah sesuatu kedaluwarsa. Jadi tentu saja saya menjadikan parameter 2 hari sebagai parameter. Minggu ini para pelaku bisnis senang, karena mereka akan meminta peningkatan itu! Saya beruntung: itu adalah perubahan yang mudah, dan saya kira itu sangat mungkin diinginkan. Seringkali lebih sulit untuk dinilai. Tetapi masih layak untuk diprediksi, dan pengalaman sering merupakan panduan yang baik. .)


0

Pertama, jawaban terbaik untuk 'Bagaimana saya tahu bagaimana reusable metode saya seharusnya? "Adalah" pengalaman. "Lakukan ini beberapa ribu kali, dan Anda biasanya mendapatkan jawaban yang tepat. Tetapi sebagai penggoda, saya dapat memberikan yang terakhir baris jawaban ini: Pelanggan Anda akan memberi tahu Anda berapa banyak fleksibilitas dan berapa banyak lapisan generalisasi yang harus Anda cari.

Banyak dari jawaban ini memiliki saran khusus. Saya ingin memberikan sesuatu yang lebih umum ... karena ironi itu terlalu menyenangkan untuk dilewatkan!

Seperti yang dicatat oleh beberapa jawaban, generalitas itu mahal. Namun, sebenarnya tidak. Tidak selalu. Memahami biaya sangat penting untuk memainkan permainan reusability.

Saya fokus pada menempatkan berbagai hal dalam skala dari "tidak dapat diubah" menjadi "dapat dibalik." Skala ini halus. Satu-satunya hal yang benar-benar tidak dapat diubah adalah "waktu yang dihabiskan untuk proyek." Anda tidak akan pernah mendapatkan sumber daya itu kembali. Situasi yang sedikit kurang dapat dibalik mungkin merupakan situasi "borgol emas" seperti Windows API. Fitur yang sudah tidak digunakan lagi tetap ada di API itu selama beberapa dekade karena model bisnis Microsoft menghendaki hal itu. Jika Anda memiliki pelanggan yang hubungannya akan rusak secara permanen dengan membatalkan beberapa fitur API, maka itu harus diperlakukan sebagai tidak dapat dipulihkan. Melihat ujung skala yang lain, Anda memiliki hal-hal seperti kode prototipe. Jika Anda tidak suka ke mana perginya, Anda bisa membuangnya. Sedikit kurang dapat dibalikkan mungkin menggunakan API internal. Mereka dapat di refactored tanpa mengganggu pelanggan,waktu (sumber daya yang paling tidak dapat dipulihkan dari semua!)

Jadi, letakkan ini dalam skala. Sekarang Anda dapat menerapkan heuristik: semakin banyak sesuatu yang dapat dibalik, semakin banyak Anda dapat menggunakannya untuk kegiatan yang tampak di masa depan. Jika sesuatu tidak dapat dipulihkan, gunakan saja untuk tugas-tugas yang digerakkan oleh pelanggan. Inilah sebabnya mengapa Anda melihat prinsip-prinsip seperti itu dari pemrograman ekstrem yang menyarankan hanya melakukan apa yang diminta pelanggan dan tidak lebih. Prinsip-prinsip ini bagus untuk memastikan Anda tidak melakukan sesuatu yang Anda sesali.

Hal-hal seperti prinsip KERING menyarankan cara untuk memindahkan keseimbangan itu. Jika Anda mendapati diri Anda mengulangi sendiri, itu adalah kesempatan untuk membuat apa yang pada dasarnya API internal. Tidak ada pelanggan yang melihatnya, jadi Anda selalu bisa mengubahnya. Setelah Anda memiliki API internal ini, sekarang Anda dapat mulai bermain dengan hal-hal yang tampak ke depan. Menurut Anda, berapa banyak tugas berdasarkan zona waktu yang berbeda yang akan diberikan istri Anda? Apakah Anda memiliki pelanggan lain yang mungkin menginginkan tugas berbasis zona waktu? Fleksibilitas Anda di sini dibeli oleh permintaan konkret dari pelanggan Anda saat ini, dan mendukung permintaan potensial pelanggan di masa depan.

Pendekatan berpikir berlapis ini, yang datang secara alami dari KERING secara alami memberikan generalisasi yang Anda inginkan tanpa limbah. Tetapi apakah ada batasannya? Tentu saja ada. Tetapi untuk melihatnya, Anda harus melihat hutan untuk pepohonan.

Jika Anda memiliki banyak lapisan fleksibilitas, sering menyebabkan kurangnya kontrol langsung terhadap lapisan yang dihadapi pelanggan Anda. Saya memiliki perangkat lunak di mana saya memiliki tugas yang brutal untuk menjelaskan kepada pelanggan mengapa mereka tidak dapat memiliki apa yang mereka inginkan karena fleksibilitas dibangun dalam 10 lapisan ke bawah yang tidak pernah mereka lihat. Kami menulis sendiri ke sudut. Kami mengikat diri kami dengan semua fleksibilitas yang kami pikir kami butuhkan.

Jadi, ketika Anda melakukan trik generalisasi / KERING ini, selalu perhatikan pelanggan Anda . Menurut Anda apa yang akan diminta istri Anda selanjutnya? Apakah Anda menempatkan diri Anda pada posisi untuk memenuhi kebutuhan itu? Jika Anda memiliki bakat, pelanggan akan secara efektif memberi tahu Anda kebutuhan mereka di masa depan. Jika Anda tidak memiliki bakat, well, kebanyakan dari kita hanya mengandalkan dugaan! (terutama dengan pasangan!) Beberapa pelanggan akan menginginkan fleksibilitas yang besar, dan bersedia menerima biaya tambahan dari pengembangan Anda dengan semua lapisan ini karena mereka secara langsung mendapat manfaat dari fleksibilitas lapisan-lapisan itu. Pelanggan lain lebih suka memperbaiki persyaratan yang tidak tergoyahkan, dan mereka lebih suka pengembangan yang lebih langsung. Pelanggan Anda akan memberi tahu Anda berapa banyak fleksibilitas dan berapa banyak lapisan generalisasi yang harus Anda cari.


Yah harus ada orang lain yang melakukan ini sebanyak 10.000 kali, jadi mengapa saya harus melakukannya 10.000 kali dan mendapatkan pengalaman ketika saya bisa belajar dari orang lain? Karena jawabannya akan berbeda untuk setiap individu sehingga jawaban dari yang berpengalaman tidak berlaku untuk saya? Juga Your customer will tell you how much flexibility and how many layers of generalization you should seek.dunia apa ini?
Koray Tugay

@ KorayTugay Ini adalah dunia bisnis. Jika pelanggan Anda tidak memberi tahu Anda apa yang harus dilakukan, maka Anda tidak cukup mendengarkan. Tentu saja, mereka tidak akan selalu memberi tahu Anda dengan kata-kata, tetapi mereka memberi tahu Anda dengan cara lain. Pengalaman membantu Anda mendengarkan pesan mereka yang lebih halus. Jika Anda belum memiliki keterampilan itu, temukan seseorang di perusahaan Anda yang memang memiliki keterampilan mendengarkan petunjuk pelanggan yang halus itu, dan berikan arahan untuk arahan. Seseorang akan memiliki keterampilan itu, bahkan jika mereka CEO atau dalam pemasaran.
Cort Ammon

Dalam kasus spesifik Anda, jika Anda gagal membuang sampah karena Anda terlalu sibuk membuat versi umum dari masalah zona waktu ini alih-alih meretas bersama solusi spesifik, bagaimana perasaan pelanggan Anda?
Cort Ammon

Jadi Anda setuju bahwa pendekatan pertama saya adalah yang benar, hardcoding 2018 pada pengambilan pertama alih-alih parameterisasi tahun? (btw, Itu tidak benar-benar mendengarkan pelanggan saya, saya pikir, contoh sampah. Itu adalah mengetahui pelanggan Anda .. Bahkan jika mendapatkan dukungan dari The Oracle, tidak ada pesan halus untuk didengarkan ketika dia mengatakan saya perlu daftar cahaya siang hari) perubahan untuk 2018.) Terima kasih atas waktu dan jawaban Anda.
Koray Tugay

@ KorayTugay Tanpa mengetahui detail tambahan, saya akan mengatakan bahwa hardcoding adalah pendekatan yang tepat. Anda tidak memiliki cara untuk mengetahui apakah Anda akan memerlukan kode DLS di masa depan, atau tidak tahu permintaan macam apa yang mungkin ia berikan selanjutnya. Dan jika pelanggan Anda mencoba menguji Anda, mereka mendapatkan apa yang mereka dapatkan = D
Cort Ammon

0

tidak ada insinyur perangkat lunak yang terlatih secara etis yang akan menyetujui untuk menulis prosedur DestroyBaghdad. Etika profesional dasar malah mengharuskannya untuk menulis prosedur DestroyCity, yang dapat diberikan Baghdad sebagai parameter.

Ini adalah sesuatu yang dikenal di kalangan rekayasa perangkat lunak canggih sebagai "lelucon". Lelucon tidak harus menjadi apa yang kita sebut "benar", walaupun untuk menjadi lucu mereka umumnya harus mengisyaratkan sesuatu yang benar.

Dalam kasus khusus ini, "lelucon" tidak "benar". Pekerjaan yang terlibat dalam penulisan prosedur umum untuk menghancurkan kota mana pun adalah, kita dapat dengan aman berasumsi, urutan besarnya yang diperlukan untuk menghancurkan satu kota tertentukota. Kalau tidak, siapa pun yang telah menghancurkan satu atau beberapa kota (Yosua dalam Alkitab, akan kita katakan, atau Presiden Truman) dapat dengan sepele menggeneralisasi apa yang mereka lakukan dan dapat menghancurkan secara mutlak kota mana pun sesuka hati. Sebenarnya ini bukan masalahnya. Metode yang dua orang terkenal digunakan untuk menghancurkan sejumlah kecil kota tertentu tidak harus bekerja di sembarang kota kapan saja. Kota lain yang temboknya memiliki frekuensi resonansi berbeda atau yang pertahanan udara di ketinggiannya lebih baik, akan membutuhkan perubahan pendekatan kecil atau mendasar (terompet atau roket dengan nada berbeda).

Ini juga mengarah pada pemeliharaan kode terhadap perubahan dari waktu ke waktu: ada banyak kota sekarang yang akan jatuh ke kedua pendekatan tersebut, berkat metode konstruksi modern dan radar di mana-mana.

Mengembangkan dan menguji cara yang sepenuhnya umum yang akan menghancurkan kota mana pun , sebelum Anda setuju untuk menghancurkan hanya satu kota, adalah pendekatan yang sangat tidak efisien. Tidak ada insinyur perangkat lunak yang terlatih secara etis akan mencoba untuk menggeneralisasi masalah sampai taraf yang membutuhkan pesanan lebih besar daripada yang harus dibayar oleh majikan / klien mereka, tanpa persyaratan yang diperlihatkan.

Jadi apa yang benar? Terkadang menambahkan generalitas itu sepele. Haruskah kita selalu menambahkan generalitas ketika itu sepele untuk melakukannya? Saya masih akan berdebat "tidak, tidak selalu", karena masalah pemeliharaan jangka panjang. Misalkan pada saat penulisan, semua kota pada dasarnya sama, jadi saya melanjutkan dengan DestroyCity. Setelah saya menulis ini, bersama dengan tes integrasi yang (karena ruang input yang tak terhitung jumlahnya) iterate atas setiap kota yang dikenal dan pastikan fungsi bekerja pada masing-masing (tidak yakin bagaimana itu bekerja. Mungkin panggilan City.clone () dan menghancurkan klon?

Dalam prakteknya fungsi ini digunakan hanya untuk menghancurkan Baghdad, misalkan seseorang membangun kota baru yang tahan terhadap teknik saya (itu jauh di bawah tanah atau sesuatu). Sekarang saya memiliki kegagalan pengujian integrasi untuk kasus penggunaan yang bahkan tidak benar-benar ada , dan sebelum saya dapat melanjutkan kampanye teror saya terhadap warga sipil Irak yang tidak bersalah, saya harus mencari cara untuk menghancurkan Subterrania. Tidak peduli apakah ini etis atau tidak, itu bodoh dan buang-buang waktu saja .

Jadi, apakah Anda benar-benar menginginkan fungsi yang dapat menghasilkan penghematan siang hari untuk setiap tahun, hanya untuk menghasilkan data untuk 2018? Mungkin, tetapi tentu saja akan membutuhkan sedikit usaha ekstra hanya dengan menyusun kasus uji. Mungkin membutuhkan banyak upaya untuk mendapatkan basis data zona waktu yang lebih baik daripada yang Anda miliki. Jadi misalnya pada tahun 1908, kota Port Arthur, Ontario memiliki periode DST dimulai pada tanggal 1 Juli. Apakah itu di basis data zona waktu OS Anda? Saya kira tidak, jadi fungsi umum Anda salah . Tidak ada yang etis tentang penulisan kode yang membuat janji tidak bisa ditepati.

Baiklah, jadi, dengan peringatan yang sesuai, mudah untuk menulis fungsi yang melakukan zona waktu selama bertahun-tahun, katakan tahun 1970-sekarang. Tapi itu sama mudahnya untuk mengambil fungsi yang sebenarnya Anda tulis, dan menggeneralisasikannya untuk parameterisasi tahun. Jadi tidak benar-benar lebih etis / masuk akal untuk menggeneralisasi sekarang, bahwa itu adalah untuk melakukan apa yang Anda lakukan dan kemudian menggeneralisasi jika dan ketika Anda membutuhkannya.

Namun, jika Anda tahu mengapa istri Anda ingin memeriksa daftar DST ini, maka Anda akan memiliki pendapat tentang apakah dia kemungkinan akan mengajukan pertanyaan yang sama lagi di tahun 2019 dan, jika demikian, apakah Anda dapat keluar dari lingkaran itu dengan memberikan dia fungsi yang bisa dia panggil, tanpa perlu mengkompilasi ulang. Setelah Anda melakukan analisis itu, jawaban untuk pertanyaan "haruskah ini digeneralisasikan ke tahun-tahun terakhir" mungkin "ya". Tetapi Anda menciptakan masalah lain untuk diri Anda sendiri, yaitu bahwa data zona waktu di masa depan hanya bersifat sementara, dan karena itu jika dia menjalankannya untuk 2019 hari ini, Dia mungkin atau mungkin tidak menyadari bahwa itu memberinya tebakan terbaik. Jadi Anda masih harus menulis banyak dokumentasi yang tidak diperlukan untuk fungsi yang kurang umum ("data berasal dari basis data zona waktu bla bla inilah tautan untuk melihat kebijakan mereka dalam mendorong pembaruan bla bla"). Jika Anda menolak kasus khusus, maka sepanjang waktu Anda melakukan itu, dia tidak bisa melanjutkan tugas yang dia butuhkan data 2018, karena omong kosong tentang 2019 dia bahkan belum peduli.

Jangan melakukan hal-hal sulit tanpa memikirkannya dengan baik, hanya karena lelucon yang menyuruh Anda melakukannya. Apakah itu berguna? Apakah cukup murah untuk kegunaan seperti itu?


0

Ini adalah garis yang mudah untuk menggambar karena kegunaan kembali sebagaimana didefinisikan oleh astronot arsitektur adalah bollocks.

Hampir semua kode yang dibuat oleh pengembang aplikasi sangat spesifik untuk domain. Ini bukan tahun 1980. Hampir semua hal yang layak repot sudah dalam kerangka.

Abstraksi dan konvensi membutuhkan dokumentasi dan upaya pembelajaran. Mohon berhenti membuat yang baru hanya demi itu. (Saya melihat Anda , orang JavaScript!)

Mari kita menikmati fantasi yang tidak masuk akal bahwa Anda telah menemukan sesuatu yang benar-benar harus ada dalam kerangka pilihan Anda. Anda tidak bisa hanya menggedor kode seperti yang biasa Anda lakukan. Oh tidak, Anda memerlukan cakupan pengujian tidak hanya untuk penggunaan yang dimaksudkan tetapi juga untuk keberangkatan dari penggunaan yang dimaksudkan, untuk semua kasus tepi yang diketahui, untuk semua mode kegagalan yang dapat dibayangkan, uji kasus untuk diagnostik, data uji, dokumentasi teknis, dokumentasi pengguna, dokumentasi rilis, manajemen rilis, dukungan skrip, tes regresi, manajemen perubahan ...

Apakah majikan Anda senang membayar semua itu? Saya akan mengatakan tidak.

Abstraksi adalah harga yang kami bayar untuk fleksibilitas. Itu membuat kode kita lebih kompleks dan lebih sulit untuk dipahami. Kecuali jika fleksibilitas melayani kebutuhan nyata dan saat ini, hanya saja tidak, karena YAGNI.

Mari kita lihat contoh dunia nyata yang harus saya tangani: HTMLRenderer. Itu tidak scaling dengan benar ketika saya mencoba membuat konteks perangkat printer. Butuh waktu seharian untuk menemukan bahwa secara default menggunakan GDI (yang tidak skala) daripada GDI + (yang tidak, tetapi tidak antialias) karena saya harus mengarungi enam tingkat tipuan dalam dua majelis sebelum saya menemukan kode yang melakukan apa saja .

Dalam hal ini saya akan memaafkan penulis. Abstraksi sebenarnya diperlukan karena ini adalah kode kerangka kerja yang menargetkan lima target render yang sangat berbeda: WinForms, WPF, dotnet Core, Mono dan PdfSharp.

Tapi itu hanya menggarisbawahi poin saya: Anda hampir pasti tidak melakukan sesuatu yang sangat kompleks (rendering HTML dengan stylesheet) menargetkan beberapa platform dengan tujuan kinerja tinggi pada semua platform.

Kode Anda hampir pasti merupakan kisi-kisi basis data lain dengan aturan bisnis yang hanya berlaku untuk perusahaan Anda dan aturan pajak yang hanya berlaku di negara Anda, dalam aplikasi yang tidak untuk dijual.

Semua tipuan itu memecahkan masalah yang tidak Anda miliki dan membuat kode Anda lebih sulit dibaca, yang secara besar-besaran meningkatkan biaya pemeliharaan dan merupakan kerugian besar bagi majikan Anda. Untungnya orang-orang yang seharusnya mengeluh tentang ini tidak dapat memahami apa yang Anda lakukan pada mereka.

Argumen tandingannya adalah bahwa abstraksi semacam ini mendukung pengembangan yang digerakkan oleh pengujian, tetapi saya pikir TDD juga mengecewakan, karena mengandaikan bahwa bisnis memiliki pemahaman yang jelas, lengkap dan benar mengenai persyaratannya. TDD bagus untuk NASA dan mengendalikan perangkat lunak untuk peralatan medis dan mobil self-driving, tetapi terlalu mahal untuk semua orang.


Secara kebetulan, tidak mungkin untuk memprediksi semua penghematan siang hari di dunia. Israel khususnya memiliki sekitar 40 transisi setiap tahun yang melompati semua tempat karena kita tidak bisa membuat orang berdoa pada waktu yang salah dan Tuhan tidak melakukan penghematan siang hari.


Meskipun saya setuju dengan apa yang Anda katakan tentang abstraksi dan upaya belajar, dan terutama ketika itu ditujukan pada kekejian yang disebut "JavaScript", saya sangat tidak setuju dengan kalimat pertama. Penggunaan kembali dapat terjadi pada banyak tingkatan, ia dapat berjalan terlalu jauh ke bawah , dan kode yang dibuang dapat ditulis oleh programmer yang dibuang. Tapi ada yang orang yang cukup idealis untuk setidaknya tujuan kode dapat digunakan kembali. Sayang sekali bagi Anda jika Anda tidak melihat manfaat yang dapat dimiliki ini.
Marco13

@ Marco13 - Karena keberatan Anda masuk akal, saya akan memperluas intinya.
Peter Wone

-3

Jika Anda menggunakan setidaknya java 8, Anda akan menulis kelas WorldTimeZones untuk memberikan apa yang pada dasarnya tampak sebagai kumpulan zona waktu.

Kemudian tambahkan metode filter (Predicate filter) ke kelas WorldTimeZones. Ini memberikan kemampuan bagi penelepon untuk memfilter apa pun yang mereka inginkan dengan melewatkan ekspresi lambda sebagai parameter.

Pada dasarnya, metode filter tunggal mendukung pemfilteran pada apa pun yang terkandung dalam nilai yang diteruskan ke predikat.

Sebagai alternatif, tambahkan metode stream () ke kelas WorldTimeZones Anda yang menghasilkan aliran zona waktu saat dipanggil. Kemudian penelepon dapat memfilter, memetakan dan mengurangi sesuai keinginan tanpa Anda menulis spesialisasi sama sekali.


3
Ini adalah ide generalisasi yang baik namun jawaban ini benar-benar melewatkan tanda pada apa yang ditanyakan dalam pertanyaan. Pertanyaannya bukan tentang bagaimana cara terbaik untuk menggeneralisasi solusi tetapi di mana kita menarik garis pada generalisasi dan bagaimana kita mempertimbangkan pertimbangan ini secara etis.
maple_shaft

Jadi saya katakan bahwa Anda harus menimbang mereka dengan jumlah kasus penggunaan yang didukung dimodifikasi oleh kompleksitas penciptaan. Metode yang mendukung satu kasus penggunaan tidak sama berharganya dengan metode yang mendukung 20 kasus penggunaan. Di sisi lain, jika semua yang Anda tahu adalah satu kasus penggunaan, dan dibutuhkan 5 menit untuk kode untuk itu - pergi untuk itu. Seringkali metode pengkodean yang mendukung penggunaan spesifik memberi tahu Anda cara menggeneralisasi.
Rodney P. Barbati
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.