Saya memiliki kelas yang digunakan untuk memproses pembayaran pelanggan. Semua kecuali satu dari metode kelas ini adalah sama untuk setiap pelanggan, kecuali satu yang menghitung (misalnya) berapa banyak pengguna berutang kepada pelanggan. Ini dapat sangat bervariasi dari pelanggan ke pelanggan dan tidak ada cara mudah untuk menangkap logika perhitungan dalam sesuatu seperti file properti, karena bisa ada sejumlah faktor khusus.
Saya bisa menulis kode jelek yang beralih berdasarkan pada customerID:
switch(customerID) {
case 101:
.. do calculations for customer 101
case 102:
.. do calculations for customer 102
case 103:
.. do calculations for customer 103
etc
}
tetapi ini membutuhkan pembangunan kembali kelas setiap kali kita mendapatkan pelanggan baru. Apa cara yang lebih baik?
[Sunting] Artikel "duplikat" sama sekali berbeda. Saya tidak bertanya bagaimana cara menghindari pernyataan switch, saya meminta desain modern yang paling cocok untuk kasus ini - yang bisa saya pecahkan dengan pernyataan switch jika saya ingin menulis kode dinosaurus. Contoh-contoh yang diberikan ada generik, dan tidak membantu, karena pada dasarnya mereka mengatakan "Hei, saklar berfungsi cukup baik dalam beberapa kasus, tidak dalam beberapa kasus lain."
[Sunting] Saya memutuskan untuk memilih jawaban berperingkat teratas (buat kelas "Pelanggan" terpisah untuk setiap pelanggan yang mengimplementasikan antarmuka standar) karena alasan berikut:
Konsistensi: Saya dapat membuat antarmuka yang memastikan semua kelas Pelanggan menerima dan mengembalikan output yang sama, bahkan jika dibuat oleh pengembang lain
Maintainability: Semua kode ditulis dalam bahasa yang sama (Java) sehingga tidak perlu orang lain untuk belajar bahasa pengkodean yang terpisah untuk mempertahankan apa yang seharusnya menjadi fitur mati-sederhana.
Penggunaan Kembali: Jika masalah serupa muncul dalam kode, saya dapat menggunakan kembali kelas Pelanggan untuk menampung sejumlah metode untuk menerapkan logika "khusus".
Keakraban: Saya sudah tahu bagaimana melakukan ini, jadi saya bisa menyelesaikannya dengan cepat dan beralih ke masalah lain yang lebih mendesak.
Kekurangan:
Setiap pelanggan baru membutuhkan kompilasi kelas Pelanggan baru, yang dapat menambah kompleksitas pada bagaimana kami menyusun dan menggunakan perubahan.
Setiap pelanggan baru harus ditambahkan oleh pengembang - orang yang mendukung tidak bisa hanya menambahkan logika ke sesuatu seperti file properti. Ini tidak ideal ... tapi kemudian saya juga tidak yakin bagaimana orang yang mendukung dapat menulis logika bisnis yang diperlukan, terutama jika kompleks dengan banyak pengecualian (seperti yang mungkin terjadi).
Ini tidak akan skala dengan baik jika kami menambahkan banyak, banyak pelanggan baru. Ini tidak diharapkan, tetapi jika itu terjadi kita harus memikirkan kembali banyak bagian lain dari kode serta yang ini.
Bagi Anda yang tertarik, Anda dapat menggunakan Java Reflection untuk memanggil kelas dengan nama:
Payment payment = getPaymentFromSomewhere();
try {
String nameOfCustomClass = propertiesFile.get("customClassName");
Class<?> cpp = Class.forName(nameOfCustomClass);
CustomPaymentProcess pp = (CustomPaymentProcess) cpp.newInstance();
payment = pp.processPayment(payment);
} catch (Exception e) {
//handle the various exceptions
}
doSomethingElseWithThePayment(payment);
Jawaban:
Dua pilihan muncul di benak saya.
Opsi 1: Jadikan kelas Anda kelas abstrak, di mana metode yang bervariasi di antara pelanggan adalah metode abstrak. Kemudian buat subkelas untuk setiap pelanggan.
Opsi 2: Buat
Customer
kelas, atauICustomer
antarmuka, yang berisi semua logika yang bergantung pada pelanggan. Alih-alih memiliki pembayaran kelas pengolahan Anda menerima ID pelanggan, telah itu menerimaCustomer
atauICustomer
objek. Kapan pun ia perlu melakukan sesuatu yang bergantung pada pelanggan, ia memanggil metode yang tepat.sumber
Anda mungkin ingin melihat penulisan perhitungan khusus sebagai "plug-in" untuk aplikasi Anda. Kemudian, Anda akan menggunakan file konfigurasi untuk memberi tahu Anda program plugin penghitung mana yang harus digunakan untuk pelanggan mana. Dengan cara ini, aplikasi utama Anda tidak perlu dikompilasi ulang untuk setiap pelanggan baru - hanya perlu membaca (atau membaca kembali) file konfigurasi dan memuat plug-in baru.
sumber
Saya akan pergi dengan aturan yang ditetapkan untuk menggambarkan perhitungan. Ini dapat disimpan di toko persistensi apa pun dan dimodifikasi secara dinamis.
Sebagai alternatif pertimbangkan ini:
Di mana
customerOpsIndex
dihitung indeks operasi yang tepat (Anda tahu pelanggan mana yang membutuhkan perawatan mana).sumber
Sesuatu seperti di bawah ini:
perhatikan Anda masih memiliki pernyataan beralih di repo. Tidak ada yang benar-benar menyelesaikan masalah ini, pada titik tertentu Anda perlu memetakan id pelanggan ke logika yang diperlukan. Anda bisa menjadi pintar dan memindahkannya ke file config, meletakkan pemetaan dalam kamus atau memuat secara dinamis di majelis logika, tetapi pada dasarnya bermuara untuk beralih.
sumber
Kedengarannya Anda memiliki pemetaan 1 banding 1 dari pelanggan ke kode khusus dan karena Anda menggunakan bahasa yang dikompilasi, Anda harus membangun kembali sistem Anda setiap kali Anda mendapatkan pelanggan baru
Coba bahasa skrip tertanam.
Misalnya, jika sistem Anda di Jawa Anda bisa menanamkan JRuby dan kemudian untuk setiap pelanggan menyimpan potongan kode Ruby yang sesuai. Idealnya di suatu tempat di bawah kontrol versi, baik dalam repo git yang sama atau dalam yang terpisah. Dan kemudian evaluasi potongan itu dalam konteks aplikasi Java Anda. JRuby dapat memanggil fungsi Java apa saja dan mengakses objek Java apa saja.
Ini adalah pola yang sangat umum. Sebagai contoh, banyak game komputer ditulis dalam C ++ tetapi menggunakan skrip Lua yang tertanam untuk mendefinisikan perilaku pelanggan dari setiap lawan dalam game.
Di sisi lain, jika Anda memiliki banyak ke 1 pemetaan dari pelanggan ke kode kustom, cukup gunakan pola "Strategi" seperti yang sudah disarankan.
Jika pemetaan tidak didasarkan pada ID pengguna buat, tambahkan
match
fungsi ke setiap objek strategi dan buat pilihan yang terurut strategi yang akan digunakan.Berikut ini beberapa kode semu
sumber
Aku akan berenang melawan arus.
Saya akan mencoba menerapkan bahasa ekspresi saya sendiri dengan ANTLR .
Sejauh ini, semua jawaban sangat didasarkan pada kustomisasi kode. Menerapkan kelas-kelas konkret untuk setiap pelanggan tampaknya bagi saya bahwa, di beberapa titik di masa depan, tidak akan skala dengan baik. Perawatannya akan mahal dan menyakitkan.
Jadi, dengan Antlr, idenya adalah mendefinisikan bahasa Anda sendiri. Anda dapat mengizinkan pengguna (atau pengembang) untuk menulis aturan bisnis dalam bahasa tersebut.
Mengambil komentar Anda sebagai contoh:
Dengan EL Anda, seharusnya memungkinkan bagi Anda untuk menyatakan kalimat seperti:
Kemudian...
Anda bisa. Ini sebuah string, Anda dapat menyimpannya sebagai properti atau atribut.
Saya tidak akan berbohong Cukup rumit dan sulit. Lebih sulit lagi jika aturan bisnisnya rumit juga.
Berikut beberapa pertanyaan SO yang mungkin menarik bagi Anda:
Catatan: ANTLR menghasilkan kode untuk Python dan Javascript juga. Itu mungkin membantu untuk menulis bukti konsep tanpa terlalu banyak biaya tambahan.
Jika Anda menemukan Antlr terlalu sulit, Anda dapat mencoba dengan lib seperti Expr4J, JEval, Parsii. Ini bekerja dengan tingkat abstraksi yang lebih tinggi.
sumber
Anda setidaknya dapat mengeksternalkan algoritme sehingga kelas Pelanggan tidak perlu berubah ketika pelanggan baru ditambahkan dengan menggunakan pola desain yang disebut Pola Strategi (ada di Gang Empat).
Dari cuplikan yang Anda berikan, dapat diperdebatkan apakah pola strategi akan kurang dapat dipertahankan atau lebih dapat dipelihara, tetapi setidaknya akan menghilangkan pengetahuan kelas Pelanggan tentang apa yang perlu dilakukan (dan akan menghilangkan kasus sakelar Anda).
Objek StrategyFactory akan membuat pointer StrategyIntf (atau referensi) berdasarkan pada CustomerID. Pabrik dapat mengembalikan implementasi default untuk pelanggan yang tidak spesial.
Kelas Pelanggan hanya perlu meminta Pabrik untuk strategi yang benar dan kemudian menyebutnya.
Ini adalah pseudo C ++ yang sangat singkat untuk menunjukkan kepada Anda apa yang saya maksud.
Kelemahan dari solusi ini adalah bahwa, untuk setiap pelanggan baru yang memerlukan logika khusus, Anda harus membuat implementasi baru dari CalculationsStrategyIntf dan memperbarui pabrik untuk mengembalikannya untuk pelanggan yang tepat. Ini juga membutuhkan kompilasi. Tetapi Anda setidaknya akan menghindari kode spageti yang terus tumbuh di kelas pelanggan.
sumber
Buat antarmuka dengan metode tunggal dan gunakan lamdas di setiap kelas implementasi. Atau Anda dapat kelas anonim untuk menerapkan metode untuk klien yang berbeda
sumber