Menghindari Objek Domain yang membengkak

12

Kami mencoba untuk memindahkan data dari lapisan Layanan kami ke lapisan Domain kami menggunakan pendekatan DDD. Saat ini kami memiliki banyak logika bisnis dalam layanan kami, yang tersebar di mana-mana dan tidak mendapat manfaat dari warisan.

Kami memiliki kelas Domain sentral yang merupakan fokus dari sebagian besar pekerjaan kami - Perdagangan. Objek Perdagangan akan tahu cara menentukan harga itu sendiri, cara memperkirakan risiko, memvalidasinya sendiri, dll. Kita kemudian dapat mengganti kondisional dengan polimorfisme. Contoh: SimpleTrade akan memberi harga sendiri dengan satu cara, tetapi ComplexTrade akan memberi harga sendiri dengan cara lain.

Namun, kami khawatir ini akan menggembungkan kelas Dagang. Ini benar-benar harus bertanggung jawab atas pemrosesan sendiri tetapi ukuran kelas akan meningkat secara eksponensial karena lebih banyak fitur ditambahkan.

Jadi kita punya pilihan:

  1. Masukkan logika pemrosesan di kelas Perdagangan. Pemrosesan logika sekarang bersifat polimorfik berdasarkan jenis perdagangan, tetapi kelas Perdagangan sekarang memiliki banyak tanggung jawab (harga, risiko, dll.) Dan besar
  2. Masukkan logika pemrosesan ke kelas lain seperti TradePricingService. Tidak lagi polimorfik dengan pohon warisan Perdagangan, tetapi kelas lebih kecil dan lebih mudah untuk diuji.

Apa yang akan menjadi pendekatan yang disarankan?

djcredo
sumber
Tidak masalah - Saya senang menerima migrasi!
1
Waspadai yang sebaliknya: martinfowler.com/bliki/AnemicDomainModel.html
TrueWill
1
"ukuran kelas akan meningkat secara eksponensial karena lebih banyak fitur ditambahkan" - setiap programmer harus tahu lebih baik daripada menyalahgunakan kata "eksponensial" seperti itu.
Michael Borgwardt
@Piskvor itu hanya bodoh
Arnis Lapsa
@Arnis L .: Terima kasih atas komentar bijaksana Anda. Perhatikan bahwa ini adalah, kutipan, "dimigrasikan dari stackoverflow.com 22 Nov pukul 22:19", bersama dengan komentar saya kemudian. Saya sekarang telah menghapus komentar saya bahwa "ini akan lebih baik di programmer.SE"; sekarang, apakah Anda memiliki sesuatu untuk ditambahkan, atau apakah itu satu-satunya ide yang ingin Anda ungkapkan?
Piskvor meninggalkan gedung

Jawaban:

8

Jika Anda menggunakan Domain Driven, pertimbangkan untuk memperlakukan kelas Perdagangan Anda sebagai akar agregat dan pisahkan tanggung jawabnya ke kelas lain.

Anda tidak ingin berakhir dengan subkelas Perdagangan untuk setiap kombinasi harga dan risiko, sehingga Perdagangan dapat berisi objek Harga dan Risiko (komposisi). Objek Harga dan Risiko melakukan perhitungan aktual, tetapi tidak terlihat oleh kelas mana pun kecuali Perdagangan. Anda bisa mengurangi ukuran Perdagangan, sambil tidak mengekspos kelas baru Anda ke dunia luar.

Cobalah menggunakan komposisi untuk menghindari pohon warisan yang besar. Terlalu banyak warisan dapat menyebabkan situasi di mana Anda mencoba untuk menyisir perilaku yang tidak benar-benar sesuai dengan model. Lebih baik menarik tanggung jawab itu ke kelas baru.

Terry Wilcox
sumber
Saya setuju. Memikirkan objek dalam hal perilaku yang membentuk solusi (Harga, RiskAssessment) daripada mencoba memodelkan masalah menghindari kelas monolitik ini.
Garrett Hall
Setuju juga. Komposisi, banyak kelas yang lebih kecil dengan tanggung jawab tunggal, spesifik, penggunaan metode pribadi yang terbatas, banyak antarmuka yang bahagia, dll.
Ian
4

Pertanyaan Anda pasti membuat saya berpikir tentang Pola Strategi . Kemudian Anda dapat bertukar dalam berbagai strategi perdagangan / harga, mirip dengan apa yang Anda sebut a TradePricingService.

Saya pasti berpikir saran yang akan Anda dapatkan di sini adalah menggunakan komposisi alih-alih warisan.

Scott Whitlock
sumber
2

Salah satu solusi yang mungkin saya gunakan dalam kasus serupa, adalah pola desain adaptor (halaman yang direferensikan berisi banyak kode sampel). Mungkin dikombinasikan dengan pola desain delegasi untuk memudahkan akses ke metode utama.

Pada dasarnya, Anda membagi fungsionalitas Trader menjadi beberapa area yang terpisah - mis. Penanganan harga, risiko, validasi, semua area yang berbeda. Untuk setiap area, Anda kemudian dapat mengimplementasikan hierarki kelas terpisah yang menangani fungsionalitas yang tepat dalam varian yang dibutuhkan - semua antarmuka umum untuk setiap area. Kelas Trader utama kemudian direduksi menjadi data paling mendasar dan referensi ke sejumlah objek handler, yang dapat dibangun saat dibutuhkan. Suka

interface IPriceCalculator {
  double getPrice(ITrader t);
}
interface ITrader {
  IPriceCalculator getPriceCalculator();
}
class Tracer implements ITrader {
  private IPriceCalculator myPriceCalculator = null;
  IPriceCalculator getPriceCalculator() {
    if (myPriceCalculator == null)
      myPriceCalculator = PriceCalculatorFactory.get(this);
    return myPriceCalculator;
  }
}

Salah satu keuntungan utama dari pendekatan ini, adalah bahwa kemungkinan kombinasi mis. Harga dan rick dipisahkan sepenuhnya dan dengan demikian dapat digabungkan sesuai kebutuhan. Ini agak sulit dengan warisan single-threaded dari sebagian besar bahasa pemrograman. Keputusan kombinasi mana yang akan digunakan bahkan dapat dihitung sangat terlambat :-)

Saya biasanya mencoba untuk menjaga kelas adaptor - mis. Sub-kelas di IPriceCalculatoratas - stateless. Yaitu kelas-kelas ini seharusnya tidak mengandung data lokal jika memungkinkan, untuk mengurangi jumlah instance yang harus dibuat. Jadi saya biasanya menyediakan objek utama yang diadaptasi sebagai argumen dalam semua metode - seperti di getPrice(ITrader)atas.

Tonny Madsen
sumber
2

tidak bisa bicara banyak tentang domain Anda, tetapi

Kami memiliki kelas Domain sentral yang merupakan fokus dari sebagian besar pekerjaan kami - Perdagangan.

... itu bau bagiku. Saya mungkin akan mencoba untuk menggambarkan tanggung jawab kelas yang berbeda dan akhirnya menguraikannya menjadi agregat yang berbeda. Agregat kemudian akan dirancang di sekitar peran dan / atau sudut pandang para pemangku kepentingan / ahli domain yang terlibat. Jika Harga dan Risiko terlibat dalam kasus perilaku / penggunaan yang sama, mereka mungkin termasuk kelompok agregasi yang sama. Tetapi jika mereka dipisahkan mereka mungkin milik agregat terpisah.

Mungkin RiskEvaluation mungkin merupakan entitas yang terpisah di domain Anda, akhirnya dengan siklus hidup tertentu (saya tidak bisa mengatakan, saya hanya berspekulasi ... Anda tahu domain Anda, saya tidak), tetapi kuncinya adalah membuat konsep implisit eksplisit dan untuk menghindari kopling yang tidak didorong oleh perilaku, tetapi hanya oleh kopling data lama.

Secara umum, saya akan berpikir tentang perilaku yang diharapkan, dan siklus hidup yang berbeda dari komponen yang terlibat. Hanya menambahkan perilaku di atas data yang dikelompokkan membuat objek kembung. Tetapi data telah dikelompokkan sesuai dengan desain berbasis data yang ada, jadi tidak perlu berpegang teguh pada itu.

ZioBrando
sumber