Saya punya kelas: A
itu adalah gabungan dari sejumlah kelas yang lebih kecil B
,, C
dan D
.
B
, C
, Dan D
mengimplementasikan interface IB
, IC
dan ID
masing-masing.
Karena A
mendukung semua fungsionalitas B
, C
dan D
, A
implementasi IB
, IC
dan ID
juga, tetapi sayangnya hal ini menyebabkan banyak perutean ulang dalam implementasiA
Seperti itu:
interface IB
{
int Foo {get;}
}
public class B : IB
{
public int Foo {get {return 5;}}
}
interface IC
{
void Bar();
}
public class C : IC
{
public void Bar()
{
}
}
interface ID
{
string Bash{get; set;}
}
public class D: ID
{
public string Bash {get;set;}
}
class A : IB, IC, ID
{
private B b = new B();
private C c = new C();
private D d = new D();
public int Foo {get {return b.Foo}}
public A(string bash)
{
Bash = bash;
}
public void Bar()
{
c.Bar();
}
public string Bash
{
get {return d.Bash;}
set {d.Bash = value;}
}
}
Apakah ada cara untuk menyingkirkan pengalihan boilerplate ini A
? B
, C
dan D
semua mengimplementasikan fungsionalitas yang berbeda tetapi umum, dan saya suka A
mengimplementasikannya IB
, IC
dan ID
karena itu berarti saya dapat lulus dengan A
sendirinya sebagai ketergantungan yang cocok dengan antarmuka tersebut, dan tidak harus mengekspos metode pembantu dalam.
c#
design-patterns
patterns-and-practices
composition
Nick Udell
sumber
sumber
A
mengimplementasikanIB
,IC
danID
apakah rasanya lebih masuk akal untuk menulisPerson.Walk()
sebagai kebalikan dariPerson.WalkHelper.Walk()
, misalnya.Jawaban:
Apa yang Anda cari biasanya disebut mixin . Sayangnya, C # tidak mendukungnya.
Ada beberapa solusi: satu , dua , tiga , dan banyak lagi.
Saya sebenarnya sangat suka yang terakhir. Gagasan untuk menggunakan kelas parsial yang dihasilkan secara otomatis untuk menghasilkan boilerplate mungkin adalah yang paling dekat yang bisa Anda dapatkan dengan solusi yang benar-benar baik:
sumber
Meskipun tidak mengurangi boilerplate dalam kode itu sendiri, Visual Studio 2015 sekarang hadir dengan opsi refactoring untuk secara otomatis menghasilkan boilerplate untuk Anda.
Untuk menggunakan ini, pertama buat antarmuka
IExample
dan implementasi AndaExample
. Kemudian buat kelas baru AndaComposite
dan buat warisanIExample
, tetapi jangan mengimplementasikan antarmuka.Tambahkan properti atau bidang tipe
Example
keComposite
kelas Anda , buka menu tindakan cepat pada tokenIExample
diComposite
file kelas Anda dan pilih "Terapkan antarmuka melalui 'Contoh'" di mana 'Contoh' dalam hal ini adalah nama bidang atau properti.Anda harus mencatat bahwa sementara Visual Studio akan menghasilkan dan mengarahkan kembali semua metode dan properti yang didefinisikan dalam antarmuka ke kelas pembantu ini untuk Anda, itu tidak akan mengarahkan peristiwa pada saat jawaban ini diposting.
sumber
Kelas Anda melakukan terlalu banyak, itu sebabnya Anda harus mengimplementasikan begitu banyak boilerplate. Anda mengatakan dalam komentar:
Saya tidak setuju. Tindakan berjalan kemungkinan melibatkan banyak aturan dan tidak termasuk dalam
Person
kelas - itu termasuk dalamWalkingService
kelas denganwalk(IWalkable)
metode di mana Orang mengimplementasikanIWalkable
.Selalu ingat prinsip-prinsip SOLID ketika Anda menulis kode. Dua yang paling berlaku di sini adalah Pemisahan Kekhawatiran (ekstrak kode berjalan ke kelas yang terpisah) dan Segregasi Antarmuka (membagi antarmuka menjadi tugas / fungsi / kemampuan tertentu). Kedengarannya seperti Anda mungkin memiliki beberapa I dengan beberapa antarmuka Anda tetapi kemudian membatalkan semua pekerjaan baik Anda dengan membuat satu kelas mengimplementasikannya.
sumber
walk(IWalkable)
metode, saya pribadi tidak suka karena layanan jalan itu menjadi tanggung jawab penelepon, dan bukan Orang, dan konsistensi tidak dijamin. Setiap penelepon harus mengetahui layanan mana yang digunakan untuk menjamin konsistensi, sedangkan menyimpannya di kelas Person berarti harus diubah secara manual agar perilaku berjalan berbeda, sambil tetap memungkinkan inversi ketergantungan.Apa yang Anda cari adalah pewarisan berganda. Namun, baik C # maupun Java tidak memilikinya.
Anda dapat memperpanjang B dari A. Ini akan menghilangkan kebutuhan untuk bidang pribadi untuk B serta lem pengarah ulang. Tetapi Anda hanya dapat melakukan ini untuk salah satu dari B, C atau D.
Sekarang jika Anda dapat membuat C mewarisi dari B, dan D dari C maka Anda hanya perlu memiliki A memperpanjang D ...;) - hanya untuk memperjelas, saya tidak benar-benar mendorongnya dalam contoh ini karena tidak ada indikasi untuk itu.
Di C ++ Anda bisa memiliki A inherit dari B, C, dan D.
Tetapi multiple inheritance membuat program lebih sulit untuk dipikirkan, dan memiliki masalah sendiri; Lebih jauh, seringkali ada cara yang bersih untuk menghindarinya. Namun, terkadang tanpa itu, kita perlu membuat tradeoffs desain antara mengulangi boilerplate dan bagaimana model objek diekspos.
Rasanya agak seperti Anda mencoba mencampur komposisi dan warisan. Jika ini adalah C ++, Anda bisa menggunakan solusi pewarisan murni. Tetapi karena itu bukan saya akan merekomendasikan merangkul komposisi.
sumber