Pada dasarnya kami ingin hal-hal berperilaku bijaksana
Pertimbangkan masalah berikut:
Saya diberi sekelompok persegi panjang dan saya ingin menambah luasnya 10%. Jadi yang saya lakukan adalah saya mengatur panjang persegi panjang menjadi 1,1 kali dari sebelumnya.
public void IncreaseRectangleSizeByTenPercent(IEnumerable<Rectangle> rectangles)
{
foreach(var rectangle in rectangles)
{
rectangle.Length = rectangle.Length * 1.1;
}
}
Sekarang dalam kasus ini, semua persegi panjang saya sekarang memiliki panjangnya meningkat 10%, yang akan meningkatkan area mereka sebesar 10%. Sayangnya, seseorang benar-benar telah memberi saya campuran kotak dan persegi panjang, dan ketika panjang persegi diubah, begitu pula lebarnya.
Tes unit saya lulus karena saya menulis semua tes unit saya untuk menggunakan kumpulan persegi panjang. Saya sekarang telah memperkenalkan bug halus ke dalam aplikasi saya yang dapat luput dari perhatian selama berbulan-bulan.
Lebih buruk lagi, Jim dari akuntansi melihat metode saya dan menulis beberapa kode lain yang menggunakan fakta bahwa jika ia memasukkan kotak ke dalam metode saya, ia mendapatkan peningkatan 21% dalam ukuran yang sangat bagus. Jim bahagia dan tidak ada yang lebih bijak.
Jim dipromosikan untuk pekerjaan luar biasa ke divisi yang berbeda. Alfred bergabung dengan perusahaan sebagai junior. Dalam laporan bug pertamanya, Jill dari Advertising telah melaporkan bahwa meneruskan kuadrat ke metode ini menghasilkan peningkatan 21% dan ingin bug diperbaiki. Alfred melihat bahwa Squares dan Rectangles digunakan di mana-mana dalam kode dan menyadari bahwa memutus rantai pewarisan tidak mungkin. Dia juga tidak memiliki akses ke kode sumber Akuntansi. Jadi Alfred memperbaiki bug seperti ini:
public void IncreaseRectangleSizeByTenPercent(IEnumerable<Rectangle> rectangles)
{
foreach(var rectangle in rectangles)
{
if (typeof(rectangle) == Rectangle)
{
rectangle.Length = rectangle.Length * 1.1;
}
if (typeof(rectangle) == Square)
{
rectangle.Length = rectangle.Length * 1.04880884817;
}
}
}
Alfred senang dengan keterampilan meretas uber dan Jill menandatangani bahwa bug diperbaiki.
Bulan depan tidak ada yang dibayar karena Akuntansi bergantung pada kemampuan untuk lulus kuadrat ke IncreaseRectangleSizeByTenPercent
metode dan mendapatkan peningkatan area 21%. Seluruh perusahaan masuk ke mode "prioritas 1 bugfix" untuk melacak sumber masalah. Mereka melacak masalahnya sampai pada perbaikan Alfred. Mereka tahu bahwa mereka harus membuat Akuntansi dan Periklanan tetap bahagia. Jadi mereka memperbaiki masalah dengan mengidentifikasi pengguna dengan pemanggilan metode seperti:
public void IncreaseRectangleSizeByTenPercent(IEnumerable<Rectangle> rectangles)
{
IncreaseRectangleSizeByTenPercent(
rectangles,
new User() { Department = Department.Accounting });
}
public void IncreaseRectangleSizeByTenPercent(IEnumerable<Rectangle> rectangles, User user)
{
foreach(var rectangle in rectangles)
{
if (typeof(rectangle) == Rectangle || user.Department == Department.Accounting)
{
rectangle.Length = rectangle.Length * 1.1;
}
else if (typeof(rectangle) == Square)
{
rectangle.Length = rectangle.Length * 1.04880884817;
}
}
}
Dan seterusnya dan seterusnya.
Anekdot ini didasarkan pada situasi dunia nyata yang dihadapi programmer setiap hari. Pelanggaran terhadap prinsip Substitusi Liskov dapat menyebabkan bug yang sangat halus yang hanya dapat diambil bertahun-tahun setelah ditulis, dimana saat memperbaiki pelanggaran akan merusak banyak hal dan tidak memperbaikinya akan membuat marah klien terbesar Anda.
Ada dua cara realistis untuk memperbaiki masalah ini.
Cara pertama adalah membuat Rectangle tidak berubah. Jika pengguna Rectangle tidak dapat mengubah properti Panjang dan Lebar, masalah ini hilang. Jika Anda ingin Rectangle dengan panjang dan lebar yang berbeda, Anda membuat yang baru. Kotak dapat mewarisi dari persegi panjang dengan senang hati.
Cara kedua adalah memutus rantai warisan antara kotak dan persegi panjang. Jika sebuah persegi didefinisikan sebagai memiliki satu SideLength
properti dan persegi panjang memiliki Length
dan Width
properti dan tidak ada warisan, tidak mungkin untuk secara tidak sengaja memecahkan sesuatu dengan mengharapkan persegi panjang dan mendapatkan persegi. Dalam istilah C #, Anda bisa seal
kelas persegi panjang Anda, yang memastikan bahwa semua Persegi Panjang yang Anda dapatkan sebenarnya adalah Persegi Panjang.
Dalam hal ini, saya suka cara "objek abadi" untuk memperbaiki masalah. Identitas persegi panjang adalah panjang dan lebarnya. Masuk akal bahwa ketika Anda ingin mengubah identitas suatu objek, apa yang sebenarnya Anda inginkan adalah objek baru . Jika Anda kehilangan pelanggan lama dan mendapatkan pelanggan baru, Anda tidak mengubah Customer.Id
bidang dari pelanggan lama ke yang baru, Anda membuat yang baru Customer
.
Pelanggaran prinsip Pergantian Liskov adalah hal biasa di dunia nyata, sebagian besar karena banyak kode di luar sana ditulis oleh orang-orang yang tidak kompeten / di bawah tekanan waktu / tidak peduli / melakukan kesalahan. Itu bisa dan memang mengarah pada beberapa masalah yang sangat buruk. Dalam kebanyakan kasus, Anda lebih memilih komposisi daripada warisan .