Bagaimana cara memverifikasi prinsip substitusi Liskov dalam hierarki warisan?

14

Terinspirasi oleh jawaban ini :

Prinsip Pergantian Liskov mensyaratkan itu

  • Prasyarat tidak dapat diperkuat dalam subtipe.
  • Postconditions tidak dapat dilemahkan dalam subtipe.
  • Invarian tipe supertipe harus dipertahankan dalam subtipe.
  • Batasan sejarah ("aturan sejarah"). Objek dianggap dapat dimodifikasi hanya melalui metode mereka (enkapsulasi). Karena subtipe dapat memperkenalkan metode yang tidak ada dalam supertipe, pengenalan metode ini memungkinkan perubahan status pada subtipe yang tidak diizinkan dalam supertipe. Batasan sejarah melarang ini.

Saya berharap jika seseorang akan memposting hirarki kelas yang melanggar 4 poin ini dan bagaimana menyelesaikannya.
Saya mencari penjelasan terperinci untuk tujuan pendidikan tentang bagaimana mengidentifikasi masing-masing dari 4 poin dalam hierarki dan cara terbaik untuk memperbaikinya.

Catatan:
Saya berharap untuk mengirim contoh kode untuk dikerjakan orang, tetapi pertanyaannya adalah tentang bagaimana mengidentifikasi hierarki yang salah :)

Songo
sumber
Ada beberapa contoh lain dari pelanggaran LSP dalam jawaban atas pertanyaan SO ini
StuartLC

Jawaban:

17

Ini jauh lebih sederhana daripada kutipan yang membuatnya terdengar, seakurat itu.

Saat Anda melihat hierarki warisan, bayangkan metode yang menerima objek dari kelas dasar. Sekarang tanyakan pada diri Anda, apakah ada asumsi bahwa seseorang yang mengedit metode ini mungkin membuat yang tidak valid untuk kelas itu.

Misalnya ( awalnya terlihat di situs Paman Bob ):

public class Square : Rectangle
{
    public Square(double width) : base(width, width)
    {
    }

    public override double Width
    {
        set
        {
            base.Width = value;
            base.Height = value;
        }
        get
        {
            return base.Width;
        }
    }

    public override double Height
    {
        set
        {
            base.Width = value;
            base.Height = value;
        }
        get
        {
            return base.Height;
        }
    }
}

Tampak cukup adil, bukan? Saya telah membuat sejenis Rectangle yang disebut Square, yang menyatakan bahwa Lebar harus sama dengan Tinggi setiap saat. Kotak adalah persegi panjang, jadi cocok dengan prinsip OO, bukan?

Tapi tunggu, bagaimana jika seseorang sekarang menulis metode ini:

public void Enlarge(Rectangle rect, double factor)
{
    rect.Width *= factor;
    rect.Height *= factor;
}

Tidak keren. Tetapi tidak ada alasan bahwa penulis metode ini seharusnya tahu ada potensi masalah.

Setiap kali Anda menurunkan satu kelas dari yang lain, pikirkan tentang kelas dasar dan apa yang mungkin diasumsikan orang tentangnya (seperti "Lebar dan Ketinggian dan keduanya akan mandiri"). Kemudian pikirkan "apakah asumsi-asumsi itu tetap valid di subkelas saya?" Jika tidak, pikirkan kembali desain Anda.

pdr
sumber
Contoh yang sangat bagus dan halus. +1. Apa yang bisa Anda lakukan, adalah membuat Perbesar metode kelas Rectangle dan menimpanya di kelas Square.
marco-fiset
@ marco-fiset: Saya lebih suka melihat Square dan Rectangle dipisahkan, Square dengan hanya satu dimensi, tetapi masing-masing menerapkan IResizable. Memang benar bahwa jika ada metode Draw, mereka akan serupa, tetapi kemudian saya lebih suka mereka berdua merangkum kelas RectangleDrawer, yang mencakup kode umum.
pdr
1
Saya kira ini bukan contoh yang baik. Masalahnya adalah bahwa persegi tidak memiliki lebar atau tinggi. Itu hanya memiliki panjang sisi itu. Masalahnya tidak akan ada jika lebar dan tinggi hanya dapat dibaca, tetapi mereka dapat ditulis dalam kasus ini. Ketika memperkenalkan kondisi yang dapat dimodifikasi, selalu lebih sulit untuk mempertahankan LSP.
SpaceTrucker
@ pdr Terima kasih untuk contohnya, tetapi mengenai 4 kondisi yang saya sebutkan di posting saya bagian mana dari Squarekelas yang melanggar mereka?
Songo
1
@Songo: Ini adalah Kendala Sejarah. Lebih baik dijelaskan di sini: blackwasp.co.uk/LSP.aspx "Berdasarkan sifatnya, subkelas mencakup semua metode dan properti dari superclasses mereka. Mereka juga dapat menambahkan anggota lebih lanjut. Kendala sejarah mengatakan bahwa anggota baru atau yang dimodifikasi tidak boleh memodifikasi keadaan suatu objek dengan cara yang tidak akan diizinkan oleh kelas dasar . Misalnya, jika kelas dasar mewakili objek dengan ukuran tetap, subkelas tidak boleh mengizinkan ukuran ini untuk dimodifikasi. "
pdr