1The Liskov Substitution PrincipleThis is the second of my Engineering translation - 1The Liskov Substitution PrincipleThis is the second of my Engineering Indonesian how to say

1The Liskov Substitution PrincipleT

1
The Liskov Substitution Principle
This is the second of my Engineering Notebookcolumns for The C++ Report. The articles
that will appear in this column will focus on the use of C++ and OOD, and will address
issues of software engineering. I will strive for articles
that are pragmatic and
directly useful to the software engineer in the
trenches. In these articles I
will make use of Booch’s
and Rumbaugh’s new unifiednotation (Version 0.8)
for documenting object oriented designs. The sidebar
provides a brief lexicon of
this notation.
Introduction
My last column (Jan, 96) talked about the Open-Closed principle. This principle is the
foundation for building code that is maintainable and reusable. It states that well designed
code can be extended without modification; that in a well designed program new features
are added by adding new code, rather than by changing old, already working, code.
The primary mechanisms behind the Open-Closed principle are abstraction and polymorphism. In statically typed languages like C++, one of the key mechanisms that supports abstraction and polymorphism is inheritance. It is by using inheritance that we can
create derived classes that conform to the abstract polymorphic interfaces defined by pure
virtual functions in abstract base classes.
What are the design rules that govern this particular use of inheritance? What are the
characteristics of the best inheritance hierarchies? What are the traps that will cause us to
create hierarchies that do not conform to the Open-Closed principle? These are the questions that this article will address.
Base Class
Derived 1 Derived 2
Had by
Reference
Had By Value
Used
Sidebar: Unified Notation 0.8
2 The Liskov Substitution Principle
The Liskov Substitution Principle
FUNCTIONSTHATUSEPOINTERSORREFERENCESTOBASE
CLASSESMUSTBEABLETOUSEOBJECTSOFDERIVEDCLASSES
WITHOUTKNOWINGIT.
The above is a paraphrase of the Liskov Substitution Principle (LSP). Barbara Liskov first
wrote it as follows nearly 8 years ago
1
:
What is wanted here is something like the following substitution property: If
for each object o
1of type S there is an object o2
of type T such that for all
programs Pdefined in terms of T, the behavior of P is unchanged when o1is
substituted for o2
then S is a subtype of T.
The importance of this principle becomes obvious when you consider the consequences of violating it. If there is a function which does not conform to the LSP, then that
function uses a pointer or reference to a base class, but must knowabout all the derivatives
of that base class. Such a function violates the Open-Closed principle because it must be
modified whenever a new derivative of the base class is created.
A Simple Example of a Violation of LSP
One of the most glaring violations of this principle is the use of C++ Run-Time Type
Information (RTTI) to select a function based upon the type of an object. i.e.:
void DrawShape(const Shape& s)
{
if (typeid(s) == typeid(Square))
DrawSquare(static_cast(s));
else if (typeid(s) == typeid(Circle))
DrawCircle(static_cast(s));
}
[Note: static_castis one of the new cast operators. In this example it works
exactly like a regular cast. i.e. DrawSquare((Square&)s);. However the new syntax has more stringent rules that make is safer to use, and is easier to locate with tools such
as grep. It is therefore preferred.]
Clearly the DrawShapefunction is badly formed. It must know about every possible
derivative of the Shapeclass, and it must be changed whenever new derivatives of
Shapeare created. Indeed, many view the structure of this function as anathema to
Object Oriented Design.
1. Barbara Liskov, “Data Abstraction and Hierarchy,” SIGPLAN Notices, 23,5 (May, 1988).
3 : The Liskov Substitution Principle
Square and Rectangle, a More Subtle Violation.
However, there are other, far more subtle, ways of violating the LSP. Consider an
application which uses the Rectangleclass as described below:
class Rectangle
{
public:
void SetWidth(double w) {itsWidth=w;}
void SetHeight(double h) {itsHeight=w;}
double GetHeight() const {return itsHeight;}
double GetWidth() const {return itsWidth;}
private:
double itsWidth;
double itsHeight;
};
Imagine that this application works well, and is installed in
many sites. As is the case with all successful software, as its
users’ needs change, new functions are needed. Imagine that
one day the users demand the ability to manipulate squares in
addition to rectangles.
It is often said that, in C++, inheritance is the ISA relationship. In other words, if a new kind of object can be said to fulfill
the ISA relationship with an old kind of object, then the class of
the new object should be derived from the class of the old
object.
Clearly, a square is a rectangle for all normal intents and
purposes. Since the ISA relationship holds, it is logical to
model the Squareclass as being derived from Rectangle.
(See Figure 1.)
This use of the ISA relationship is considered by many to be one of the fundamental
techniques of Object Oriented Analysis. A square is a rectangle, and so the Squareclass
should be derived from the Rectangleclass. However this kind of thinking can lead to
some subtle, yet significant, problems. Generally these problem are not foreseen until we
actually try to code the application.
Our first clue might be the fact that a Squaredoes not need both itsHeightand
itsWidthmember variables. Yet it will inherit them anyway. Clearly this is wasteful.
Moreover, if we are going to create hundreds of thousands of Squareobjects (e.g. a
CAD/CAE program in which every pin of every component of a complex circuit is drawn
as a square), this waste could be extremely significant.
However, let’s assume that we are not very concerned with memory efficiency. Are
there other problems? Indeed! Squarewill inherit the SetWidthand SetHeight
functions. These functions are utterly inappropriate for a Square, since the width and
height of a square are identical.”. This should be a significant clue that there is a problem
Rectangle
Square
Figure 1.
4 The Liskov Substitution Principle
with the design. However, there is a way to sidestep the problem. We could override SetWidthand SetHeightas follows:
void Square::SetWidth(double w)
{
Rectangle::SetWidth(w);
Rectangle::SetHeight(w);
}
void Square::SetHeight(double h)
{
Rectangle::SetHeight(h);
Rectangle::SetWidth(h);
}
Now, when someone sets the width of a Squareobject, its height will change correspondingly. And when someone sets its height, the width will change with it. Thus, the
invariants of the Squareremain intact. The Squareobject will remain a mathematically
proper square.
Square s;
s.SetWidth(1); // Fortunately sets the height to 1 too.
s,SetHeight(2); // sets width and heigt to 2, good thing.
But consider the following function:
void f(Rectangle& r)
{
r.SetWidth(32); // calls Rectangle::SetWidth
}
If we pass a reference to a Squareobject into this function, the Squareobject will
be corrupted because the height won’t be changed. This is a clear violation of LSP. The f
function does not work for derivatives of its arguments. The reason for the failure is that
SetWidthand SetHeightwere not declared virtualin Rectangle.
We can fix this easily. However, when the creation of a derived class causes us to
make changes to the base class, it often implies that the design is faulty. Indeed, it violates
the Open-Closed principle. We might counter this with argument that forgetting to make
SetWidthand SetHeightvirtualwas the real design flaw, and we are just fixing it
now. However, this is hard to justify since setting the height and width of a rectangle are
exceedingly primitive operations. By what reasoning would we make them virtualif
we did not anticipate the existence of Square.
Still, let’s assume that we accept the argument, and fix the classes. We wind up with
the following code:
class Rectangle
{
public:
virtual void SetWidth(double w) {itsWidth=w;}
virtual void SetHeight(double h) {itsHeight=h;}
double GetHeight() const {return itsHeight;}
double GetWidth() const {return itsWidth;}
5 : The Liskov Substitution Principle
private:
double itsHeight;
double itsWidth;
};
class Square : public Rectangle
{
public:
virtual void SetWidth(double w);
virtual void SetHeight(double h);
};
void Square::SetWidth(double w)
{
Rectangle::SetWidth(w);
Rectangle::SetHeight(w);
}
void Square::SetHeight(double h)
{
Rectangle::SetHeight(h);
Rectangle::SetWidth(h);
}
The Real Problem
At this point in time we have two classes, Squareand Rectangle, that appear to work.
No matter what you do to a Squareobject, it will remain consistent with a mathematical
square. And regardless of what you do to a Rectangleobject, it will remain a mathematical rectangle. Moreover, you can pass a Squareinto a function that accepts a pointer
or reference to a Rectangle, and the Squarewill still act like a square and will remain
consistent.
Thus, we might conclude that the model is now self consistent, and correct. However,
this conclusion would be amiss. A model that is self consistent is not necessarily consistent with all its users! Consider function gbelow.
void g(Rectangle& r)
{
r.SetWidth(5);
r.SetHeight(4);
assert(r.GetWidth() * r.GetHeight()) == 20);
}
This function invokes the SetWidthand SetHeightmembers of what it believes
to be a Rectangle. The function works just fine for a Rectangle, but declares an
assertion error if passed a Square. So here is the real problem: Was the programmer who
wrote that function justified in assuming that changing the width of a Rectangleleaves
its height unchanged?
Clearly, the programmer of gmade this very reasonable assumption. Passing a
Squareto functions whose programmers made this assumption will result in problems.
Therefore, there exist functions that take pointers or references to Rectangleobjects,
6 The Liskov Substitution Principle
but cannot operate properly upon Squareobjects. These functions expose a violation of
the LSP. The addition of
0/5000
From: -
To: -
Results (Indonesian) 1: [Copy]
Copied!
1The Liskov Substitution PrincipleThis is the second of my Engineering Notebookcolumns for The C++ Report. The articlesthat will appear in this column will focus on the use of C++ and OOD, and will addressissues of software engineering. I will strive for articlesthat are pragmatic anddirectly useful to the software engineer in thetrenches. In these articles Iwill make use of Booch’sand Rumbaugh’s new unifiednotation (Version 0.8)for documenting object oriented designs. The sidebarprovides a brief lexicon ofthis notation.IntroductionMy last column (Jan, 96) talked about the Open-Closed principle. This principle is thefoundation for building code that is maintainable and reusable. It states that well designedcode can be extended without modification; that in a well designed program new featuresare added by adding new code, rather than by changing old, already working, code. The primary mechanisms behind the Open-Closed principle are abstraction and polymorphism. In statically typed languages like C++, one of the key mechanisms that supports abstraction and polymorphism is inheritance. It is by using inheritance that we cancreate derived classes that conform to the abstract polymorphic interfaces defined by purevirtual functions in abstract base classes.What are the design rules that govern this particular use of inheritance? What are thecharacteristics of the best inheritance hierarchies? What are the traps that will cause us tocreate hierarchies that do not conform to the Open-Closed principle? These are the questions that this article will address.Base ClassDerived 1 Derived 2Had byReferenceHad By ValueUsedSidebar: Unified Notation 0.82 The Liskov Substitution PrincipleThe Liskov Substitution PrincipleFUNCTIONSTHATUSEPOINTERSORREFERENCESTOBASECLASSESMUSTBEABLETOUSEOBJECTSOFDERIVEDCLASSESWITHOUTKNOWINGIT.The above is a paraphrase of the Liskov Substitution Principle (LSP). Barbara Liskov firstwrote it as follows nearly 8 years ago1:What is wanted here is something like the following substitution property: Iffor each object o1of type S there is an object o2of type T such that for allprograms Pdefined in terms of T, the behavior of P is unchanged when o1issubstituted for o2then S is a subtype of T.The importance of this principle becomes obvious when you consider the consequences of violating it. If there is a function which does not conform to the LSP, then thatfunction uses a pointer or reference to a base class, but must knowabout all the derivativesof that base class. Such a function violates the Open-Closed principle because it must bemodified whenever a new derivative of the base class is created.A Simple Example of a Violation of LSPOne of the most glaring violations of this principle is the use of C++ Run-Time TypeInformation (RTTI) to select a function based upon the type of an object. i.e.:void DrawShape(const Shape& s){if (typeid(s) == typeid(Square))DrawSquare(static_cast(s));else if (typeid(s) == typeid(Circle))DrawCircle(static_cast(s));}[Note: static_castis one of the new cast operators. In this example it worksexactly like a regular cast. i.e. DrawSquare((Square&)s);. However the new syntax has more stringent rules that make is safer to use, and is easier to locate with tools suchas grep. It is therefore preferred.]Clearly the DrawShapefunction is badly formed. It must know about every possiblederivative of the Shapeclass, and it must be changed whenever new derivatives ofShapeare created. Indeed, many view the structure of this function as anathema toObject Oriented Design.1. Barbara Liskov, “Data Abstraction and Hierarchy,” SIGPLAN Notices, 23,5 (May, 1988).3 : The Liskov Substitution PrincipleSquare and Rectangle, a More Subtle Violation.However, there are other, far more subtle, ways of violating the LSP. Consider anapplication which uses the Rectangleclass as described below:class Rectangle{public:void SetWidth(double w) {itsWidth=w;}void SetHeight(double h) {itsHeight=w;}double GetHeight() const {return itsHeight;}double GetWidth() const {return itsWidth;}private:double itsWidth;double itsHeight;};Imagine that this application works well, and is installed inmany sites. As is the case with all successful software, as itsusers’ needs change, new functions are needed. Imagine thatone day the users demand the ability to manipulate squares inaddition to rectangles.It is often said that, in C++, inheritance is the ISA relationship. In other words, if a new kind of object can be said to fulfillthe ISA relationship with an old kind of object, then the class ofthe new object should be derived from the class of the oldobject.Clearly, a square is a rectangle for all normal intents andpurposes. Since the ISA relationship holds, it is logical tomodel the Squareclass as being derived from Rectangle.(See Figure 1.)This use of the ISA relationship is considered by many to be one of the fundamentaltechniques of Object Oriented Analysis. A square is a rectangle, and so the Squareclassshould be derived from the Rectangleclass. However this kind of thinking can lead tosome subtle, yet significant, problems. Generally these problem are not foreseen until weactually try to code the application. Our first clue might be the fact that a Squaredoes not need both itsHeightanditsWidthmember variables. Yet it will inherit them anyway. Clearly this is wasteful.Moreover, if we are going to create hundreds of thousands of Squareobjects (e.g. aCAD/CAE program in which every pin of every component of a complex circuit is drawnas a square), this waste could be extremely significant. However, let’s assume that we are not very concerned with memory efficiency. Arethere other problems? Indeed! Squarewill inherit the SetWidthand SetHeightfunctions. These functions are utterly inappropriate for a Square, since the width andheight of a square are identical.”. This should be a significant clue that there is a problemRectangleSquareFigure 1.4 The Liskov Substitution Principlewith the design. However, there is a way to sidestep the problem. We could override SetWidthand SetHeightas follows:void Square::SetWidth(double w){Rectangle::SetWidth(w);Rectangle::SetHeight(w);}void Square::SetHeight(double h){Rectangle::SetHeight(h);Rectangle::SetWidth(h);}Now, when someone sets the width of a Squareobject, its height will change correspondingly. And when someone sets its height, the width will change with it. Thus, theinvariants of the Squareremain intact. The Squareobject will remain a mathematicallyproper square.Square s;s.SetWidth(1); // Fortunately sets the height to 1 too.s,SetHeight(2); // sets width and heigt to 2, good thing.But consider the following function:void f(Rectangle& r){r.SetWidth(32); // calls Rectangle::SetWidth}If we pass a reference to a Squareobject into this function, the Squareobject willbe corrupted because the height won’t be changed. This is a clear violation of LSP. The ffunction does not work for derivatives of its arguments. The reason for the failure is thatSetWidthand SetHeightwere not declared virtualin Rectangle.We can fix this easily. However, when the creation of a derived class causes us tomake changes to the base class, it often implies that the design is faulty. Indeed, it violatesthe Open-Closed principle. We might counter this with argument that forgetting to makeSetWidthand SetHeightvirtualwas the real design flaw, and we are just fixing itnow. However, this is hard to justify since setting the height and width of a rectangle areexceedingly primitive operations. By what reasoning would we make them virtualifwe did not anticipate the existence of Square.Still, let’s assume that we accept the argument, and fix the classes. We wind up withthe following code:class Rectangle{public:virtual void SetWidth(double w) {itsWidth=w;}virtual void SetHeight(double h) {itsHeight=h;}double GetHeight() const {return itsHeight;}double GetWidth() const {return itsWidth;}5 : The Liskov Substitution Principleprivate:double itsHeight;double itsWidth;};class Square : public Rectangle{public:virtual void SetWidth(double w);virtual void SetHeight(double h);};void Square::SetWidth(double w){Rectangle::SetWidth(w);Rectangle::SetHeight(w);}void Square::SetHeight(double h){Rectangle::SetHeight(h);Rectangle::SetWidth(h);}The Real ProblemAt this point in time we have two classes, Squareand Rectangle, that appear to work.No matter what you do to a Squareobject, it will remain consistent with a mathematicalsquare. And regardless of what you do to a Rectangleobject, it will remain a mathematical rectangle. Moreover, you can pass a Squareinto a function that accepts a pointeror reference to a Rectangle, and the Squarewill still act like a square and will remainconsistent. Thus, we might conclude that the model is now self consistent, and correct. However,this conclusion would be amiss. A model that is self consistent is not necessarily consistent with all its users! Consider function gbelow. void g(Rectangle& r){r.SetWidth(5);r.SetHeight(4);assert(r.GetWidth() * r.GetHeight()) == 20);}This function invokes the SetWidthand SetHeightmembers of what it believesto be a Rectangle. The function works just fine for a Rectangle, but declares anassertion error if passed a Square. So here is the real problem: Was the programmer whowrote that function justified in assuming that changing the width of a Rectangleleavesits height unchanged? Clearly, the programmer of gmade this very reasonable assumption. Passing aSquareto functions whose programmers made this assumption will result in problems.Therefore, there exist functions that take pointers or references to Rectangleobjects,6 The Liskov Substitution Principlebut cannot operate properly upon Squareobjects. These functions expose a violation ofthe LSP. The addition of
Being translated, please wait..
Results (Indonesian) 2:[Copy]
Copied!
1
The Liskov Pergantian Prinsip
ini adalah kedua saya Teknik Notebookcolumns untuk The C ++ Report. Artikel-artikel
yang akan muncul di kolom ini akan fokus pada penggunaan C ++ dan OOD, dan akan membahas
isu-isu rekayasa perangkat lunak. Saya akan berusaha untuk artikel
yang pragmatis dan
langsung berguna untuk insinyur perangkat lunak dalam
parit. Dalam artikel ini saya
akan memanfaatkan Booch ini
unifiednotation baru dan Rumbaugh ini (Version 0.8)
untuk mendokumentasikan berorientasi objek desain. Sidebar
menyediakan leksikon singkat
notasi ini.
Pendahuluan
My kolom terakhir (Jan 96) berbicara tentang prinsip Open-Closed. Prinsip ini adalah
dasar untuk kode yang dipertahankan dan dapat digunakan kembali membangun. Ini menyatakan bahwa dirancang dengan baik
kode dapat diperpanjang tanpa modifikasi; bahwa dalam sebuah program yang dirancang dengan baik fitur baru
ditambahkan dengan menambahkan kode baru, bukan dengan mengubah tua, sudah bekerja, kode.
Mekanisme utama di balik prinsip Open-Tertutup adalah abstraksi dan polimorfisme. Dalam bahasa statis diketik seperti C ++, salah satu mekanisme kunci yang mendukung abstraksi dan polimorfisme adalah warisan. Itu adalah dengan menggunakan warisan bahwa kita dapat
membuat kelas turunan yang sesuai dengan antarmuka polimorfik abstrak didefinisikan oleh murni
fungsi virtual di kelas dasar abstrak.
Apa aturan desain yang mengatur penggunaan tertentu dari warisan? Apa
karakteristik dari warisan hierarki terbaik? Apa perangkap yang akan menyebabkan kita
membuat hirarki yang tidak sesuai dengan prinsip Open-Tertutup? Ini adalah pertanyaan yang artikel ini akan membahas.
Base Class
Berasal 1 Berasal 2
Telah oleh
Referensi
Harus Dengan Nilai
Digunakan
Sidebar: Bersatu Notasi 0,8
2 The Liskov Pergantian Prinsip
The Liskov Pergantian di atas adalah parafrase dari Liskov Pergantian Prinsip (LSP). Barbara Liskov pertama menulis sebagai berikut hampir 8 tahun yang lalu 1: Yang ingin di sini adalah sesuatu seperti properti substitusi berikut: Jika untuk setiap objek o 1of tipe S ada o2 objek tipe T sehingga untuk semua program Pdefined dalam hal T, perilaku P tidak berubah ketika o1is diganti o2 maka S adalah subtipe T. Pentingnya prinsip ini menjadi jelas ketika Anda mempertimbangkan konsekuensi dari melanggar itu. Jika ada fungsi yang tidak sesuai dengan LSP, maka itu fungsi menggunakan pointer atau referensi untuk kelas dasar, tetapi harus tahu soal semua turunan dari kelas dasar. Fungsi seperti melanggar prinsip Open-Tertutup karena harus diubah setiap kali turunan baru dari kelas dasar dibuat. Sebuah Contoh sederhana dari Pelanggaran LSP Salah satu pelanggaran yang paling mencolok dari prinsip ini adalah penggunaan C ++ Run-Time Ketik Informasi (RTTI) untuk memilih fungsi berdasarkan jenis objek. yaitu: kekosongan DrawShape (const Shape & s) {if (typeid (s) == typeid (Lapangan)) DrawSquare (static_cast
























(s));
lain jika (typeid (s) == typeid (Circle))
DrawCircle (static_cast
(s));}
[Catatan: static_castis salah satu operator cor baru. Dalam contoh ini bekerja
persis seperti pemain tetap. yaitu DrawSquare ((Square &) s) ;. Namun sintaks baru memiliki aturan yang lebih ketat yang membuat lebih aman untuk digunakan, dan lebih mudah untuk menemukan dengan alat seperti
sebagai grep. Oleh karena itu lebih disukai.]
Jelas DrawShapefunction yang buruk terbentuk. Ini harus tahu tentang setiap kemungkinan
turunan dari Shapeclass, dan itu harus diubah setiap kali derivatif baru
Shapeare dibuat. Memang, banyak melihat struktur fungsi ini sebagai kutukan bagi
Object Oriented Desain.
1. Barbara Liskov, "Data Abstraksi dan Hirarki," Pemberitahuan SIGPLAN, 23,5 (Mei, 1988).
3: The Liskov Pergantian
Prinsip. Square dan Rectangle, Pelanggaran Lebih Halus
Namun, ada yang lain, jauh lebih halus, cara melanggar LSP. Mempertimbangkan
aplikasi yang menggunakan Rectangleclass seperti yang dijelaskan di bawah ini:
class Rectangle {public: kekosongan SetWidth (double w) {itsWidth = w;} kekosongan SetHeight (double h) {itsHeight = w;} ganda getHeight () const {kembali itsHeight;} ganda getWidth () const {kembali itsWidth;} private: ganda itsWidth; ganda itsHeight;}; Bayangkan bahwa aplikasi ini bekerja dengan baik, dan dipasang di banyak situs. Seperti halnya dengan semua perangkat lunak yang sukses, sebagai yang kebutuhan pengguna berubah, fungsi baru yang diperlukan. Bayangkan bahwa suatu hari pengguna menuntut kemampuan untuk memanipulasi kotak di samping empat persegi panjang. Hal ini sering mengatakan bahwa, dalam C ++, warisan adalah hubungan ISA. Dengan kata lain, jika jenis baru objek dapat dikatakan memenuhi hubungan ISA dengan jenis lama objek, maka kelas obyek baru harus berasal dari kelas yang lama objek. Jelas, persegi adalah persegi panjang untuk semua maksud normal dan tujuan. Sejak hubungan ISA memegang, adalah logis untuk model Squareclass sebagai yang berasal dari Rectangle. (Lihat Gambar 1.) ini menggunakan hubungan ISA dianggap oleh banyak untuk menjadi salah satu dasar teknik Object Oriented Analysis. Sebuah persegi adalah persegi panjang, sehingga Squareclass harus berasal dari Rectangleclass. Namun pemikiran seperti ini dapat menyebabkan beberapa masalah halus, namun signifikan,. Umumnya masalah ini tidak diramalkan sampai kita benar-benar mencoba untuk kode aplikasi. Petunjuk pertama kami mungkin fakta bahwa Squaredoes tidak perlu kedua itsHeightand variabel itsWidthmember. Namun akan mewarisi mereka pula. Jelas ini boros. Apalagi jika kita akan membuat ratusan ribu Squareobjects (misalnya CAD / Program CAE dimana setiap pin dari setiap komponen dari sirkuit kompleks diambil sebagai persegi), limbah ini bisa sangat signifikan. Namun, mari kita asumsikan bahwa kita tidak sangat peduli dengan efisiensi memori. Apakah ada masalah lain? Memang! Squarewill mewarisi SetWidthand SetHeight fungsi. Fungsi-fungsi ini benar-benar tidak pantas untuk Square, karena lebar dan tinggi persegi identik. ". Ini harus menjadi petunjuk penting bahwa ada masalah Rectangle Lapangan Gambar 1. 4 Liskov Pergantian Prinsip dengan desain. Namun, ada cara untuk menghindari masalah. Kita bisa menimpa SetWidthand SetHeightas berikut: kekosongan persegi :: SetWidth (double w) {Rectangle :: SetWidth (w); Rectangle :: SetHeight (w);} kekosongan Lapangan :: SetHeight (double h) {Rectangle :: SetHeight (h ); Rectangle :: SetWidth (h);} Sekarang, ketika seseorang menetapkan lebar Squareobject, tingginya akan berubah Sejalan. Dan ketika seseorang menetapkan ketinggian, lebar akan berubah dengan itu. Dengan demikian, invariants dari Squareremain utuh. The Squareobject akan tetap menjadi matematis persegi yang tepat. Lapangan s; s.SetWidth (1); // Untungnya menetapkan ketinggian 1 juga. S, SetHeight (2); // Set lebar dan heigt ke 2, hal yang baik. Tapi mempertimbangkan fungsi berikut: void f (Rectangle & r) {r.SetWidth (32); // Memanggil Rectangle :: SetWidth} Jika kita melewati referensi ke Squareobject ke fungsi ini, Squareobject akan rusak karena ketinggian tidak akan berubah. Ini adalah jelas melanggar LSP. F fungsi tidak bekerja untuk turunan dari argumen. Alasan kegagalan adalah bahwa SetWidthand SetHeightwere tidak dinyatakan virtualin Rectangle. Kita bisa memperbaiki ini dengan mudah. Namun, ketika penciptaan kelas turunan menyebabkan kita membuat perubahan pada kelas dasar, sering menyiratkan bahwa desain rusak. Memang, itu melanggar prinsip Open-Closed. Kita mungkin mengatasi ini dengan argumen bahwa lupa untuk membuat SetWidthand SetHeightvirtualwas cacat desain nyata, dan kami hanya memperbaiki itu sekarang. Namun, hal ini sulit untuk membenarkan sejak pengaturan tinggi dan lebar dari persegi panjang yang sangat primitif operasi. Dengan alasan apa yang akan kita membuat mereka virtualif kami tidak mengantisipasi adanya Square. Namun, mari kita asumsikan bahwa kita menerima argumen, dan memperbaiki kelas. Kami angin dengan kode berikut: class Rectangle {public: virtual void SetWidth (double w) {itsWidth = w;} virtual void SetHeight (double h) {itsHeight = h;} ganda getHeight () const {kembali itsHeight;} ganda getWidth () const {kembali itsWidth;} 5: The Liskov Pergantian Prinsip pribadi: ganda itsHeight; ganda itsWidth;}; class Square: Rectangle publik {public: virtual void SetWidth (double w); virtual void SetHeight (double h);} ; membatalkan Lapangan :: SetWidth (double w) {Rectangle :: SetWidth (w); Rectangle :: SetHeight (w);} kekosongan Lapangan :: SetHeight (double h) {Rectangle :: SetHeight (h); Rectangle :: SetWidth (h);} The Real Masalah. Pada titik ini dalam waktu yang kita miliki dua kelas, Squareand Rectangle, yang muncul untuk bekerja Tidak peduli apa yang Anda lakukan untuk Squareobject, itu akan tetap konsisten dengan matematika persegi. Dan terlepas dari apa yang Anda lakukan untuk Rectangleobject, itu akan tetap persegi panjang matematika. Selain itu, Anda dapat melewati Squareinto fungsi yang menerima pointer atau referensi ke Rectangle, dan Squarewill masih bertindak seperti persegi dan akan tetap konsisten. Dengan demikian, kita dapat menyimpulkan bahwa model sekarang diri yang konsisten, dan benar. Namun, kesimpulan ini akan menjadi salah. Sebuah model yang konsisten diri tidak selalu konsisten dengan semua penggunanya! Mempertimbangkan fungsi gbelow. Batal g (Rectangle & r) {r.SetWidth (5); r.SetHeight (4); menegaskan (r.GetWidth () * r.GetHeight ()) == 20);} Fungsi ini memanggil SetWidthand yang SetHeightmembers dari apa yang percaya menjadi Rectangle. Fungsi bekerja dengan baik untuk Rectangle, namun menyatakan suatu kesalahan pernyataan jika melewati Lapangan a. Jadi di sini adalah masalah nyata: Apakah programmer yang menulis bahwa fungsi dibenarkan dengan asumsi bahwa mengubah lebar Rectangleleaves tinggi tidak berubah? Jelas, programmer dari gmade asumsi yang sangat wajar ini. Melewati fungsi Squareto yang programmer membuat asumsi ini akan mengakibatkan masalah. Oleh karena itu, terdapat fungsi yang mengambil pointer atau referensi ke Rectangleobjects, 6 Liskov Pergantian Prinsip tetapi tidak dapat beroperasi dengan baik pada Squareobjects. Fungsi-fungsi ini mengekspos pelanggaran LSP. Penambahan
































































































































Being translated, please wait..
 
Other languages
The translation tool support: Afrikaans, Albanian, Amharic, Arabic, Armenian, Azerbaijani, Basque, Belarusian, Bengali, Bosnian, Bulgarian, Catalan, Cebuano, Chichewa, Chinese, Chinese Traditional, Corsican, Croatian, Czech, Danish, Detect language, Dutch, English, Esperanto, Estonian, Filipino, Finnish, French, Frisian, Galician, Georgian, German, Greek, Gujarati, Haitian Creole, Hausa, Hawaiian, Hebrew, Hindi, Hmong, Hungarian, Icelandic, Igbo, Indonesian, Irish, Italian, Japanese, Javanese, Kannada, Kazakh, Khmer, Kinyarwanda, Klingon, Korean, Kurdish (Kurmanji), Kyrgyz, Lao, Latin, Latvian, Lithuanian, Luxembourgish, Macedonian, Malagasy, Malay, Malayalam, Maltese, Maori, Marathi, Mongolian, Myanmar (Burmese), Nepali, Norwegian, Odia (Oriya), Pashto, Persian, Polish, Portuguese, Punjabi, Romanian, Russian, Samoan, Scots Gaelic, Serbian, Sesotho, Shona, Sindhi, Sinhala, Slovak, Slovenian, Somali, Spanish, Sundanese, Swahili, Swedish, Tajik, Tamil, Tatar, Telugu, Thai, Turkish, Turkmen, Ukrainian, Urdu, Uyghur, Uzbek, Vietnamese, Welsh, Xhosa, Yiddish, Yoruba, Zulu, Language translation.

Copyright ©2025 I Love Translation. All reserved.

E-mail: