Saat membaca artikel tentang ISP, tampaknya ada dua definisi yang bertentangan tentang ISP:
Menurut definisi pertama (lihat 1 , 2 , 3 ), ISP menyatakan bahwa kelas yang mengimplementasikan antarmuka tidak boleh dipaksa untuk mengimplementasikan fungsionalitas yang tidak mereka butuhkan. Dengan demikian, antarmuka yang gemukIFat
interface IFat
{
void A();
void B();
void C();
void D();
}
class MyClass: IFat
{ ... }
harus dibagi menjadi antarmuka yang lebih kecil ISmall_1
danISmall_2
interface ISmall_1
{
void A();
void B();
}
interface ISmall_2
{
void C();
void D();
}
class MyClass:ISmall_2
{ ... }
karena dengan cara ini saya MyClass
hanya dapat mengimplementasikan metode yang diperlukan ( D()
dan C()
), tanpa dipaksa juga menyediakan implementasi dummy untuk A()
, B()
dan C()
:
Tetapi menurut definisi kedua (lihat 1 , 2 , jawaban oleh Nazar Merza ), ISP menyatakan bahwa MyClient
memanggil metode MyService
tidak boleh menyadari metode MyService
yang tidak diperlukan. Dengan kata lain, jika MyClient
hanya membutuhkan fungsionalitas C()
dan D()
, maka alih-alih
class MyService
{
public void A();
public void B();
public void C();
public void D();
}
/*client code*/
MyService service = ...;
service.C();
service.D();
kita harus memisahkan MyService's
metode menjadi antarmuka khusus klien :
public interface ISmall_1
{
void A();
void B();
}
public interface ISmall_2
{
void C();
void D();
}
class MyService:ISmall_1, ISmall_2
{ ... }
/*client code*/
ISmall_2 service = ...;
service.C();
service.D();
Jadi dengan definisi sebelumnya, tujuan ISP adalah untuk " membuat kehidupan kelas yang mengimplementasikan antarmuka IFat lebih mudah ", sedangkan dengan yang terakhir tujuan ISP adalah untuk " membuat kehidupan klien yang memanggil metode MyService lebih mudah ".
Manakah dari dua definisi berbeda ISP yang benar?
@MARJAN VENEMA
1.
Jadi ketika Anda akan membagi IFat menjadi antarmuka yang lebih kecil, metode mana yang berakhir di mana antarmuka kecil IS harus diputuskan berdasarkan seberapa kohesifnya para anggota.
Meskipun masuk akal untuk menempatkan metode kohesif dalam antarmuka yang sama, saya pikir dengan pola ISP kebutuhan klien lebih diutamakan daripada "keterpaduan" antarmuka. Dengan kata lain, saya pikir dengan ISP kita harus menggumpal dalam antarmuka yang sama metode-metode yang dibutuhkan oleh klien tertentu, bahkan jika itu berarti meninggalkan antarmuka itu metode-metode yang seharusnya, demi keterpaduan, juga dimasukkan ke dalam antarmuka yang sama?
Jadi, jika ada banyak klien yang hanya perlu dihubungi CutGreens
, tetapi tidak juga GrillMeat
, maka untuk mematuhi pola ISP kita hanya harus memasukkan ke CutGreens
dalam ICook
, tetapi tidak juga GrillMeat
, meskipun kedua metode ini sangat kohesif ?!
2.
Saya pikir bahwa kebingungan Anda berasal dari asumsi tersembunyi dalam definisi pertama: bahwa kelas-kelas pelaksana sudah mengikuti prinsip tanggung jawab tunggal.
Dengan "menerapkan kelas yang tidak mengikuti SRP" apakah Anda merujuk ke kelas yang menerapkan IFat
atau ke kelas yang menerapkan ISmall_1
/ ISmall_2
? Saya menganggap Anda merujuk ke kelas yang menerapkan IFat
? Jika demikian, mengapa Anda menganggap mereka belum mengikuti SRP?
Terima kasih
sumber
Jawaban:
Keduanya benar
Cara saya membacanya, tujuan ISP (Interface Segregation Principle) adalah untuk menjaga antarmuka tetap kecil dan fokus: semua anggota antarmuka harus memiliki kohesi yang sangat tinggi. Kedua definisi tersebut dimaksudkan untuk menghindari antarmuka "jack-of-all-trade-master-of-none".
Segregasi antarmuka dan SRP (Prinsip Tanggung Jawab Tunggal) memiliki tujuan yang sama: memastikan komponen perangkat lunak yang kecil dan sangat kohesif. Mereka saling melengkapi. Pemisahan antarmuka memastikan bahwa antarmuka kecil, fokus, dan sangat kohesif. Mengikuti prinsip tanggung jawab tunggal memastikan bahwa kelas kecil, fokus dan sangat kohesif.
Definisi pertama yang Anda sebutkan berfokus pada pelaksana, yang kedua pada klien. Yang bertentangan dengan @ user61852, saya menganggapnya sebagai pengguna / penelepon antarmuka, bukan pelaksana.
Saya pikir bahwa kebingungan Anda berasal dari asumsi tersembunyi dalam definisi pertama: bahwa kelas-kelas pelaksana sudah mengikuti prinsip tanggung jawab tunggal.
Bagi saya definisi kedua, dengan klien sebagai penelepon antarmuka, adalah cara yang lebih baik untuk mencapai tujuan yang dimaksud.
Memisahkan
Dalam pertanyaan Anda, Anda menyatakan:
Tapi itu membalikkan dunia.
Jadi ketika Anda akan dipisah
IFat
menjadi antarmuka yang lebih kecil, metode mana yang berakhir di manaISmall
antarmuka harus diputuskan berdasarkan seberapa kohesifnya anggota.Pertimbangkan antarmuka ini:
Metode apa yang akan Anda masukkan
ICook
dan mengapa? Apakah AndaCleanSink
bergabungGrillMeat
hanya karena Anda kebetulan memiliki kelas yang hanya melakukan itu dan beberapa hal lain tetapi tidak seperti metode lainnya? Atau apakah Anda akan membaginya menjadi dua antarmuka yang lebih kohesif, seperti:Catatan deklarasi antarmuka
Definisi antarmuka sebaiknya berada pada unitnya sendiri, tetapi jika benar-benar perlu untuk hidup dengan pemanggil atau pelaksana, itu harus benar-benar dengan pemanggil. Kalau tidak, penelepon mendapat ketergantungan langsung pada implementer yang mengalahkan tujuan antarmuka sama sekali. Lihat juga: Mendeklarasikan antarmuka dalam file yang sama dengan kelas dasar, apakah ini praktik yang baik? pada Programmer dan Mengapa kita harus menempatkan antarmuka dengan kelas yang menggunakannya daripada yang mengimplementasikannya? di StackOverflow.
sumber
ICook
bukan tipeSomeCookImplementor
, seperti mandat DIP, maka itu tidak harus bergantung padaSomeCookImplementor
.Anda mengacaukan kata "klien" seperti yang digunakan dalam dokumen Gang of Four dengan "klien" seperti pada konsumen layanan.
"Klien", sebagaimana dimaksud oleh definisi Gang of Four, adalah kelas yang mengimplementasikan antarmuka. Jika kelas A mengimplementasikan antarmuka B, maka mereka mengatakan A adalah klien B. Jika tidak, frasa "klien tidak boleh dipaksa untuk mengimplementasikan antarmuka yang tidak mereka gunakan" tidak akan masuk akal karena "klien" (seperti pada konsumen) tidak dapat mengimplementasikan apa pun. Ungkapan itu hanya masuk akal ketika Anda melihat "klien" sebagai "implementor".
Jika "klien" berarti kelas yang "mengkonsumsi" (panggilan) metode kelas lain yang mengimplementasikan antarmuka besar, maka dengan memanggil dua metode yang Anda pedulikan dan mengabaikan sisanya, akan cukup untuk membuat Anda dipisahkan dari sisa metode yang tidak Anda gunakan.
Semangat prinsip ini adalah menghindari "klien" (kelas yang mengimplementasikan antarmuka) harus menerapkan metode dummy untuk mematuhi seluruh antarmuka ketika hanya peduli tentang serangkaian metode yang terkait.
Juga bertujuan untuk memiliki jumlah kopling yang lebih sedikit sehingga perubahan yang dilakukan di satu tempat menyebabkan dampak yang lebih kecil. Dengan memisahkan antarmuka Anda mengurangi kopling.
Masalah itu muncul ketika antarmuka melakukan terlalu banyak dan memiliki metode yang harus dibagi dalam beberapa antarmuka, bukan hanya satu.
Kedua contoh kode Anda OK . Hanya saja dalam yang kedua Anda menganggap "klien" berarti "kelas yang mengkonsumsi / memanggil layanan / metode yang ditawarkan oleh kelas lain".
Saya tidak menemukan kontradiksi dalam konsep yang dijelaskan dalam tiga tautan yang Anda berikan.
Tetap jelas bahwa "klien" adalah implementor , dalam pembicaraan SOLID.
sumber
ISP adalah tentang mengisolasi klien dari mengetahui lebih banyak tentang layanan daripada yang perlu diketahui (melindunginya terhadap perubahan yang tidak terkait, misalnya). Definisi kedua Anda benar. Untuk bacaan saya, hanya satu dari tiga artikel itu yang menyatakan sebaliknya ( yang pertama ) dan itu benar-benar salah. (Sunting: Tidak, tidak salah, hanya menyesatkan.)
Definisi pertama jauh lebih erat terkait dengan LSP.
sumber