Oke .. setelah semua diskusi saya mengubah pertanyaan saya sedikit untuk lebih mencerminkan contoh nyata yang saya hadapi.
Saya memiliki dua kelas ModelOne
dan ModelTwo
, kelas-kelas ini melakukan jenis fungsi yang serupa tetapi tidak saling berhubungan. Namun saya memiliki kelas ketiga CommonFunc
yang berisi beberapa fungsi publik yang diimplementasikan di keduanya ModelOne
dan ModelTwo
dan telah difaktorkan sesuai DRY
. Dua model ini dipakai di dalam ModelMain
kelas (yang itu sendiri adalah instantiated di tingkat yang lebih tinggi dll - tapi saya berhenti di tingkat ini).
Kontainer IoC yang saya gunakan adalah Microsoft Unity . Saya tidak berpura-pura menjadi ahli di dalamnya, tetapi pemahaman saya tentang hal itu adalah bahwa Anda mendaftarkan tuple antarmuka dan kelas dengan wadah dan ketika Anda menginginkan kelas konkret Anda meminta wadah IoC untuk objek apa pun yang cocok dengan antarmuka tertentu. Ini menyiratkan bahwa untuk setiap objek yang ingin saya instantiate dari Unity, harus ada antarmuka yang cocok. Karena setiap kelas saya melakukan fungsionalitas yang berbeda (dan -tidak tumpang tindih) ini berarti ada rasio 1: 1 antara antarmuka dan kelas 1 . Namun itu tidak berarti bahwa saya dengan kasar menulis sebuah antarmuka untuk setiap kelas yang saya tulis.
Jadi kode bijak saya berakhir dengan 2 :
public interface ICommonFunc
{
}
public interface IModelOne
{
ICommonFunc Common { get; }
..
}
public interface IModelTwo
{
ICommonFunc Common { get; }
..
}
public interface IModelMain
{
IModelOne One { get; }
IModelTwo Two { get; }
..
}
public class CommonFunc : ICommonFunc { .. }
public class ModelOne : IModelOne { .. }
public class ModelTwo : IModelTwo { .. }
public class ModelMain : IModelMain { .. }
Pertanyaannya adalah bagaimana mengatur solusi saya. Haruskah saya menjaga kelas dan antarmuka bersama? Atau haruskah saya menjaga kelas dan antarmuka bersama? MISALNYA:
Opsi 1 - Diorganisasikan berdasarkan nama kelas
MySolution
|
|-MyProject
| |
|-Models
| |
|-Common
| |
| |-CommonFunc.cs
| |-ICommonFunc.cs
|
|-Main
| |
| |-IModelMain.cs
| |-ModelMain.cs
|
|-One
| |
| |-IModelOne.cs
| |-ModelOne.cs
|
|-Two
|
|-IModelTwo.cs
|-ModelTwo.cs
|
Opsi 2 - Diatur berdasarkan fungsionalitas (kebanyakan)
MySolution
|
|-MyProject
| |
|-Models
| |
|-Common
| |
| |-CommonFunc.cs
| |-ICommonFunc.cs
|
|-IModelMain.cs
|-IModelOne.cs
|-IModelTwo.cs
|-ModelMain.cs
|-ModelOne.cs
|-ModelTwo.cs
|
Opsi 3 - Memisahkan Antarmuka dan Implementasi
MySolution
|
|-MyProject
|
|-Interfaces
| |
| |-Models
| | |
| |-Common
| | |-ICommonFunc.cs
| |
| |-IModelMain.cs
| |-IModelOne.cs
| |-IModelTwo.cs
|
|-Classes
|
|-Models
| |
|-Common
| |-CommonFunc.cs
|
|-ModelMain.cs
|-ModelOne.cs
|-ModelTwo.cs
|
Opsi 4 - Mengambil contoh fungsionalitas lebih lanjut
MySolution
|
|-MyProject
| |
|-Models
| |
|-Components
| |
| |-Common
| | |
| | |-CommonFunc.cs
| | |-ICommonFunc.cs
| |
| |-IModelOne.cs
| |-IModelTwo.cs
| |-ModelOne.cs
| |-ModelTwo.cs
|
|-IModelMain.cs
|-ModelMain.cs
|
Saya agak tidak suka opsi 1 karena nama kelas di jalan. Tetapi karena saya cenderung rasio 1: 1 karena pilihan / penggunaan IoC saya (dan itu mungkin bisa diperdebatkan) ini memiliki keuntungan dalam melihat hubungan antara file.
Opsi 2 menarik bagi saya, tetapi sekarang saya telah mengeruhkan air antara ModelMain
dan sub-model.
Opsi 3 berfungsi untuk memisahkan definisi antarmuka dari implementasi, tetapi sekarang saya memiliki jeda buatan ini di nama path.
Opsi 4. Saya mengambil Opsi 2 dan men-tweak untuk memisahkan komponen dari model induk.
Adakah alasan yang baik untuk lebih memilih yang satu daripada yang lain? Atau kemungkinan tata letak lain yang saya lewatkan?
1. Frank membuat komentar yang memiliki rasio 1: 1 kembali ke C ++ hari dari file .h dan .cpp. Saya tahu dari mana dia berasal. Pemahaman saya tentang Persatuan tampaknya menempatkan saya pada sudut ini, tetapi saya juga tidak yakin bagaimana cara keluar darinya jika Anda juga mengikuti pepatah Program to an interface
Tapi itu diskusi untuk hari lain.
2. Saya telah meninggalkan detail masing-masing konstruktor objek. Di sinilah wadah IoC menyuntikkan objek sesuai kebutuhan.
sumber
Client1
membutuhkanIBase
, itu menyediakanDerived1
. KetikaClient2
membutuhkanIBase
, IoC menyediakan aDerived2
.interface
. Aninterface
benar-benar hanya kelas abstrak dengan semua anggota virtual.Jawaban:
Karena antarmuka secara abstrak mirip dengan kelas dasar, gunakan logika yang sama yang akan Anda gunakan untuk kelas dasar. Kelas-kelas yang mengimplementasikan suatu antarmuka sangat terkait dengan antarmuka.
Saya ragu Anda lebih suka direktori yang disebut "Kelas Dasar"; kebanyakan pengembang tidak menginginkannya, atau direktori yang disebut "Antarmuka". Di c #, direktori juga ruang nama secara default, sehingga membingungkan dua kali lipat.
Pendekatan terbaik adalah memikirkan bagaimana Anda akan memecah kelas / antarmuka jika Anda harus meletakkan beberapa ke perpustakaan yang terpisah, dan mengatur ruang nama / direktori dengan cara yang sama. Pedoman desain .net framework memiliki saran namespace yang mungkin bermanfaat.
sumber
Saya memang mengambil pendekatan kedua, dengan beberapa folder / namespaces dalam proyek yang kompleks tentunya. Itu berarti bahwa saya de-pasangan antarmuka dari implementasi konkret.
Dalam proyek yang berbeda, Anda mungkin harus mengetahui definisi antarmuka, tetapi sama sekali tidak perlu mengetahui kelas konkret yang mengimplementasikannya - terutama ketika Anda menggunakan wadah IoC. Jadi proyek-proyek lain hanya perlu referensi proyek antarmuka, bukan proyek implementasi. Ini dapat membuat referensi rendah, dan dapat mencegah masalah referensi melingkar.
sumber
Seperti biasa dengan pertanyaan desain apa pun, hal pertama yang harus dilakukan adalah menentukan siapa pengguna Anda. Bagaimana mereka akan menggunakan organisasi Anda?
Apakah mereka pembuat kode yang akan menggunakan kelas Anda? Maka memisahkan antarmuka dari kode mungkin yang terbaik.
Apakah mereka pengelola kode Anda? Maka pertahankan antarmuka dan kelas bersama mungkin yang terbaik.
Duduk dan buat beberapa use case. Maka organisasi terbaik mungkin muncul di hadapan Anda.
sumber
Saya pikir lebih baik untuk menyimpan antarmuka di perpustakaan yang terpisah. Pertama, desain semacam ini meningkatkan ketergantungan. Itu sebabnya implementasi antarmuka ini dapat dilakukan oleh bahasa pemrograman lain.
sumber