Saya perlu merancang hierarki kelas untuk proyek C # saya. Pada dasarnya, fungsi kelas mirip dengan kelas WinForms jadi mari kita ambil toolkit WinForms sebagai contoh. (Namun, saya tidak bisa menggunakan WinForms atau WPF.)
Ada beberapa sifat dan fungsi inti yang perlu disediakan oleh setiap kelas. Dimensi, posisi, warna, visibilitas (benar / salah), metode Draw, dll.
Saya memerlukan saran desain. Saya telah menggunakan desain dengan kelas dasar abstrak dan antarmuka yang tidak benar-benar tipe tetapi lebih seperti perilaku. Apakah ini desain yang bagus? Jika tidak, apa yang akan menjadi desain yang lebih baik.
Kode ini terlihat seperti ini:
abstract class Control
{
public int Width { get; set; }
public int Height { get; set; }
public int BackColor { get; set; }
public int X { get; set; }
public int Y { get; set; }
public int BorderWidth { get; set; }
public int BorderColor { get; set; }
public bool Visible { get; set; }
public Rectangle ClipRectange { get; protected set; }
abstract public void Draw();
}
Beberapa Kontrol dapat berisi Kontrol lainnya, beberapa hanya dapat berisi (sebagai anak-anak) jadi saya berpikir untuk membuat dua antarmuka untuk fungsi-fungsi ini:
interface IChild
{
IContainer Parent { get; set; }
}
internal interface IContainer
{
void AddChild<T>(T child) where T : IChild;
void RemoveChild<T>(T child) where T : IChild;
IChild GetChild(int index);
}
WinForms mengontrol tampilan teks sehingga ini juga masuk ke antarmuka:
interface ITextHolder
{
string Text { get; set; }
int TextPositionX { get; set; }
int TextPositionY { get; set; }
int TextWidth { get; }
int TextHeight { get; }
void DrawText();
}
Beberapa Kontrol dapat dimasukkan ke dalam Kontrol induknya sehingga:
enum Docking
{
None, Left, Right, Top, Bottom, Fill
}
interface IDockable
{
Docking Dock { get; set; }
}
... dan sekarang mari kita buat beberapa kelas konkret:
class Panel : Control, IDockable, IContainer, IChild {}
class Label : Control, IDockable, IChild, ITextHolder {}
class Button : Control, IChild, ITextHolder, IDockable {}
class Window : Control, IContainer, IDockable {}
"Masalah" pertama yang bisa saya pikirkan di sini adalah bahwa antarmuka pada dasarnya diatur setelah mereka dipublikasikan. Tetapi mari kita asumsikan saya akan dapat membuat antarmuka saya cukup baik untuk menghindari kebutuhan membuat perubahan pada mereka di masa depan.
Masalah lain yang saya lihat dalam desain ini adalah bahwa setiap kelas ini perlu mengimplementasikan antarmuka itu dan duplikasi kode akan segera terjadi. Misalnya dalam Label dan Tombol metode DrawText () berasal dari antarmuka ITextHolder atau di setiap kelas yang berasal dari manajemen anak IContainer.
Solusi saya untuk masalah ini adalah mengimplementasikan fungsionalitas "duplikat" ini dalam adaptor khusus dan meneruskan panggilan kepada mereka. Jadi baik Label dan Tombol akan memiliki anggota TextHolderAdapter yang akan disebut metode di dalam yang diwarisi dari antarmuka ITextHolder.
Saya pikir desain ini harus melindungi saya dari keharusan memiliki banyak fungsi umum di kelas dasar yang dapat dengan cepat membengkak dengan metode virtual dan "kode noise" yang tidak perlu. Perubahan perilaku akan dilakukan dengan memperluas adaptor dan bukan kelas yang diturunkan dari Kontrol.
Saya pikir ini disebut pola "Strategi" dan meskipun ada jutaan pertanyaan dan jawaban tentang topik itu, saya ingin meminta pendapat Anda tentang apa yang saya pertimbangkan untuk desain ini dan kekurangan apa yang dapat Anda pikirkan di pendekatan saya.
Saya harus menambahkan bahwa ada kemungkinan hampir 100% bahwa persyaratan di masa depan akan membutuhkan kelas baru dan fungsi baru.
sumber
System.ComponentModel.Component
atauSystem.Windows.Forms.Control
dari kelas dasar yang ada? Mengapa Anda perlu membuat hierarki kontrol Anda sendiri dan mendefinisikan semua fungsi ini lagi dari awal?IChild
sepertinya nama yang mengerikan.Jawaban:
[1] Tambahkan "getter" & "setter" virtual ke properti Anda, saya harus meretas pustaka kontrol lain, karena saya memerlukan fitur ini:
Saya tahu saran ini lebih "verbose" atau kompleks, tetapi, sangat berguna di dunia nyata.
[2] Tambahkan properti "IsEnabled", jangan bingung dengan "IsReadOnly":
Berarti Anda harus menunjukkan kendali Anda, tetapi jangan perlihatkan informasi apa pun.
[3] Tambahkan properti "IsReadOnly", jangan bingung dengan "IsEnabled":
Itu berarti kontrol dapat menampilkan informasi, tetapi tidak dapat diubah oleh pengguna.
sumber
Pertanyaan bagus! Hal pertama yang saya perhatikan adalah bahwa metode menggambar tidak memiliki parameter, di mana itu menarik? Saya pikir itu harus menerima beberapa objek Surface atau Graphics sebagai parameter (sebagai antarmuka tentu saja).
Satu hal lagi yang saya temukan aneh adalah antarmuka IContainer. Ini memiliki
GetChild
metode yang mengembalikan satu anak dan tidak memiliki parameter - mungkin ia harus mengembalikan koleksi atau jika Anda memindahkan metode Draw ke antarmuka, maka ia dapat mengimplementasikan antarmuka ini dan memiliki metode menggambar yang dapat menarik koleksi anak internal tanpa memaparkannya. .Mungkin untuk ide Anda bisa melihat WPF juga - itu adalah kerangka kerja yang dirancang dengan sangat baik menurut saya. Anda juga dapat melihat pola desain Komposisi dan Dekorator. Yang pertama mungkin berguna untuk elemen kontainer dan yang kedua untuk menambahkan fungsionalitas ke elemen. Saya tidak bisa mengatakan seberapa baik itu akan bekerja pada tampilan pertama, tapi inilah yang saya pikirkan:
Untuk menghindari duplikasi, Anda dapat memiliki sesuatu seperti elemen primitif - seperti elemen teks. Kemudian Anda dapat membangun elemen yang lebih rumit menggunakan primitif ini. Sebagai contoh, a
Border
adalah elemen yang memiliki anak tunggal. Ketika Anda memanggilDraw
metodenya, ia menggambar perbatasan dan latar belakang lalu menarik anaknya di dalam perbatasan. SEBUAHTextElement
hanya menggambar beberapa teks. Sekarang jika Anda menginginkan sebuah tombol, Anda dapat membangun satu dengan menyusun dua primitif itu, yaitu meletakkan teks di dalam Border. Di sini Border adalah sesuatu seperti Dekorator.Postingnya agak panjang jadi beri tahu saya jika Anda menemukan ide itu menarik dan saya bisa memberikan beberapa contoh atau lebih banyak penjelasan.
sumber
Memotong kode dan menuju ke inti pertanyaan Anda "Apakah desain saya dengan kelas dasar abstrak dan antarmuka bukan sebagai tipe tetapi lebih karena perilaku adalah desain yang baik atau tidak.", Saya akan mengatakan tidak ada yang salah dengan pendekatan itu.
Sebenarnya saya telah menggunakan pendekatan seperti itu (di mesin rendering saya) di mana setiap antarmuka mendefinisikan perilaku subsistem yang dikonsumsi. Misalnya, IUpdateable, ICollidable, IRenderable, ILoadable, ILoader dan sebagainya dan sebagainya. Untuk lebih jelasnya, saya juga memisahkan masing-masing "perilaku" ini ke dalam kelas parsial yang terpisah seperti "Entity.IUpdateable.cs", "Entity.IRenderable.cs" dan mencoba untuk menjaga bidang dan metode sel independen mungkin.
Pendekatan menggunakan antarmuka untuk mendefinisikan pola perilaku juga setuju dengan saya karena parameter tipe generik, kendala dan co (ntra) bekerja sangat baik bersama.
Salah satu hal yang saya amati tentang metode desain ini adalah: Jika Anda mendapati diri Anda mendefinisikan antarmuka dengan lebih dari beberapa metode, Anda mungkin tidak menerapkan perilaku.
sumber