Seperti prinsip pemisahan antarmuka menyarankan tidak ada klien harus dipaksa untuk bergantung pada metode yang tidak digunakan, sehingga klien tidak boleh menerapkan metode kosong untuk metode antarmuka, jika tidak metode antarmuka ini harus dimasukkan ke antarmuka lain.
Tetapi bagaimana dengan metode konkret? Haruskah saya memisahkan metode yang tidak akan digunakan oleh setiap klien? Pertimbangkan kelas berikut:
public class Car{
....
public boolean isQualityPass(){
...
}
public int getTax(){
...
}
public int getCost(){
...
}
}
public class CarShop{
...
public int getCarPrice(int carId){
Car car=carList[carId];
int price=car.getTax() + car.getCost()... (some formula);
return price;
}
}
pada kode di atas, CarShop tidak menggunakan metode isQualityPass () di Car sama sekali, haruskah saya memisahkan isQualityPass () ke dalam kelas baru:
public class CheckCarQualityPass{
public boolean isQualityPass(Car car){
}
}
untuk mengurangi kopling CarShop? Karena saya pikir sekali jika isQualityPass () membutuhkan dependensi tambahan, misalnya:
public boolean isQualityPass(){
HttpClient client=...
}
CarShop akan bergantung pada HttpClient bahkan tidak pernah menggunakan HttpClient sebenarnya. Jadi pertanyaan saya adalah: sesuai dengan prinsip antarmuka-pemisahan, haruskah saya memisahkan metode konkret yang tidak akan digunakan oleh setiap klien, sehingga metode-metode tersebut bergantung pada klien hanya ketika klien benar-benar menggunakan, untuk mengurangi sambungan?
sumber
Car
kelas Anda yang tidak Anda inginkan (semua) diketahui oleh pengguna, maka buat (lebih dari satu) antarmuka yangCar
diimplementasikan oleh kelas yang hanya menyatakan metode yang berguna dalam konteks antarmuka.Jawaban:
Dalam contoh Anda,
CarShop
tidak bergantung padaisQualityPass
, dan tidak dipaksa untuk membuat implementasi kosong untuk suatu metode. Bahkan tidak ada antarmuka yang terlibat. Jadi istilah "ISP" sama sekali tidak cocok di sini. Dan selama metode sukaisQualityPass
adalah metode yang cocok denganCar
objek, tanpa dibebani dengan tanggung jawab atau ketergantungan tambahan, ini baik-baik saja. Tidak perlu mengubah metode publik suatu kelas ke tempat lain hanya karena ada satu klien yang tidak menggunakan metode tersebut.Namun, membuat kelas domain seperti
Car
langsung bergantung pada sesuatu sepertiHttpClient
mungkin juga bukan ide yang baik, terlepas dari mana klien menggunakan atau tidak menggunakan metode ini. Memindahkan logika ke kelas yang terpisahCheckCarQualityPass
tidak disebut "ISP", ini disebut "pemisahan masalah" . Perhatian terhadap objek mobil yang dapat digunakan kembali mungkin tidak untuk membuat panggilan HTTP eksternal, setidaknya tidak secara langsung, ini membatasi usabilitas dan terlebih lagi testabilitas terlalu banyak.Jika
isQualityPass
tidak dapat dengan mudah dipindahkan ke kelas lain, alternatifnya adalah membuatHttp
panggilan melalui antarmuka abstrakIHttpClient
yang disuntikkanCar
pada waktu konstruksi, atau dengan menyuntikkan seluruh strategi pengecekan "QualityPass" (dengan permintaan Http dienkapsulasi) keCar
objek . Tapi itu hanya IMHO solusi terbaik kedua, karena meningkatkan kompleksitas keseluruhan alih-alih menguranginya.sumber
Car
. Itu tidak akan menjadi pilihan pertama saya untuk solusi (setidaknya tidak dalam konteks contoh yang dibuat-buat ini). Namun, mungkin lebih masuk akal dalam kode "nyata", saya tidak tahu.Prinsip pemisahan antarmuka bukan tentang melarang akses ke apa yang tidak Anda butuhkan. Ini tentang tidak menuntut akses ke apa yang tidak Anda butuhkan.
Antarmuka tidak dimiliki oleh kelas yang mengimplementasikannya. Itu dimiliki oleh benda-benda yang menggunakannya.
Apa yang digunakan di sini adalah
getTax()
dangetCost()
. Yang ditekankan adalah segala sesuatu dapat diaksesCar
. Masalahnya adalah bersikerasCar
berarti bersikeras aksesisQualityPass()
yang tidak diperlukan.Ini bisa diperbaiki. Anda bertanya apakah itu dapat diperbaiki secara konkret. Bisa.
Tidak satu pun dari kode itu yang tahu apakah
CarLiability
antarmuka atau kelas yang konkret. Itu hal yang baik. Itu tidak mau tahu.Jika itu sebuah antarmuka
Car
mungkin mengimplementasikannya. Ini tidak akan melanggar ISP karena meskipunisQuality()
diCar
CarShop
tidak bersikeras. Ini baikJika ini konkret, itu mungkin karena
isQuality()
tidak ada atau telah pindah ke tempat lain. Ini baikMungkin juga itu
CarLiability
adalah pembungkus konkretCar
yang mendelegasikan pekerjaan untuk itu. SelamaCarLiability
tidak mengeksposisQuality()
makaCarShop
baik-baik saja. Tentu saja ini hanya menendang kaleng di jalan danCarLiability
harus mencari cara untuk mengikuti ISP denganCar
cara yang samaCarShop
harus dilakukan.Singkatnya,
isQuality()
tidak perlu dihapus dariCar
karena ISP. Kebutuhan tersirat perluisQuality()
dihapus dariCarShop
karenaCarShop
tidak membutuhkannya, jadi tidak seharusnya memintanya.sumber
Tidak juga. Ada berbagai cara untuk menyembunyikan
Car.isQualityPass
dariCarShop
.1. Akses pengubah
Dari sudut pandang Hukum Demeter , kita dapat mempertimbangkan
Car
danCardShop
tidak menjadi teman . Itu melegitimasi kita untuk melakukan yang berikutnya.Waspadai kedua komponen tersebut dalam paket yang berbeda. Sekarang
CarShop
tidak memiliki visibilitas atas perilaku yangCar
dilindungi . (Maafkan saya sebelumnya jika contoh di atas terlihat sangat sederhana).2. Segregasi Antarmuka
The ISP bekerja dari premis bahwa kita bekerja dengan abstraksi, tidak dengan kelas beton. Saya akan berasumsi bahwa Anda sudah terbiasa dengan implementasi ISP dan dengan antarmuka peran .
Meskipun
Car
implementasi yang sebenarnya , tidak ada yang menghentikan kita dari mempraktikkan ISP.Apa yang saya lakukan di sini. Saya telah mempersempit interaksi antara
Car
danCarShop
melalui antarmuka peran Billable . Waspadai perubahan padagetPrice
tanda tangan. Saya telah memodifikasi argumen dengan sengaja. Saya ingin menjelaskan bahwaCarShop
hanya "terikat / terikat" ke salah satu antarmuka peran yang tersedia. Saya bisa saja mengikuti implementasi yang sebenarnya tetapi saya tidak tahu detail implementasi yang sebenarnya dan saya takut yang sebenarnyagetPrice(String carId)
memiliki akses (visibilitas) ke kelas yang konkret. Jika sudah, semua pekerjaan yang dilakukan dengan ISP menjadi tidak berguna karena itu ada di tangan pengembang untuk melakukan casting dan hanya bekerja dengan antarmuka Billable . Tidak peduli seberapa metodisnya kita, godaan akan selalu ada.3. Tanggung jawab tunggal
Saya khawatir saya tidak dalam posisi untuk mengatakan apakah ketergantungan antara
Car
danHttpClient
memadai, tapi saya setuju dengan @DocBrown, itu memunculkan beberapa peringatan yang layak review desain. Baik Hukum Demeter maupun ISP tidak akan membuat desain Anda "lebih baik" pada saat ini. Mereka hanya akan menutupi masalah, bukan memperbaikinya.Saya telah menyarankan DocBrown Pola Strategi sebagai solusi yang mungkin. Saya setuju dengan dia bahwa polanya menambah kompleksitas, tetapi saya juga berpikir bahwa desain ulang akan. Ini adalah trade-off, semakin banyak decoupling yang kita inginkan, semakin banyak bagian bergerak yang kita miliki (biasanya). Bagaimanapun, saya pikir keduanya setuju dengan desain ulang sangat disarankan.
Menyimpulkan
Tidak, Anda tidak perlu memindahkan metode konkret ke kelas luar agar tidak membuatnya dapat diakses. Mungkin ada banyak konsumen. Apakah Anda akan memindahkan semua metode konkret ke kelas luar setiap kali konsumen baru ikut bermain? Saya harap kamu tidak.
sumber