Lebih baik memiliki 2 metode dengan makna yang jelas, atau hanya 1 metode penggunaan ganda?

30

Untuk menyederhanakan antarmuka, apakah lebih baik tidak memiliki getBalance()metode ini? Melewati 0ke charge(float c);akan memberikan hasil yang sama:

public class Client {
    private float bal;
    float getBalance() { return bal; }
    float charge(float c) {
        bal -= c;
        return bal;
    }
}

Mungkin membuat catatan javadoc? Atau, serahkan saja pada pengguna kelas untuk mencari tahu bagaimana cara mendapatkan keseimbangan?

David
sumber
26
Saya dengan murah hati mengasumsikan ini adalah contoh, kode ilustratif dan bahwa Anda tidak menggunakan matematika floating point untuk menyimpan nilai moneter. Kalau tidak, saya harus berkhotbah. :-)
corsiKa
1
@corsiKa nah. Itu hanya sebuah contoh. Tapi, ya, saya akan menggunakan float untuk mewakili uang di kelas sungguhan ... Terima kasih telah mengingatkan saya tentang bahaya angka floating point.
david
10
Beberapa bahasa membedakan antara mutator dan aksesor dengan efek yang baik. Akan sangat menyebalkan karena tidak bisa mendapatkan keseimbangan instance yang hanya dapat diakses sebagai konstanta!
JDługosz

Jawaban:

90

Anda tampaknya menyarankan bahwa kompleksitas suatu antarmuka diukur oleh jumlah elemen yang dimilikinya (metode, dalam hal ini). Banyak yang berpendapat bahwa harus ingat bahwa chargemetode ini dapat digunakan untuk mengembalikan keseimbangan Clientmenambah kompleksitas daripada memiliki elemen tambahan dari getBalancemetode. Membuat hal-hal lebih eksplisit jauh lebih sederhana, terutama ke titik di mana ia tidak meninggalkan ambiguitas, terlepas dari jumlah elemen yang lebih tinggi di antarmuka.

Selain itu, panggilan charge(0)melanggar prinsip tercengang , juga dikenal sebagai metrik WTF per menit (dari Kode Bersih, gambar di bawah), menyulitkan anggota tim yang baru (atau yang saat ini, setelah beberapa saat menjauh dari kode) hingga mereka mengerti bahwa panggilan itu sebenarnya digunakan untuk mendapatkan keseimbangan. Pikirkan bagaimana reaksi pembaca lain:

masukkan deskripsi gambar di sini

Juga, tanda tangan chargemetode bertentangan dengan pedoman melakukan satu dan hanya satu hal dan pemisahan perintah-permintaan , karena menyebabkan objek untuk mengubah keadaannya sementara juga mengembalikan nilai baru.

Secara keseluruhan, saya percaya bahwa antarmuka paling sederhana dalam kasus ini adalah:

public class Client {
  private float bal;
  float getBalance() { return bal; }
  void charge(float c) { bal -= c; }
}
MichelHenrich
sumber
25
Poin tentang pemisahan perintah-permintaan sangat penting, IMO. Bayangkan Anda adalah pengembang baru di proyek ini. Melihat melalui kode, segera jelas bahwa getBalance()mengembalikan beberapa nilai dan tidak mengubah apa pun. Di sisi lain, charge(0)sepertinya itu memodifikasi sesuatu ... mungkin mengirimkan acara PropertyChanged? Dan apa yang dikembalikan, nilai sebelumnya atau nilai baru? Sekarang Anda harus mencarinya di dokumen dan membuang kekuatan otak, yang bisa dihindari dengan menggunakan metode yang lebih jelas.
Mage Xy
19
Juga, menggunakan charge(0)sebagai cara mendapatkan keseimbangan karena kebetulan tidak berpengaruh sekarang membuat Anda terperinci pada detail implementasi ini; Saya dapat dengan mudah membayangkan persyaratan di masa depan di mana melacak jumlah tagihan menjadi relevan, pada titik mana basis kode Anda dikotori dengan biaya yang bukan biaya sebenarnya menjadi titik yang menyakitkan. Anda harus memberikan elemen antarmuka yang berarti hal-hal yang perlu dilakukan klien Anda, bukan yang hanya melakukan hal-hal yang perlu dilakukan klien Anda.
Ben
12
Peringatan CQS belum tentu sesuai. Untuk charge()metode, jika metode itu adalah atom dalam sistem bersamaan, mungkin tepat untuk mengembalikan nilai baru atau lama.
Dietrich Epp
3
Saya menemukan dua kutipan Anda untuk CQRS sangat tidak meyakinkan. Sebagai contoh, saya umumnya menyukai ide-ide Meyer, tetapi melihat jenis fungsi yang kembali untuk mengetahui apakah ia bermutasi sesuatu yang sewenang-wenang dan tidak. Banyak struktur data yang bersamaan atau tidak dapat diubah membutuhkan mutasi + pengembalian. Bagaimana Anda menambahkan ke String tanpa melanggar CQRS? Apakah Anda memiliki kutipan yang lebih baik?
user949300
6
Hampir tidak ada kode yang sesuai dengan Command Query Separation. Ini tidak idiomatis dalam bahasa berorientasi objek apa pun, dan dilanggar oleh API bawaan dari setiap bahasa yang pernah saya gunakan. Untuk menggambarkannya sebagai "praktik terbaik" tampaknya aneh; dapatkah Anda menyebutkan satu saja proyek perangkat lunak, yang benar-benar mengikuti pola ini?
Mark Amery
28

IMO, mengganti getBalance()dengan charge(0)seluruh aplikasi Anda bukanlah penyederhanaan. Ya itu lebih sedikit baris, tetapi mengaburkan makna charge()metode, yang berpotensi menyebabkan sakit kepala di telepon ketika Anda atau orang lain perlu meninjau kembali kode ini.

Meskipun mereka mungkin memberikan hasil yang sama, mendapatkan saldo akun tidak sama dengan tagihan nol, jadi mungkin lebih baik memisahkan kekhawatiran Anda. Misalnya, jika Anda perlu memodifikasi charge()untuk masuk setiap kali ada transaksi akun, Anda sekarang memiliki masalah dan tetap harus memisahkan fungsionalitasnya.

Matt Dalzell
sumber
13

Penting untuk diingat bahwa kode Anda harus mendokumentasikan diri sendiri. Ketika saya menelepon charge(x), saya berharap xakan dikenakan biaya. Informasi tentang keseimbangan adalah informasi sekunder. Terlebih lagi, saya mungkin tidak tahu bagaimana charge()penerapannya ketika saya menyebutnya dan saya pasti tidak akan tahu bagaimana penerapannya besok. Misalnya, pertimbangkan potensi pembaruan di masa mendatang untuk charge():

float charge(float c) {
    lockDownUserAccountUntilChargeClears();
    bal -= c;
    Unlock();
    return bal;
}

Tiba-tiba menggunakan charge()untuk mendapatkan keseimbangan tidak terlihat begitu bagus.

David berkata Reinstate Monica
sumber
Iya nih! Poin bagus yang chargejauh lebih berat.
Deduplicator
2

Menggunakan charge(0);untuk mendapatkan keseimbangan adalah ide yang buruk: suatu hari, seseorang mungkin menambahkan beberapa kode di sana untuk mencatat biaya yang dibuat tanpa menyadari penggunaan fungsi lainnya, dan kemudian setiap kali seseorang mendapatkan saldo itu akan dicatat sebagai tagihan. (Ada beberapa cara di sekitar ini seperti pernyataan bersyarat yang mengatakan sesuatu seperti:

if (c > 0) {
    // make and log charge
}
return bal;

tetapi ini bergantung pada programmer yang tahu untuk mengimplementasikannya, yang tidak akan dia lakukan jika tidak segera jelas bahwa mereka diperlukan.

Singkatnya: jangan bergantung pada pengguna atau penerus programer Anda menyadari bahwa itu charge(0);adalah cara yang benar untuk mendapatkan keseimbangan, karena kecuali ada dokumentasi yang dijamin tidak akan terlewatkan, maka sejujurnya itu sepertinya cara paling menakutkan untuk mendapatkan keseimbangan mungkin.

Micheal Johnson
sumber
0

Saya tahu ada banyak jawaban, tetapi alasan lain yang menentang charge(0)adalah karena kesalahan ketik yang sederhana charge(9)akan menyebabkan saldo pelanggan Anda berkurang setiap kali Anda ingin mendapatkan saldo mereka. Jika Anda memiliki pengujian unit yang baik, Anda mungkin dapat mengurangi risiko itu, tetapi jika Anda gagal untuk rajin pada setiap panggilan ke chargeAnda bisa memiliki kecelakaan ini.

Shaz
sumber
-2

Saya ingin menyebutkan kasus tertentu di mana ia akan masuk akal untuk memiliki lebih sedikit, lebih serbaguna, metode: jika ada banyak polimorfisme, yaitu, banyak implementasi dari ini antarmuka ; terutama jika implementasi tersebut dalam kode yang dikembangkan secara terpisah yang tidak dapat diperbarui dalam sinkronisasi (antarmuka didefinisikan oleh perpustakaan).

Dalam hal itu, menyederhanakan pekerjaan menulis setiap implementasi jauh lebih berharga daripada kejelasan penggunaannya, karena yang pertama menghindari bug pelanggaran kontrak (dua metode menjadi tidak konsisten satu sama lain), sedangkan yang kedua hanya menyakitkan keterbacaan, yang dapat dipulihkan oleh fungsi pembantu atau metode superclass yang mendefinisikan getBalancedalam hal charge.

(Ini adalah pola desain, yang saya tidak ingat nama spesifik untuk: mendefinisikan antarmuka penelepon yang kompleks dalam hal yang ramah implementor minimal. Dalam Classic Mac OS antarmuka minimal untuk operasi menggambar disebut "bottleneck" tapi ini sepertinya bukan istilah yang populer.)

Jika ini bukan masalahnya (ada beberapa implementasi atau tepatnya satu) maka memisahkan metode untuk kejelasan, dan untuk memungkinkan penambahan sederhana dari perilaku yang relevan dengan biaya bukan nol charge(), masuk akal.

Kevin Reid
sumber
1
Memang, saya memiliki kasus di mana antarmuka yang lebih minimal dipilih untuk membuat pengujian cakupan lengkap lebih mudah. Tapi ini belum tentu terpapar ke pengguna kelas. Misalnya menyisipkan , menambahkan , dan menghapus semua bisa menjadi pembungkus sederhana dari pengganti umum yang memiliki semua jalur kontrol dipelajari dan diuji dengan baik.
JDługosz
Saya telah melihat API semacam itu. Pengalokasi Lua 5.1+ menggunakannya. Anda menyediakan satu fungsi, dan berdasarkan parameter yang dilewatinya, Anda memutuskan apakah ia sedang mencoba mengalokasikan memori baru, membatalkan alokasi memori , atau mengalokasikan kembali memori dari memori lama. Sangat menyakitkan untuk menulis fungsi seperti itu. Saya lebih suka bahwa Lua baru saja memberi Anda dua fungsi, satu untuk alokasi dan satu untuk deallokasi.
Nicol Bolas
@NicolBolas: Dan tidak ada untuk realokasi? Bagaimanapun, yang memiliki "manfaat" tambahan hampir sama dengan realloc, kadang-kadang, pada beberapa sistem.
Deduplicator
@Dupuplikator: alokasi vs. realokasi adalah ... OK. Tidaklah bagus untuk menempatkan mereka dalam fungsi yang sama, tetapi tidak seburuk menempatkan deallokasi pada fungsi yang sama. Ini juga merupakan perbedaan yang mudah dibuat.
Nicol Bolas
Saya akan mengatakan bahwa mengkonfigurasi deallocation dengan alokasi (kembali) adalah contoh yang sangat buruk dari jenis antarmuka. (Deallokasi memiliki kontrak yang sangat berbeda: itu tidak menghasilkan pointer yang valid.)
Kevin Reid