ලිස්කොව් ආදේශන මූලධර්මය (එල්එස්පී) වස්තු නැඹුරු නිර්මාණයේ මූලික මූලධර්මයක් බව මම අසා ඇත්තෙමි. එය කුමක්ද සහ එහි භාවිතය පිළිබඳ උදාහරණ මොනවාද?
ලිස්කොව් ආදේශන මූලධර්මය (එල්එස්පී) වස්තු නැඹුරු නිර්මාණයේ මූලික මූලධර්මයක් බව මම අසා ඇත්තෙමි. එය කුමක්ද සහ එහි භාවිතය පිළිබඳ උදාහරණ මොනවාද?
Answers:
එල්එස්පී (බොබ් මාමා විසින් මෑතකදී මා අසා ඇති පොඩ්කාස්ට් එකකින් ලබා දුන්) නිදර්ශනය කරන විශිෂ්ට උදාහරණයක් නම්, සමහර විට ස්වාභාවික භාෂාවෙන් නිවැරදි යැයි පෙනෙන දෙයක් කේතයෙන් ක්රියා නොකරන්නේ කෙසේද යන්නයි.
ගණිතයේ a Square
යනු a Rectangle
. ඇත්ත වශයෙන්ම එය සෘජුකෝණාස්රයේ විශේෂීකරණයකි. "යනු" ඔබට මෙය උරුමය සමඟ ආදර්ශනය කිරීමට අවශ්ය කරයි. කෙසේ වෙතත් ඔබ සෑදූ කේතයෙන් Square
ව්යුත්පන්න වී ඇත්නම් Rectangle
, Square
ඔබ අපේක්ෂා කරන ඕනෑම තැනක භාවිතා කළ හැකි විය යුතුය Rectangle
. මෙය යම් අමුතු හැසිරීමක් ඇති කරයි.
ඔබේ මූලික පන්තියේ ඔබට ඇති ක්රම SetWidth
සහ SetHeight
ක්රම ගැන සිතන්න Rectangle
; මෙය සම්පූර්ණයෙන්ම තර්කානුකූල බව පෙනේ. ඔබගේ නම් කෙසේ වෙතත් Rectangle
අදාල නම් පෙන්වා Square
, පසුව SetWidth
හා SetHeight
හැඟීමක් එක් එය ගැලපෙන අනෙක් වෙනස් කිරීමට ඉඩ ඇත සැකසීම නිසා කරන්නේ නැහැ. මෙම අවස්ථාවේ Square
දී ලිස්කොව් ආදේශන පරීක්ෂණය අසමත් Rectangle
වන අතර එයින් Square
උරුමයක් ලබා Rectangle
ගැනීම වියුක්ත කිරීම නරක දෙයකි.
අනෙක් මිල කළ නොහැකි SOLID මූලධර්ම අභිප්රේරණ පෝස්ටර් පරීක්ෂා කළ යුතුය .
Square.setWidth(int width)
මේ වගේ ක්රියාත්මක කරන ලදී: this.width = width; this.height = width;
? මෙම අවස්ථාවේ දී පළල උස සමාන බව සහතික කෙරේ.
ලිස්කොව් ආදේශන මූලධර්මය (එල්එස්පී, lsp) යනු වස්තු දිශානතිගත ක්රමලේඛනයේ සංකල්පයකි:
පාදක පංති වෙත යොමු කිරීම් හෝ යොමු කිරීම් භාවිතා කරන කාර්යයන් නොදැන ව්යුත්පන්න පංතිවල වස්තු භාවිතා කළ යුතුය.
එල්එස්පී එහි හදවතේ අතුරුමුහුණත් සහ කොන්ත්රාත්තු මෙන්ම පන්තියක් එදිරිව දීර් extend කළ යුත්තේ කවදාද යන්න තීරණය කරන්නේ කෙසේද යන්න පිළිබඳව ඔබේ ඉලක්කය සපුරා ගැනීම සඳහා සංයුතිය වැනි තවත් උපාය මාර්ගයක් භාවිතා කරන්න.
මෙම කරුණ නිදර්ශනය කිරීම සඳහා මා දුටු වඩාත් way ලදායී ක්රමය වූයේ හෙඩ් ෆස්ට් ඕඕඒ සහ ඩී . උපාය මාර්ගික ක්රීඩා සඳහා රාමුවක් ගොඩනැගීම සඳහා ඔබ ව්යාපෘතියක සංවර්ධකයෙකු වන අවස්ථාවක් ඔවුන් ඉදිරිපත් කරයි.
ඔවුන් මේ ආකාරයට පෙනෙන පුවරුවක් නියෝජනය කරන පන්තියක් ඉදිරිපත් කරයි:
ද්විමාන අරාවෙහි උළු පිහිටීම සොයා ගැනීම සඳහා සියලු ක්රම X සහ Y ඛණ්ඩාංක පරාමිතීන් ලෙස ගනී Tiles
. මෙමඟින් ක්රීඩා සංවර්ධකයෙකුට ක්රීඩාව අතරතුර පුවරුවේ ඒකක කළමනාකරණය කිරීමට ඉඩ දෙනු ඇත.
පියාසර කරන ක්රීඩා සඳහා ඉඩ සැලසීමට ක්රීඩා රාමු වැඩ ත්රිමාණ ක්රීඩා පුවරුවලට සහාය විය යුතු බව පැවසීමට අවශ්යතාවයන් පොත වෙනස් කරයි. එබැවින් ThreeDBoard
පන්තියක් හඳුන්වා දෙනු Board
ලැබේ.
බැලූ බැල්මට මෙය හොඳ තීරණයක් සේ පෙනේ. සහ ගුණාංග Board
දෙකම සපයන අතර Z අක්ෂය සපයයි.Height
Width
ThreeDBoard
එය බිඳ වැටෙන ස්ථානය වන්නේ ඔබ උරුම කර ගත් අනෙක් සියලුම සාමාජිකයන් දෙස බැලීමයි Board
. සඳහා ක්රම AddUnit
, GetTile
, GetUnits
, එසේ සියලු තුළ X හා Y පරාමිතීන් දෙක ගත Board
පන්ති නමුත් ThreeDBoard
ලෙස හොඳින් Z පරාමිතිය අවශ්ය වේ.
එබැවින් ඔබ නැවත Z පරාමිතියක් සමඟ එම ක්රම ක්රියාත්මක කළ යුතුය. ඉසෙඩ් පරාමිතියට Board
පන්තියට සන්දර්භයක් නොමැති අතර පන්තියෙන් උරුම වූ ක්රමවල Board
අර්ථය නැති වී යයි. කේත ඒකකයක් ThreeDBoard
පන්තිය එහි මූලික පන්තිය ලෙස භාවිතා කිරීමට උත්සාහ Board
කිරීම වාසනාවකි.
සමහර විට අපි වෙනත් ප්රවේශයක් සොයා ගත යුතුයි. දීර්ඝ වෙනුවට Board
, ThreeDBoard
සමන්විත විය යුතුය Board
වස්තූන්. Board
ඉසෙඩ් අක්ෂයේ ඒකකයකට එක් වස්තුවක්.
සංවෘත කිරීම සහ නැවත භාවිතා කිරීම වැනි හොඳ වස්තු නැඹුරු මූලධර්ම භාවිතා කිරීමට මෙය අපට ඉඩ සලසයි.
ආදේශකතාව යනු වස්තු-නැඹුරු වැඩසටහන්කරණයේ මූලධර්මයක් වන අතර, පරිගණක වැඩසටහනක S යනු T හි උප ප්රභේදයක් නම්, T වර්ගයේ වස්තු S වර්ගයේ වස්තූන් සමඟ ප්රතිස්ථාපනය කළ හැකිය.
ජාවා හි සරල උදාහරණයක් කරමු:
public class Bird{
public void fly(){}
}
public class Duck extends Bird{}
කුරුල්ලෙකු නිසා තාරාවාට පියාසර කළ හැකිය, නමුත් මේ ගැන කුමක් කිව හැකිද:
public class Ostrich extends Bird{}
පැස්බරා කුරුල්ලෙකි, නමුත් එයට පියාසර කළ නොහැක, පැස්බරා පන්තිය කුරුල්ලන්ගේ උප ප්රභේදයකි, නමුත් එයට පියාසර ක්රමය භාවිතා කළ නොහැක, එයින් අදහස් වන්නේ අප එල්එස්පී මූලධර්මය කඩ කරන බවයි.
public class Bird{
}
public class FlyingBirds extends Bird{
public void fly(){}
}
public class Duck extends FlyingBirds{}
public class Ostrich extends Bird{}
Bird bird
. මැස්සන් භාවිතා කිරීම සඳහා ඔබ වස්තුව ෆ්ලයිංබර්ඩ්ස් වෙත දැමිය යුතුය, එය හරිද?
Bird bird
, එයින් අදහස් වන්නේ එය භාවිතා කළ නොහැකි fly()
බවයි. ඒක තමයි. සම්මත Duck
වීමෙන් මෙම කාරණය වෙනස් නොවේ. සේවාදායකයාට තිබේ FlyingBirds bird
නම්, එය සමත් වුව ද එය Duck
සෑම විටම එකම ආකාරයකින් ක්රියා කළ යුතුය.
එල්එස්පී ආක්රමණ ගැන සැලකිලිමත් වේ.
සම්භාව්ය උදාහරණය පහත දැක්වෙන ව්යාජ කේත ප්රකාශනය මගින් ලබා දී ඇත (ක්රියාත්මක කිරීම් මඟ හැරී ඇත):
class Rectangle {
int getHeight()
void setHeight(int value)
int getWidth()
void setWidth(int value)
}
class Square : Rectangle { }
අතුරුමුහුණත ගැලපුණද දැන් අපට ගැටලුවක් තිබේ. හේතුව, අපි චතුරස්රයන් සහ සෘජුකෝණාස්රා වල ගණිතමය අර්ථ දැක්වීමෙන් පැනනැඟී ඇති වෙනස්වීම් උල්ලං have නය කර තිබීමයි. ලබා ගන්නන් සහ සැකසීම් ක්රියා කරන ආකාරය, Rectangle
පහත සඳහන් වෙනස්වීම් සපුරාලිය යුතුය:
void invariant(Rectangle r) {
r.setHeight(200)
r.setWidth(100)
assert(r.getHeight() == 200 and r.getWidth() == 100)
}
කෙසේ වෙතත්, මෙම වෙනස් කිරීම නිවැරදිව ක්රියාත්මක කිරීම මගින් උල්ලං must නය කළ යුතුය Square
, එබැවින් එය වලංගු ආදේශකයක් නොවේ Rectangle
.
රොබට් මාටින් ලිස්කොව් ආදේශන මූලධර්මය පිළිබඳ විශිෂ්ට ලිපියක් ඇත . මූලධර්මය උල්ලං may නය විය හැකි සියුම් හා එතරම් සියුම් නොවන ආකාරයන් මෙහි සාකච්ඡා කරයි.
පුවත්පතේ අදාළ සමහර කොටස් (දෙවන උදාහරණය දැඩි ලෙස ensed නීභවනය වී ඇති බව සලකන්න):
එල්එස්පී උල්ලං lation නය කිරීම පිළිබඳ සරල උදාහරණයක්
මෙම මූලධර්මයේ වඩාත්ම පැහැදිලිව උල්ලං lations නය කිරීම්වලින් එකක් වන්නේ වස්තුවක වර්ගය මත පදනම්ව ශ්රිතයක් තෝරා ගැනීම සඳහා C ++ ධාවන කාල තොරතුරු තොරතුරු (RTTI) භාවිතා කිරීමයි. එනම්:
void DrawShape(const Shape& s) { if (typeid(s) == typeid(Square)) DrawSquare(static_cast<Square&>(s)); else if (typeid(s) == typeid(Circle)) DrawCircle(static_cast<Circle&>(s)); }
පැහැදිලිවම
DrawShape
කාර්යය නරක ලෙස පිහිටුවා ඇත.Shape
පංතියේ විය හැකි සෑම ව්යුත්පන්නයක් ගැනම එය දැන සිටිය යුතු අතර, නව ව්යුත්පන්නයන්Shape
නිර්මාණය කරන සෑම විටම එය වෙනස් කළ යුතුය . ඇත්ත වශයෙන්ම, බොහෝ දෙනෙක් මෙම ශ්රිතයේ ව්යුහය වස්තු දිශානත නිර්මාණයට පිළිකුලක් ලෙස සලකති.චතුරස්රය සහ සෘජුකෝණාස්රය, වඩාත් සියුම් උල්ලං .නය කිරීමකි.
කෙසේ වෙතත්, එල්එස්පී උල්ලං ting නය කිරීමේ වෙනත්, ඊටත් වඩා සියුම් ක්රම තිබේ.
Rectangle
පහත විස්තර කර ඇති පරිදි පන්තිය භාවිතා කරන යෙදුමක් සලකා බලන්න :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; };
[...] එක් දිනක් පරිශීලකයින් සෘජුකෝණාස්රා වලට අමතරව චතුරස්රයන් හැසිරවීමේ හැකියාව ඉල්ලා සිටින බව සිතන්න. [...]
පැහැදිලිවම, චතුරස්රයක් යනු සියලු සාමාන්ය අභිප්රායන් සහ අරමුණු සඳහා සෘජුකෝණාස්රයකි. අයිඑස්ඒ සම්බන්ධතාවය පවතින බැවින්,
Square
පන්තිය ව්යුත්පන්න වූවක් ලෙස ආදර්ශනය කිරීම තර්කානුකූල යRectangle
. [...]
Square
SetWidth
සහSetHeight
කාර්යයන් උරුම කර දෙනු ඇත .Square
වර්ගයක පළල සහ උස සමාන බැවින් මෙම කාර්යයන් a සඳහා සම්පූර්ණයෙන්ම නුසුදුසු ය . සැලසුමේ ගැටලුවක් ඇති බවට මෙය සැලකිය යුතු හෝඩුවාවක් විය යුතුය. කෙසේ වෙතත්, ගැටළුව මඟ හැරීමට ක්රමයක් තිබේ. අපට අභිබවා යා හැකිSetWidth
අතරSetHeight
[...]නමුත් පහත සඳහන් කාර්යය සලකා බලන්න:
void f(Rectangle& r) { r.SetWidth(32); // calls Rectangle::SetWidth }
Square
මෙම ශ්රිතයට අප යම් වස්තුවක් වෙත යොමු කිරීමක් කළහොත්Square
, උස වෙනස් නොවන නිසා වස්තුව දූෂිත වේ. මෙය පැහැදිලිවම එල්එස්පී උල්ලං is නය කිරීමකි. ශ්රිතය එහි තර්කවල ව්යුත්පන්නයන් සඳහා ක්රියා නොකරයි.[...]
Now the rule for the preconditions and postconditions for derivatives, as stated by Meyer is: ...when redefining a routine [in a derivative], you may only replace its precondition by a weaker one, and its postcondition by a stronger one.
ළමා පන්තියේ පූර්ව කොන්දේසිය දෙමාපිය පන්තියේ පූර්ව කොන්දේසියට වඩා ශක්තිමත් නම්, ඔබට පූර්ව කොන්දේසිය උල්ලං without නය නොකර දෙමව්පියන්ට දරුවෙකු ආදේශ කළ නොහැක. එබැවින් එල්එස්පී.
සමහර කේතයන් එය වර්ගයක ක්රමවේදයන් යැයි සිතන තැන LSP අවශ්ය වන අතර T
, නොදැනුවත්වම වර්ගයක ක්රමවේදයන් හැඳින්විය හැකිය S
, එහිදී S extends T
(එනම්, S
උරුම, ව්යුත්පන්න හෝ සුපිරි ප්රභේදයේ උප ප්රභේදයකි T
).
නිදසුනක් ලෙස, මෙය සිදුවන්නේ වර්ගයේ ආදාන පරාමිතියක් සහිත ශ්රිතයක් T
(එනම් ආයාචනා කරන ලද) වර්ගයේ තර්ක අගය සමඟිනි S
. නැතහොත්, වර්ගයේ හඳුනාගැනීමක් සඳහා, වර්ගයක T
අගයක් පවරනු ලැබේ S
.
val id : T = new S() // id thinks it's a T, but is a S
LSP සඳහා වර්ග T
(උදා Rectangle
) ක්රම සඳහා අපේක්ෂාවන් (එනම් වෙනස්වීම්) අවශ්ය වේ, ඒ වෙනුවට S
(උදා Square
) වර්ගයේ ක්රම කැඳවූ විට උල්ලං not නය නොවේ .
val rect : Rectangle = new Square(5) // thinks it's a Rectangle, but is a Square
val rect2 : Rectangle = rect.setWidth(10) // height is 10, LSP violation
වෙනස් කළ නොහැකි ක්ෂේත්ර සහිත වර්ගයක පවා තවමත් වෙනස්වීම් ඇත, උදා: වෙනස් කළ නොහැකි සෘජුකෝණාස්රාකාර සැකසුම්කරුවන් විසින් මානයන් ස්වාධීනව වෙනස් කරනු ඇතැයි අපේක්ෂා කරයි, නමුත් වෙනස් කළ නොහැකි චතුරස්ර සැකසුම්කරුවන් මෙම අපේක්ෂාව උල්ලං late නය කරයි.
class Rectangle( val width : Int, val height : Int )
{
def setWidth( w : Int ) = new Rectangle(w, height)
def setHeight( h : Int ) = new Rectangle(width, h)
}
class Square( val side : Int ) extends Rectangle(side, side)
{
override def setWidth( s : Int ) = new Square(s)
override def setHeight( s : Int ) = new Square(s)
}
එල්එස්පීයට අවශ්ය වන්නේ උප S
ප්රභේදයේ එක් එක් ක්රමයට ප්රතිවිරෝධී ආදාන පරාමිතීන් සහ සහසංයුජ ප්රතිදානය තිබිය යුතුය.
පරස්පර විරෝධී යන්නෙන් අදහස් වන්නේ විචල්යතාවය උරුමයේ දිශාවට පටහැනි බවයි, එනම් Si
උප ප්රභේදයේ එක් එක් ක්රමයේ එක් එක් ආදාන පරාමිතියේ වර්ගය S
, සුපර් ටයිප් Ti
හි අනුරූප ක්රමයේ අනුරූප ආදාන පරාමිතියේ වර්ගයට සමාන හෝ සුපිරි ආකාරයක් විය යුතුය. T
.
කෝවරියන්ස් යනු විචල්යතාවය උරුමයේ එකම දිශාවට ය, එනම් So
උප ප්රභේදයේ එක් එක් ක්රමයේ ප්රතිදානයේ වර්ගය S
, සුපර් ටයිප් To
හි අනුරූප ක්රමයේ අනුරූප ප්රතිදානයේ වර්ගයට සමාන හෝ උප වර්ගයක් විය යුතුය T
.
මෙයට හේතුව ඇමතුම්කරුට එය වර්ගයක් ඇතැයි T
සිතන්නේ නම්, එය ක්රමවේදයක් යැයි සිතන්නේ නම්, එය වර්ගයේ T
තර්ක (ය) සපයන Ti
අතර ප්රතිදානය වර්ගයට පවරයි To
. එය සැබවින්ම අනුරූප ක්රමයට කතා කරන විට S
, එක් එක් Ti
ආදාන පරාමිතිය ආදාන පරාමිතියකට පවරා ඇති Si
අතර So
ප්රතිදානය වර්ගයට පවරනු ලැබේ To
. මේ අනුව නම් Si
නොවේ contravariant wrt විය Ti
, පසුව subtype Xi
කලහ ක subtype වන්නේ නැහැ Si
පවරා -could වේවා Ti
.
මීට අමතරව, වර්ග බහුඅවයවික පරාමිතීන් (එනම් ජනක විද්යාව) පිළිබඳ අර්ථ දැක්වීම්-අඩවි විචල්යතා විවරණ ඇති භාෂා සඳහා (උදා: ස්කලා හෝ ලංකාව), එක් එක් වර්ගයේ පරාමිතීන් සඳහා විචල්ය විවරණයේ සම හෝ ප්රතිවිරුද්ධ දිශාව ප්රතිවිරුද්ධ හෝ එකම දිශාවකින් T
විය යුතුය. පිළිවෙලින් වර්ගය පරාමිතිය ඇති සෑම ආදාන පරාමිතියකට හෝ ප්රතිදානයකට (සෑම ක්රමයකම ).T
මීට අමතරව, ශ්රිත වර්ගයක් ඇති එක් එක් ආදාන පරාමිතිය හෝ ප්රතිදානය සඳහා, අවශ්ය විචල්ය දිශාව ආපසු හරවනු ලැබේ. මෙම නියමය පුනරාවර්තනය වේ.
ආක්රමණ ගණනය කළ හැකි ස්ථානවල උප වර්ග කිරීම සුදුසුය .
ආක්රමණ ආදර්ශනය කරන්නේ කෙසේද යන්න පිළිබඳව බොහෝ පර්යේෂණ සිදු වෙමින් පවතින අතර එමඟින් ඒවා සම්පාදකයා විසින් බලාත්මක කරනු ලැබේ.
ටයිප්ස්ටේට් (3 වන පිටුව බලන්න) යතුරු ලියනය කිරීම සඳහා විකලාංග රාජ්ය ආක්රමණ ප්රකාශයට පත් කරයි. විකල්පයක් ලෙස, ප්රකාශයන් වර්ග බවට පරිවර්තනය කිරීමෙන් ආක්රමණ ක්රියාත්මක කළ හැකිය . උදාහරණයක් ලෙස, ගොනුවක් වැසීමට පෙර එය විවෘතව ඇති බව තහවුරු කර ගැනීම සඳහා, File.open () හට OpenFile වර්ගයක් ආපසු ලබා දිය හැකිය, එය ගොනුවේ නොමැති සමීප () ක්රමයක් අඩංගු වේ. සංයුක්ත වේලාවේදී ආක්රමණ ක්රියාත්මක කිරීමට ටයිප් කිරීම සඳහා ටික්-ටැක්-ටෝ ඒපීඅයි තවත් උදාහරණයක් විය හැකිය. වර්ගය පද්ධතිය ටියුරින්-සම්පුර්ණ විය හැකිය, උදා: පරිමාණය . යැපෙන ලෙස ටයිප් කළ භාෂා සහ ප්රමේය ප්රොවර්ස් ඉහළ පෙළේ ටයිප් කිරීමේ ආකෘති විධිමත් කරයි.
විස්තාරණයට වඩා අර්ථ නිරූපණයන්හි අවශ්යතාවය නිසා, ආකෘති ආක්රමණ සඳහා යතුරු ලියනය කිරීම, එනම් ඒකාබද්ධ ඉහළ පෙළේ අර්ථ දැක්වීමේ අර්ථ නිරූපණය ටයිප්ස්ටේටයට වඩා උසස් යැයි මම අපේක්ෂා කරමි. 'ව්යාප්තිය' යනු සම්බන්ධීකරනය නොකළ, මොඩියුලර් සංවර්ධනයේ අසීමිත, අවසර ලත් සංයුතියයි. විස්තාරණ සංයුතිය සඳහා එකිනෙකා සමඟ ඒකාබද්ධ කළ නොහැකි, හවුල් අර්ථකථන ප්රකාශ කිරීම සඳහා අන්යෝන්ය වශයෙන් රඳා පවතින ආකෘති දෙකක් (උදා: වර්ග සහ ටයිප්ස්ටේට්) තිබීම, ඒකීයභාවයේ ප්රතිවිරෝධය ලෙස මට පෙනේ. . නිදසුනක් ලෙස, ප්රකාශන ගැටළුව- සමාන දිගුව උප වර්ගය, ක්රියාකාරී අධි බර පැටවීම සහ පරාමිතික ටයිප් කිරීමේ වසම් තුළ ඒකාබද්ධ විය.
මගේ න්යායාත්මක ස්ථාවරය නම් දැනුම පැවතීම සඳහා (“මධ්යගත කිරීම අන්ධ හා නුසුදුසු” යන කොටස බලන්න), ටියුරින්-සම්පූර්ණ පරිගණක භාෂාවක් තුළ ඇති විය හැකි සියලු වෙනස්වීම් 100% ක් ආවරණය කළ හැකි පොදු ආකෘතියක් කිසි විටෙකත් නොතිබෙනු ඇත . දැනුම පැවතීමට නම්, අනපේක්ෂිත හැකියාවන් බොහෝමයක් පවතී, එනම් ආබාධය සහ එන්ට්රොපිය සෑම විටම වැඩි විය යුතුය. මෙය එන්ට්රොපික් බලයයි. විභව දිගුවක ඇති විය හැකි සියලුම ගණනය කිරීම් සනාථ කිරීම සඳහා, හැකි සෑම දිගුවක්ම ප්රාථමික ගණනය කිරීමකි.
හැල්ටිං ප්රමේය පවතින්නේ මේ නිසාය, එනම් ටියුරින්-සම්පූර්ණ ක්රමලේඛන භාෂාවක හැකි සෑම වැඩසටහනක්ම අවසන් වේද යන්න තීරණය කළ නොහැක. සමහර විශේෂිත වැඩසටහන් අවසන් වන බව සනාථ කළ හැකිය (සියලු හැකියාවන් නිර්වචනය කර ගණනය කර ඇති එකක්). නමුත් එම වැඩසටහන දීර් extension කිරීමේ හැකියාවන් සම්පූර්ණ නොවේ නම් (උදා: යැපුම්-ටයිප් කිරීම හරහා) එම වැඩසටහනේ දීර් extension කිරීමේ හැකියාව අවසන් වන බව ඔප්පු කළ නොහැක. ටියුරින්-සම්පූර්ණත්වය සඳහා වන මූලික අවශ්යතාව අසීමිත පුනරාවර්තනයක් බැවින් , ගොඩෙල්ගේ අසම්පූර්ණ ප්රමේයයන් සහ රසල්ගේ විරුද්ධාභාසය දීර්. කිරීම සඳහා අදාළ වන්නේ කෙසේද යන්න තේරුම් ගැනීම බුද්ධිමත් ය.
මෙම ප්රමේයයන්ගේ අර්ථ නිරූපණය එන්ට්රොපික් බලය පිළිබඳ සාමාන්යකරණය වූ සංකල්පීය අවබෝධයකට ඒවා ඇතුළත් කරයි:
සෑම පිළිතුරකම සෘජුකෝණාස්රා සහ චතුරස්රයන් සහ එල්එස්පී උල්ලං how නය කරන්නේ කෙසේදැයි මම දකිමි.
එල්එස්පී සැබෑ ලෝක උදාහරණයකට අනුකූල වන්නේ කෙසේදැයි පෙන්වීමට මම කැමතියි:
<?php
interface Database
{
public function selectQuery(string $sql): array;
}
class SQLiteDatabase implements Database
{
public function selectQuery(string $sql): array
{
// sqlite specific code
return $result;
}
}
class MySQLDatabase implements Database
{
public function selectQuery(string $sql): array
{
// mysql specific code
return $result;
}
}
මෙම සැලසුම එල්එස්පීයට අනුකූල වන්නේ අප භාවිතා කිරීමට තෝරා ගැනීම ක්රියාත්මක කිරීම නොසලකා හැසිරීම නොවෙනස්ව පවතින බැවිනි.
ඔව්, ඔබට මෙම වින්යාසය තුළ එල්එස්පී උල්ලං can නය කළ හැකිය.
<?php
interface Database
{
public function selectQuery(string $sql): array;
}
class SQLiteDatabase implements Database
{
public function selectQuery(string $sql): array
{
// sqlite specific code
return $result;
}
}
class MySQLDatabase implements Database
{
public function selectQuery(string $sql): array
{
// mysql specific code
return ['result' => $result]; // This violates LSP !
}
}
දැන් උප ප්රභේද එකම ආකාරයකින් භාවිතා කළ නොහැක, මන්ද ඒවා තවදුරටත් එකම ප්රති result ලය ලබා නොදේ.
Database::selectQuery
මඟින් සහය දක්වන SQL හි උප කුලකයට සහය දැක්වීමේ අර්ථ නිරූපණය අප විසින් සීමා කරන තාක් කල් උදාහරණය එල්එස්පී උල්ලං does නය නොකරයි . එය කිසිසේත්ම ප්රායෝගික නොවේ ... එයින් කියැවෙන්නේ මෙහි භාවිතා කර ඇති අනෙක් ඒවාට වඩා උදාහරණය තවමත් ග්රහණය කර ගැනීම පහසු බවය.
ඔබ ලිස්කොව් උල්ලං are නය කරනවාද නැද්ද යන්න තීරණය කිරීම සඳහා චෙක් ලැයිස්තුවක් තිබේ.
පිරික්සුම් ලැයිස්තුව:
ඉතිහාසය අවහිර කිරීම : ක්රමවේදයක් ඉක්මවා යන විට මූලික පන්තියේ වෙනස් කළ නොහැකි දේපලක් වෙනස් කිරීමට ඔබට අවසර නැත. මෙම කේතය දෙස බලන්න, නම වෙනස් කළ නොහැකි (පුද්ගලික කට්ටලයක්) ලෙස අර්ථ දක්වා ඇති නමුත් උප ටයිප් නව ක්රමයක් හඳුන්වා දෙන අතර එය වෙනස් කිරීමට ඉඩ සලසයි (පරාවර්තනය හරහා):
public class SuperType
{
public string Name { get; private set; }
public SuperType(string name, int age)
{
Name = name;
Age = age;
}
}
public class SubType : SuperType
{
public void ChangeName(string newName)
{
var propertyType = base.GetType().GetProperty("Name").SetValue(this, newName);
}
}
: 2 අන් අය භාණ්ඩ ඇත ක්රමය තර්ක Contravariance හා නැවත වර්ග Covariance . නමුත් එය C # හි කළ නොහැකි ය (මම C # සංවර්ධකයෙක්) එබැවින් මම ඔවුන් ගැන තැකීමක් නොකරමි.
යොමුව:
එල්එස්පී යනු වගන්තිවල කොන්ත්රාත්තුව පිළිබඳ රීතියකි: මූලික පංතියක් කොන්ත්රාත්තුවක් තෘප්තිමත් කරන්නේ නම්, එල්එස්පී ව්යුත්පන්න පංති විසින් ද එම කොන්ත්රාත්තුව සපුරාලිය යුතුය.
ව්යාජ පයිතන්හි
class Base:
def Foo(self, arg):
# *... do stuff*
class Derived(Base):
def Foo(self, arg):
# *... do stuff*
ව්යුත්පන්න වස්තුවක් මත ඔබ Foo අමතන සෑම අවස්ථාවකම එය LSP තෘප්තිමත් කරයි, එය Arg සමාන වන තාක් දුරට මූලික වස්තුවක් මත Foo ඇමතීමට සමාන ප්රති results ල ලබා දෙයි.
2 + "2"
). සමහර විට ඔබ "තදින් ටයිප් කළ" "සංඛ්යානමය වශයෙන් ටයිප් කළ" ලෙස පටලවා ගන්නවාද?
දීර් story කතන්දර කෙටියි, සෘජුකෝණාස්රාකාර හතරැස් සහ චතුරස්ර චතුරස්රයන් තබමු, මව් පංතියක් දීර් ing කිරීමේදී ප්රායෝගික උදාහරණය, ඔබට නිශ්චිත මාපිය API පෙරදසුන් කිරීම හෝ එය දිගු කිරීම කළ යුතුය.
අපි ඔබට මූලික අයිතම ගබඩාවක් ඇති බව කියමු.
class ItemsRepository
{
/**
* @return int Returns number of deleted rows
*/
public function delete()
{
// perform a delete query
$numberOfDeletedRows = 10;
return $numberOfDeletedRows;
}
}
එය දීර් ing කරන උප පංතියක්:
class BadlyExtendedItemsRepository extends ItemsRepository
{
/**
* @return void Was suppose to return an INT like parent, but did not, breaks LSP
*/
public function delete()
{
// perform a delete query
$numberOfDeletedRows = 10;
// we broke the behaviour of the parent class
return;
}
}
එවිට ඔබට සේවාදායකයෙකුට මූලික අයිතම රිපෝසිටරි ඒපීඅයි සමඟ වැඩ කර එය මත විශ්වාසය තැබිය හැකිය.
/**
* Class ItemsService is a client for public ItemsRepository "API" (the public delete method).
*
* Technically, I am able to pass into a constructor a sub-class of the ItemsRepository
* but if the sub-class won't abide the base class API, the client will get broken.
*/
class ItemsService
{
/**
* @var ItemsRepository
*/
private $itemsRepository;
/**
* @param ItemsRepository $itemsRepository
*/
public function __construct(ItemsRepository $itemsRepository)
{
$this->itemsRepository = $itemsRepository;
}
/**
* !!! Notice how this is suppose to return an int. My clients expect it based on the
* ItemsRepository API in the constructor !!!
*
* @return int
*/
public function delete()
{
return $this->itemsRepository->delete();
}
}
මෙම LSP විට කැඩී ඇත ආදේශ මව් සමඟ පන්ති උප පන්තිය වැලහින්නිය API කොන්ත්රාත්තුව .
class ItemsController
{
/**
* Valid delete action when using the base class.
*/
public function validDeleteAction()
{
$itemsService = new ItemsService(new ItemsRepository());
$numberOfDeletedItems = $itemsService->delete();
// $numberOfDeletedItems is an INT :)
}
/**
* Invalid delete action when using a subclass.
*/
public function brokenDeleteAction()
{
$itemsService = new ItemsService(new BadlyExtendedItemsRepository());
$numberOfDeletedItems = $itemsService->delete();
// $numberOfDeletedItems is a NULL :(
}
}
නඩත්තු කළ හැකි මෘදුකාංග ලිවීම පිළිබඳ මගේ පා course මාලාවෙන් ඔබට වැඩිදුර ඉගෙන ගත හැකිය: https://www.udemy.com/enterprise-php/
එල්එස්පී තාක්ෂණිකව ඇති දේ සෑම කෙනෙකුම ආවරණය කර ඇති බව මම අනුමාන කරමි: ඔබට මූලික වශයෙන් අවශ්ය වන්නේ උප වර්ග විස්තර වලින් වියුක්ත වී සුපිරි ටයිප් ආරක්ෂිතව භාවිතා කිරීමට ය.
එබැවින් ලිස්කොව්ට යටින් පවතින නීති 3 ක් ඇත:
අත්සන රීතිය: උප ප්රභේදයේ සුපිරි මාදිලියේ සෑම ක්රියාවක්ම සින්ටැක්ටිකල් ලෙස වලංගු ලෙස ක්රියාත්මක කළ යුතුය. සම්පාදකයෙකුට ඔබ වෙනුවෙන් පරීක්ෂා කිරීමට හැකි වනු ඇත. අඩු ව්යතිරේකයන් විසි කිරීම සහ අවම වශයෙන් සුපිරි ටයිප් ක්රමවලට ප්රවේශ විය හැකි වීම පිළිබඳ කුඩා රීතියක් තිබේ.
ක්රම රීතිය: එම මෙහෙයුම් ක්රියාත්මක කිරීම අර්ථාන්විත ය.
ගුණාංග රීතිය: මෙය තනි ක්රියාකාරී ඇමතුම් ඉක්මවා යයි.
මෙම සියලු ගුණාංග සංරක්ෂණය කළ යුතු අතර අතිරේක උප ප්රභේදයේ ක්රියාකාරිත්වය සුපිරි ටයිප් ගුණාංග උල්ලං not නය නොකළ යුතුය.
මෙම කරුණු තුන ගැන සැලකිලිමත් වන්නේ නම්, ඔබ යටින් පවතින දේවලින් වියුක්ත වී ඇති අතර ඔබ ලිහිල්ව සම්බන්ධිත කේතයක් ලියයි.
මූලාශ්රය: ජාවා හි වැඩසටහන් සංවර්ධනය - බාබරා ලිස්කොව්
පාදක පංති වෙත යොමු කිරීම් හෝ යොමු කිරීම් භාවිතා කරන කාර්යයන් නොදැන ව්යුත්පන්න පංතිවල වස්තු භාවිතා කළ යුතුය.
එල්එස්පී ගැන මා මුලින් කියවූ විට, මෙය ඉතා දැඩි අර්ථයකින් අදහස් කළ බව උපකල්පනය කළෙමි. එයින් අදහස් වන්නේ එල්එස්පී සහතික කරනු ලබන්නේ හෝ භාෂාව විසින්ම නොවන බවයි. උදාහරණයක් ලෙස, මෙම දැඩි අර්ථයෙන් ගත් කල, සම්පාදකයාට අනුව, ත්රි ඩී බෝඩ් නිසැකවම මණ්ඩලයට ආදේශ කළ හැකිය.
එල්එස්පී සාමාන්යයෙන් ඊට වඩා පුළුල් ලෙස අර්ථකථනය කර ඇති බව මට පෙනී ගියද, සංකල්පය පිළිබඳ වැඩිදුර කියවීමෙන් පසුව මට පෙනී ගියේය.
කෙටියෙන් කිවහොත්, ග්රාහක කේතයට “දැනගැනීම” යන්නෙන් අදහස් කරන්නේ දර්ශකයේ පිටුපස ඇති වස්තුව දර්ශක වර්ගයට වඩා ව්යුත්පන්න වර්ගයක් බව ටයිප්-ආරක්ෂාවට පමණක් සීමා නොවේ. වස්තූන්ගේ සැබෑ හැසිරීම පරීක්ෂා කිරීම තුළින් එල්එස්පී පිළිපැදීම ද පරීක්ෂා කළ හැකිය. එනම්, ක්රම ඇමතුම්වල ප්රති results ල මත වස්තුවක තත්වය සහ ක්රම තර්ක වල බලපෑම හෝ වස්තුවෙන් විසි කරන ව්යතිරේක වර්ග පරීක්ෂා කිරීම.
නැවත උදාහරණයට යාම, න්යායිකව , ත්රි ඩී බෝඩ් හි හොඳින් ක්රියා කිරීමට මණ්ඩල ක්රමවේදයන් සකස් කළ හැකිය. කෙසේ වෙතත්, ප්රායෝගිකව, ThreeDBoard එකතු කිරීමට අදහස් කරන ක්රියාකාරීත්වයට බාධා නොකර, සේවාදායකයා නිසි ලෙස හැසිරවිය නොහැකි හැසිරීම් වල වෙනස්කම් වැළැක්වීම ඉතා අපහසු වනු ඇත.
මෙම දැනුම අතේ ඇති හෙයින්, උරුමය වෙනුවට පවත්නා ක්රියාකාරිත්වය දීර් for කිරීම සඳහා සංයුතිය වඩාත් සුදුසු යාන්ත්රණය වන්නේ කවදාද යන්න තීරණය කිරීම සඳහා එල්එස්පී පිළිපැදීම ඇගයීම හොඳ මෙවලමක් විය හැකිය.
එල්එස්පී භාවිතය පිළිබඳ වැදගත් උදාහරණයක් වන්නේ මෘදුකාංග පරීක්ෂාවයි .
මට B පන්තියේ LSP අනුකූල උප පංතියක් වන A පන්තියක් තිබේ නම්, මට A පරීක්ෂා කිරීම සඳහා B හි පරීක්ෂණ කට්ටලය නැවත භාවිතා කළ හැකිය.
උප පංතිය A සම්පූර්ණයෙන් පරීක්ෂා කිරීම සඳහා, මට තවත් පරීක්ෂණ අවස්ථා කිහිපයක් එකතු කිරීමට අවශ්ය වනු ඇත, නමුත් අවම වශයෙන් මට සුපිරි පන්තියේ B හි පරීක්ෂණ අවස්ථා සියල්ලම නැවත භාවිතා කළ හැකිය.
අවබෝධ කර ගත හැකි ක්රමයක් නම් මැක්ග්රෙගර් “පරීක්ෂණ සඳහා සමාන්තර ධූරාවලියක්” ලෙස හැඳින්වීමයි: මගේ ATest
පන්තිය උරුම වනු ඇත BTest
. පරීක්ෂණ අවස්ථාව B වර්ගයට වඩා A වර්ගයේ වස්තූන් සමඟ ක්රියා කරන බව සහතික කිරීම සඳහා යම් ආකාරයක එන්නත් කිරීමක් අවශ්ය වේ (සරල අච්චු ක්රම රටාවක් මඟින් කරනු ඇත).
සියළුම උප පංති ක්රියාත්මක කිරීම් සඳහා සුපිරි පරීක්ෂණ කට්ටලය නැවත භාවිතා කිරීම ඇත්ත වශයෙන්ම මෙම උප පංති ක්රියාත්මක කිරීම් එල්එස්පී අනුකූලදැයි පරීක්ෂා කිරීමට ක්රමයක් බව සලකන්න. මේ අනුව, ඕනෑම උප පංතියක සන්දර්භය තුළ යමෙකු සුපිරි පන්තියේ පරීක්ෂණ කට්ටලය ධාවනය කළ යුතු යැයි කෙනෙකුට තර්ක කළ හැකිය .
ජාවා හි නිදර්ශනය කරමු:
class TrasportationDevice
{
String name;
String getName() { ... }
void setName(String n) { ... }
double speed;
double getSpeed() { ... }
void setSpeed(double d) { ... }
Engine engine;
Engine getEngine() { ... }
void setEngine(Engine e) { ... }
void startEngine() { ... }
}
class Car extends TransportationDevice
{
@Override
void startEngine() { ... }
}
මෙතන කිසිම ප්රශ්නයක් නැහැ නේද? මෝටර් රථයක් අනිවාර්යයෙන්ම ප්රවාහන උපකරණයක් වන අතර මෙහිදී අපට දැක ගත හැකි වන්නේ එය එහි සුපිරි පන්තියේ ආරම්භක එන්ජින් () ක්රමය ඉක්මවා යන බවයි.
අපි තවත් ප්රවාහන උපාංගයක් එකතු කරමු:
class Bicycle extends TransportationDevice
{
@Override
void startEngine() /*problem!*/
}
දැන් සියල්ල සැලසුම් කළ පරිදි සිදු නොවේ! ඔව්, බයිසිකලයක් යනු ප්රවාහන උපකරණයකි, කෙසේ වෙතත් එයට එන්ජිමක් නොමැති අතර ඒ නිසා ආරම්භක එන්ජින් () ක්රමය ක්රියාත්මක කළ නොහැක.
ලිස්කොව් ආදේශන මූලධර්මය උල්ලං violation නය කිරීමට හේතු වන ගැටළු මේවා වන අතර ඒවා බොහෝ විට හඳුනාගත හැක්කේ කිසිවක් නොකරන හෝ ක්රියාත්මක කළ නොහැකි ක්රමවේදයක් මගිනි.
මෙම ගැටළුවලට විසඳුම නිවැරදි උරුම ධූරාවලියක් වන අතර, අපගේ නඩුවේදී අපි එන්ජින් සමඟ සහ රහිතව ප්රවාහන උපකරණ වර්ග වෙන්කර හඳුනා ගැනීමෙන් ගැටළුව විසඳන්නෙමු. බයිසිකලයක් ප්රවාහන උපකරණයක් වුවද එයට එන්ජිමක් නොමැත. මෙම උදාහරණයේ දී ප්රවාහන උපාංගය පිළිබඳ අපගේ අර්ථ දැක්වීම වැරදිය. එයට එන්ජිමක් නොතිබිය යුතුය.
අපගේ ප්රවාහන සේවා පන්තිය පහත පරිදි ප්රතිනිර්මාණය කළ හැකිය:
class TrasportationDevice
{
String name;
String getName() { ... }
void setName(String n) { ... }
double speed;
double getSpeed() { ... }
void setSpeed(double d) { ... }
}
දැන් අපට මෝටර් නොවන උපාංග සඳහා ප්රවාහන සේවය දීර් extend කළ හැකිය.
class DevicesWithoutEngines extends TransportationDevice
{
void startMoving() { ... }
}
යතුරුපැදි උපාංග සඳහා ප්රවාහන සේවය දීර් extend කරන්න. එන්ජින් වස්තුව එකතු කිරීම සඳහා වඩාත් සුදුසු වේ.
class DevicesWithEngines extends TransportationDevice
{
Engine engine;
Engine getEngine() { ... }
void setEngine(Engine e) { ... }
void startEngine() { ... }
}
මේ අනුව ලිස්කොව් ආදේශන මූලධර්මයට අනුකූලව අපගේ කාර් පන්තිය වඩාත් විශේෂිත වේ.
class Car extends DevicesWithEngines
{
@Override
void startEngine() { ... }
}
අපේ බයිසිකල් පන්තිය ද ලිස්කොව් ආදේශන මූලධර්මයට අනුකූල වේ.
class Bicycle extends DevicesWithoutEngines
{
@Override
void startMoving() { ... }
}
එල්එස්පී හි මෙම සූත්රගත කිරීම ඉතා ශක්තිමත් ය:
S වර්ගයේ සෑම වස්තුවක් සඳහාම T2 වර්ගයේ වස්තුවක් තිබේ නම්, සියලු වැඩසටහන් සඳහා T ට අනුව P de fi ned නම්, o1 o2 වෙනුවට ආදේශ කිරීමේදී P හි හැසිරීම නොවෙනස්ව පවතී, එවිට S යනු T හි උප ප්රභේදයකි.
එහි මූලික අරුත වන්නේ එස් යනු ටී හා සමාන දෙයක් මුළුමනින්ම සංවෘත ලෙස ක්රියාත්මක කිරීමයි. තවද මට නිර්භීත විය හැකි අතර කාර්ය සාධනය පී හි හැසිරීමේ කොටසක් බව තීරණය කළ හැකිය.
එබැවින්, මූලික වශයෙන්, ප්රමාද බන්ධනවල ඕනෑම භාවිතයක් එල්එස්පී උල්ලං lates නය කරයි. අප එක් වර්ගයක වස්තුවක් වෙනත් වර්ගයකට ආදේශ කරන විට වෙනස් හැසිරීමක් ලබා ගැනීම OO හි සමස්ත කරුණයි!
විකිපීඩියාව විසින් උපුටා දක්වන ලද සූත්රගත කිරීම වඩා හොඳ වන්නේ දේපල සන්දර්භය මත රඳා පවතින බැවින් සහ වැඩසටහනේ සමස්ත හැසිරීම අනිවාර්යයෙන්ම ඇතුළත් නොවන බැවිනි.
ඉතා සරල වාක්යයකින් අපට මෙසේ පැවසිය හැකිය.
ළමා පන්තිය එහි මූලික පන්ති ලක්ෂණ උල්ලං not නය නොකළ යුතුය. එය සමඟ හැකියාව තිබිය යුතුය. අපට කිව හැක්කේ එය උප වර්ගය හා සමාන බවයි.
ලිස්කොව්ගේ ආදේශන මූලධර්මය (එල්එස්පී)
සෑම විටම අපි වැඩසටහන් මොඩියුලයක් නිර්මාණය කරන අතර අපි පන්ති ධූරාවලියක් නිර්මාණය කරමු. ඉන්පසුව අපි ව්යුත්පන්න පංති කිහිපයක් නිර්මාණය කරමින් පන්ති දීර් extend කරමු.
පැරණි ව්යුත්පන්න පංතිවල ක්රියාකාරිත්වය ප්රතිස්ථාපනය නොකර නව ව්යුත්පන්න පංති විස්තාරණය වන බවට අප වග බලා ගත යුතුය. එසේ නොමැති නම්, නව පංති පවතින වැඩසටහන් මොඩියුලවල භාවිතා කරන විට අනවශ්ය බලපෑම් ඇති කළ හැකිය.
ලිස්කොව්ගේ ආදේශන මූලධර්මය පවසන්නේ වැඩසටහන් මොඩියුලයක් මූලික පංතියක් භාවිතා කරන්නේ නම්, වැඩසටහන් මොඩියුලයේ ක්රියාකාරීත්වයට කිසිදු බලපෑමක් නොකර මූලික පන්තිය පිළිබඳ සඳහන ව්යුත්පන්න පන්තියක් සමඟ ප්රතිස්ථාපනය කළ හැකි බවයි.
උදාහරණයක්:
පහත දැක්වෙන්නේ ලිස්කොව්ගේ ආදේශන මූලධර්මය උල්ලං is නය වන හොඳම උදාහරණයයි. උදාහරණයක් ලෙස, පන්ති 2 ක් භාවිතා කරයි: සෘජුකෝණාස්රය සහ චතුරස්රය. යෙදුමේ කොතැනක හෝ සෘජුකෝණාස්රාකාර වස්තුව භාවිතා කර ඇතැයි උපකල්පනය කරමු. අපි යෙදුම දිගු කර වර්ග පන්තිය එකතු කරමු. සමහර කොන්දේසි මත පදනම්ව වර්ග පන්තිය කර්මාන්තශාලා රටාවකින් ආපසු ලබා දෙන අතර කුමන ආකාරයේ වස්තුවක් ආපසු ලබා දෙනු ඇත්දැයි අපි නොදනිමු. නමුත් අපි දන්නවා එය සෘජුකෝණාස්රයක්. අපි සෘජුකෝණාස්රාකාර වස්තුව ලබා ගනිමු, පළල 5 ට සහ උස 10 ට සකසා ප්රදේශය ලබා ගනිමු. පළල 5 සහ උස 10 සහිත සෘජුකෝණාස්රයක් සඳහා, ප්රදේශය 50 ක් විය යුතුය. ඒ වෙනුවට, ප්රති result ලය 100 ක් වනු ඇත
// Violation of Likov's Substitution Principle
class Rectangle {
protected int m_width;
protected int m_height;
public void setWidth(int width) {
m_width = width;
}
public void setHeight(int height) {
m_height = height;
}
public int getWidth() {
return m_width;
}
public int getHeight() {
return m_height;
}
public int getArea() {
return m_width * m_height;
}
}
class Square extends Rectangle {
public void setWidth(int width) {
m_width = width;
m_height = width;
}
public void setHeight(int height) {
m_width = height;
m_height = height;
}
}
class LspTest {
private static Rectangle getNewRectangle() {
// it can be an object returned by some factory ...
return new Square();
}
public static void main(String args[]) {
Rectangle r = LspTest.getNewRectangle();
r.setWidth(5);
r.setHeight(10);
// user knows that r it's a rectangle.
// It assumes that he's able to set the width and height as for the base
// class
System.out.println(r.getArea());
// now he's surprised to see that the area is 100 instead of 50.
}
}
නිගමනය:
මෙම මූලධර්මය විවෘත වසා දැමීමේ මූලධර්මයේ දිගුවක් පමණක් වන අතර එයින් අදහස් කරන්නේ නව ව්යුත්පන්න පංති ඔවුන්ගේ හැසිරීම වෙනස් නොකර මූලික පන්ති පුළුල් කරන බවට අප වග බලා ගත යුතු බවයි.
මෙයද බලන්න: විවෘත වසන්න මූලධර්මය
වඩා හොඳ ව්යුහයක් සඳහා සමාන සංකල්ප කිහිපයක්: වින්යාසය පිළිබඳ සම්මුතිය
සමහර අතිරේකයක්:
ව්යුත්පන්න පංතිවලට අවනත විය යුතු මූලික පංතියේ වෙනස් නොවන, පූර්ව කොන්දේසි සහ පශ්චාත් කොන්දේසි ගැන කිසිවෙකු ලිව්වේ නැත්තේ ඇයිදැයි මම කල්පනා කරමි. ව්යුත්පන්න පංතිය B මූලික පන්තියෙන් සම්පූර්ණයෙන්ම ගැලවිය හැකි නම්, D පන්තිය සමහර කොන්දේසි වලට අවනත විය යුතුය:
එබැවින් ව්යුත්පන්නය මූලික පන්තිය විසින් පනවා ඇති ඉහත කොන්දේසි තුන පිළිබඳව දැනුවත් විය යුතුය. එබැවින් උප වර්ගීකරණය කිරීමේ නීති කලින් තීරණය කර ඇත. එහි අර්ථය නම්, 'අයිඑස් ඒ' සම්බන්ධතාවයට අවනත විය යුත්තේ යම් යම් නීති රීති උප ප්රභේදයට අවනත වූ විට පමණි. මෙම නීති, වෙනස්වීම්, පූර්ව නිගමන සහ පශ්චාත් කොන්දේසි ලෙස විධිමත් ' සැලසුම් කොන්ත්රාත්තුවක් ' මගින් තීරණය කළ යුතුය .
මේ පිළිබඳ වැඩිදුර සාකච්ඡා මගේ බ්ලොග් අඩවියෙන් ලබා ගත හැකිය: ලිස්කොව් ආදේශන මූලධර්මය
එල්එස්පී සරල වචන වලින් කියනවා එකම සුපිරි පන්තියේ වස්තූන් කිසිවක් කඩ නොකර එකිනෙකා සමඟ හුවමාරු කර ගත හැකි බව .
උදාහරණයක් ලෙස, අපි නම් Cat
සහ Dog
ක ව්යුත්පන්න පන්තියේ Animal
පන්ති, සත්ව පන්ති භාවිතා කිසිදු කාර්යයක් භාවිතා කිරීමට හැකි විය යුතුය Cat
හෝ Dog
හා සාමාන්යයෙන් හැසිරෙන.
මෙම මූලධර්මය 1987 දී බාබරා ලිස්කොව් විසින් හඳුන්වා දෙන ලද අතර සුපිරි පන්තියක හැසිරීම සහ එහි උප ප්රභේද කෙරෙහි අවධානය යොමු කරමින් විවෘත සංවෘත මූලධර්මය පුළුල් කරයි.
එය උල්ලං of නය කිරීමේ ප්රතිවිපාක සලකා බලන විට එහි වැදගත්කම පැහැදිලි වේ. පහත පන්තිය භාවිතා කරන යෙදුමක් සලකා බලන්න.
public class Rectangle
{
private double width;
private double height;
public double Width
{
get
{
return width;
}
set
{
width = value;
}
}
public double Height
{
get
{
return height;
}
set
{
height = value;
}
}
}
එක් දිනක් සේවාදායකයා සෘජුකෝණාස්රා වලට අමතරව චතුරස්රයන් හැසිරවීමේ හැකියාව ඉල්ලා සිටින බව සිතන්න. චතුරස්රයක් සෘජුකෝණාස්රයක් බැවින්, වර්ග පන්තිය සෘජුකෝණාස්රා පන්තියෙන් ව්යුත්පන්න විය යුතුය.
public class Square : Rectangle
{
}
කෙසේ වෙතත්, එසේ කිරීමෙන් අපට ගැටළු දෙකක් ඇති වේ:
හතරැස් කොටුවකට උරුම වූ උස හා පළල විචල්යයන් අවශ්ය නොවන අතර වර්ග වස්තු සිය දහස් ගණනක් නිර්මාණය කිරීමට සිදුවුවහොත් මෙය මතකයේ සැලකිය යුතු අපද්රව්යයක් නිර්මාණය කළ හැකිය. හතරැස් කොටුවෙන් පළල සහ උස සැකසීමේ ගුණාංග වර්ගයකට නුසුදුසු බැවින් වර්ගයක පළල සහ උස සමාන වේ. උස සහ පළල යන දෙකම එකම අගයකට සැකසීම සඳහා, අපට පහත පරිදි නව ගුණාංග දෙකක් නිර්මාණය කළ හැකිය:
public class Square : Rectangle
{
public double SetWidth
{
set
{
base.Width = value;
base.Height = value;
}
}
public double SetHeight
{
set
{
base.Height = value;
base.Width = value;
}
}
}
දැන්, යමෙකු හතරැස් වස්තුවක පළල සකසන විට, එහි උස ඒ අනුව සහ අනෙක් අතට වෙනස් වේ.
Square s = new Square();
s.SetWidth(1); // Sets width and height to 1.
s.SetHeight(2); // sets width and height to 2.
අපි ඉදිරියට යමු මෙම අනෙක් කාර්යය සලකා බලමු:
public void A(Rectangle r)
{
r.SetWidth(32); // calls Rectangle.SetWidth
}
අපි මෙම ශ්රිතයට වර්ග වස්තුවක් වෙත යොමු කිරීමක් කළහොත්, අපි එල්එස්පී උල්ලං would නය කරන්නේ එම ශ්රිතය එහි තර්කවල ව්යුත්පන්නයන් සඳහා ක්රියා නොකරන බැවිනි. ගුණාංග පළල සහ උස බහුඅවයවික නොවේ, මන්ද ඒවා සෘජුකෝණාස්රයේ අථත්ය ලෙස ප්රකාශයට පත් නොකෙරේ (උස වෙනස් නොවන නිසා වර්ග වස්තුව දූෂිත වනු ඇත).
කෙසේ වෙතත්, සැකසුම් ගුණාංග අතථ්ය යැයි ප්රකාශ කිරීමෙන් අපට තවත් උල්ලං face නය කිරීමකට මුහුණ දීමට සිදුවේ. ඇත්ත වශයෙන්ම, ව්යුත්පන්න පංති චතුරස්රයක් නිර්මාණය කිරීම මූලික පන්තියේ සෘජුකෝණාස්රයේ වෙනස්කම් ඇති කරයි.
චතුරස්රයක් යනු පළල උස හා සමාන වන සෘජුකෝණාස්රයකි. පළල සහ උස සඳහා වර්ග දෙක වෙනස් ප්රමාණ දෙකක් සකසන්නේ නම් එය වර්ග ආක්රමණ උල්ලං lates නය කරයි. අතුරු ආබාධ හඳුන්වා දීමෙන් මෙය ක්රියාත්මක වේ. නමුත් සෘජුකෝණාස්රයේ පූර්ව කොන්දේසිය 0 <උස සහ 0 <පළල සහිත කට්ටලයක් (උස, පළල) තිබේ නම්. ව්යුත්පන්න උප වර්ගයට උස == පළල අවශ්ය වේ; වඩා ශක්තිමත් පූර්ව කොන්දේසියක් (සහ එය lsp උල්ලං lates නය කරයි). මෙයින් පෙනී යන්නේ හතරැස් සෘජුකෝණාස්රයක් වුවද එය වලංගු උප ප්රභේදයක් නොවන බැවින් පූර්ව කොන්දේසිය ශක්තිමත් වන බවයි. අවට වැඩ (පොදුවේ නරක දෙයක්) අතුරු ආබාධයක් ඇති කරන අතර මෙය පශ්චාත් තත්වය දුර්වල කරයි (එය lsp උල්ලං) නය කරයි). setWidth පාදමේ 0 <පළල. ව්යුත්පන්නය උස == පළල සමඟ එය දුර්වල කරයි.
එබැවින් ප්රතිනිර්මාණය කළ හැකි චතුරස්රයක් නැවත වෙනස් කළ හැකි සෘජුකෝණාස්රයක් නොවේ.
පුවරු සමූහයක් අනුව ත්රි ඩී බෝඩ් ක්රියාත්මක කිරීම එතරම් ප්රයෝජනවත් වේද?
සමහර විට ඔබට විවිධ ගුවන් යානා වල ත්රී ඩී බෝඩ් පෙති මණ්ඩලයක් ලෙස සැලකීමට අවශ්ය විය හැකිය. එවැනි අවස්ථාවකදී ඔබට මණ්ඩලය සඳහා බහු අතුරු මුහුණතක් (හෝ වියුක්ත පන්තියක්) සාරාංශ කිරීමට අවශ්ය විය හැකිය.
බාහිර අතුරුමුහුණත සම්බන්ධයෙන් ගත් කල, ඔබට TwoDBoard සහ ThreeDBoard යන දෙකටම පුවරු අතුරුමුහුණතක් ඉදිරිපත් කිරීමට අවශ්ය විය හැකිය (ඉහත ක්රම කිසිවක් නොගැලපේ).
එල්එස්පී සඳහා මා මෙතෙක් සොයාගෙන ඇති පැහැදිලි පැහැදිලි කිරීම නම් “ලිස්කොව් ආදේශන මූලධර්මය පවසන්නේ ව්යුත්පන්න පන්තියක වස්තුවට පද්ධතියේ කිසිදු දෝෂයක් ගෙන ඒමට හෝ මූලික පන්තියේ හැසිරීම වෙනස් නොකර මූලික පන්තියේ වස්තුවක් ප්රතිස්ථාපනය කිරීමට හැකි විය යුතු බවයි. " මෙතනින් . එල්එස්පී උල්ලං ting නය කිරීම සහ එය නිවැරදි කිරීම සඳහා ලිපිය කේත උදාහරණයක් සපයයි.
අපි අපේ කේතයේ සෘජුකෝණාස්රයක් භාවිතා කරමු
r = new Rectangle();
// ...
r.setDimensions(1,2);
r.fill(colors.red());
canvas.draw(r);
අපගේ ජ්යාමිති පන්තියේදී අපි දැනගත්තේ චතුරස්රයක් යනු විශේෂ සෘජුකෝණාස්රයක් වන බැවින් එහි පළල එහි උස හා සමාන දිගක් වන බැවිනි. Square
මෙම තොරතුරු මත පදනම්ව අපි පන්තියක් කරමු :
class Square extends Rectangle {
setDimensions(width, height){
assert(width == height);
super.setDimensions(width, height);
}
}
අපගේ පළමු කේතය Rectangle
සමඟ අපි එය ප්රතිස්ථාපනය කළහොත් Square
එය බිඳී යනු ඇත:
r = new Square();
// ...
r.setDimensions(1,2); // assertion width == height failed
r.fill(colors.red());
canvas.draw(r);
මෙයට හේතුව පන්තියේ Square
අප සතුව නොතිබූ නව පූර්ව කොන්දේසියක් තිබීමයි Rectangle
: width == height
. එල්එස්පීයට අනුව, උප පංති අවස්ථා Rectangle
සමඟ ආදේශ කළ යුතුය Rectangle
. මෙයට හේතුව මෙම සිද්ධීන් නිදසුන් සඳහා වන වර්ගයේ චෙක්පත පසු කරන Rectangle
අතර එමඟින් ඔබේ කේතයේ අනපේක්ෂිත දෝෂ ඇති වේ.
මේ සඳහා උදාහරණයක් ලෙස ඔහු "පූර්ව කොන්දේසි ඇති subtype ශක්තිමත් කළ නොහැක" මෙම ප්රදේශයේ දී , විකි ලිපිය . සාරාංශයක් ලෙස, එල්එස්පී උල්ලං ting නය කිරීම සමහර විට ඔබේ කේතයේ දෝෂ ඇති කරයි.
එල්එස්පී පවසන්නේ වස්තූන් ඒවායේ උප ප්රභේද මගින් ප්රතිස්ථාපනය කළ යුතු බවයි. අනෙක් අතට, මෙම මූලධර්මය පෙන්වා දෙයි
ළමා පන්ති කිසි විටෙකත් මව් පන්තියේ අර්ථ දැක්වීම් කඩ නොකළ යුතුය.
එල්එස්පී පිළිබඳ වඩා හොඳ අවබෝධයක් ලබා ගැනීමට පහත උදාහරණය උපකාරී වේ.
එල්එස්පී නොමැතිව:
public interface CustomerLayout{
public void render();
}
public FreeCustomer implements CustomerLayout {
...
@Override
public void render(){
//code
}
}
public PremiumCustomer implements CustomerLayout{
...
@Override
public void render(){
if(!hasSeenAd)
return; //it isn`t rendered in this case
//code
}
}
public void renderView(CustomerLayout layout){
layout.render();
}
එල්එස්පී විසින් සවි කිරීම:
public interface CustomerLayout{
public void render();
}
public FreeCustomer implements CustomerLayout {
...
@Override
public void render(){
//code
}
}
public PremiumCustomer implements CustomerLayout{
...
@Override
public void render(){
if(!hasSeenAd)
showAd();//it has a specific behavior based on its requirement
//code
}
}
public void renderView(CustomerLayout layout){
layout.render();
}
ලිපිය කියවීමට මම ඔබව දිරිමත් කරමි: ලිස්කොව් ආදේශන මූලධර්මය (එල්එස්පී) උල්ලං ting නය කිරීම .
ලිස්කොව් ආදේශන මූලධර්මය යනු කුමක්ද යන්න පැහැදිලි කිරීමක් ඔබට සොයාගත හැකිය, ඔබ දැනටමත් එය උල්ලං has නය කර ඇත්දැයි අනුමාන කිරීමට උපකාරී වන සාමාන්ය ඉඟි සහ ඔබේ පන්ති ධූරාවලිය වඩාත් ආරක්ෂිත කිරීමට ඔබට උපකාරී වන ප්රවේශයේ උදාහරණයක්.
LISKOV SUBSTITUTION PRINCIPLE (මාර්ක් සීමන් පොතෙන්) සඳහන් කරන්නේ සේවාදායකයා හෝ ක්රියාත්මක කිරීම නොකොට අතුරු මුහුණතක එක් ක්රියාත්මක කිරීමක් වෙනත් එකක් සමඟ ප්රතිස්ථාපනය කිරීමට අපට හැකි විය යුතු බවයි. මෙම මූලධර්මය මඟින් අනාගතයේදී සිදුවන අවශ්යතා සපුරාලීමට අපට හැකි වුවද, අද ඒවා පුරෝකථනය නොකරන්න.
අපි පරිගණකය බිත්තියෙන් ඉවත් කළහොත් (ක්රියාත්මක කිරීම), බිත්ති දොරටුව (අතුරුමුහුණත) හෝ පරිගණකය (සේවාදායකයා) බිඳ වැටෙන්නේ නැත (ඇත්ත වශයෙන්ම එය ලැප්ටොප් පරිගණකයක් නම්, එය යම් කාලයක් සඳහා එහි බැටරි මත පවා ධාවනය කළ හැකිය) . කෙසේ වෙතත්, මෘදුකාංග සමඟ, සේවාදායකයෙකු බොහෝ විට සේවාවක් ලබා ගැනීමට අපේක්ෂා කරයි. සේවාව ඉවත් කර ඇත්නම්, අපට NullReferenceException එකක් ලැබේ. මෙවැනි තත්වයන්ට මුහුණ දීම සඳහා අපට “කිසිවක්” නොකරන අතුරු මුහුණතක් ක්රියාත්මක කළ හැකිය. මෙය Null Object ලෙස හැඳින්වෙන මෝස්තර රටාවකි, [4] එය දළ වශයෙන් පරිගණකය බිත්තියෙන් ඉවත් කිරීමට අනුරූප වේ. අප ලිහිල් කප්ලිං භාවිතා කරන නිසා, අපට සැබෑ ක්රියාවලියක් කරදරයකින් තොරව කිසිවක් නොකරන දෙයක් සමඟ ප්රතිස්ථාපනය කළ හැකිය.
ලිකොව්ගේ ආදේශන මූලධර්මය පවසන්නේ වැඩසටහන් මොඩියුලයක් මූලික පන්තියක් භාවිතා කරන්නේ නම්, වැඩසටහන් මොඩියුලයේ ක්රියාකාරීත්වයට කිසිදු බලපෑමක් නොකර මූලික පන්තිය පිළිබඳ සඳහන ව්යුත්පන්න පන්තියක් සමඟ ප්රතිස්ථාපනය කළ හැකි බවයි.
අභිප්රාය - ව්යුත්පන්න වර්ග ඒවායේ මූලික වර්ග සඳහා සම්පූර්ණයෙන්ම ආදේශ කළ යුතුය.
උදාහරණය - ජාවා හි සම-විචල්ය ප්රතිලාභ වර්ග.
මෙන්න මේ පෝස්ට් එකෙන් උපුටා ගත් කොටසක් කාරණා මනාව පැහැදිලි කරයි:
[..] සමහර මූලධර්ම අවබෝධ කර ගැනීම සඳහා, එය උල්ලං has නය වූ විට එය වටහා ගැනීම වැදගත්ය. මෙය මම දැන් කරන්නෙමි.
මෙම මූලධර්මය උල්ලං violation නය කිරීම යන්නෙන් අදහස් කරන්නේ කුමක්ද? එයින් ගම්ය වන්නේ වස්තුවක් අතුරුමුහුණතක් සමඟ ප්රකාශිත සාරාංශයක් මගින් පනවන ලද කොන්ත්රාත්තුව ඉටු නොකරන බවයි. වෙනත් වචන වලින් කිවහොත්, එයින් අදහස් වන්නේ ඔබ ඔබේ සාරාංශ වැරදි ලෙස හඳුනාගෙන ඇති බවයි.
පහත උදාහරණය සලකා බලන්න:
interface Account
{
/**
* Withdraw $money amount from this account.
*
* @param Money $money
* @return mixed
*/
public function withdraw(Money $money);
}
class DefaultAccount implements Account
{
private $balance;
public function withdraw(Money $money)
{
if (!$this->enoughMoney($money)) {
return;
}
$this->balance->subtract($money);
}
}
මෙය එල්එස්පී උල්ලං is නය කිරීමක්ද? ඔව්. මෙයට හේතුව ගිණුමේ මුදල් ආපසු ගන්නා බව ගිණුමේ කොන්ත්රාත්තුව අපට පවසන නමුත් මෙය සැමවිටම එසේ නොවේ. එබැවින්, එය නිවැරදි කිරීම සඳහා මා කුමක් කළ යුතුද? මම දැන් කොන්ත්රාත්තුව වෙනස් කරමි:
interface Account
{
/**
* Withdraw $money amount from this account if its balance is enough.
* Otherwise do nothing.
*
* @param Money $money
* @return mixed
*/
public function withdraw(Money $money);
}
Voilà, දැන් කොන්ත්රාත්තුව සෑහීමකට පත්වේ.
මෙම සියුම් උල්ලං violation නය කිරීම බොහෝ විට සේවාදායකයකු විසින් භාවිතා කරනු ලබන කොන්ක්රීට් වස්තූන් අතර වෙනස පැවසීමේ හැකියාව ඇත. උදාහරණයක් ලෙස, පළමු ගිණුමේ කොන්ත්රාත්තුව අනුව, එය පහත පරිදි විය හැකිය:
class Client
{
public function go(Account $account, Money $money)
{
if ($account instanceof DefaultAccount && !$account->hasEnoughMoney($money)) {
return;
}
$account->withdraw($money);
}
}
තවද, මෙය ස්වයංක්රීයව විවෘත සංවෘත මූලධර්මය උල්ලං lates නය කරයි [එනම් මුදල් ආපසු ගැනීමේ අවශ්යතාවය සඳහා. කොන්ත්රාත්තුව උල්ලං ting නය කරන වස්තුවකට ප්රමාණවත් මුදලක් නොමැති නම් කුමක් සිදුවේදැයි ඔබ කිසි විටෙකත් නොදන්නා බැවිනි. බොහෝ විට එය කිසිවක් ආපසු නොදෙනු ඇත, බොහෝ විට ව්යතිරේකයක් විසි කරනු ඇත. එබැවින් ඔබ එය පරික්ෂා කළ යුතුය hasEnoughMoney()
- එය අතුරු මුහුණතක කොටසක් නොවේ. එබැවින් මෙම බලහත්කාරයෙන් කොන්ක්රීට් පන්තිය මත යැපෙන චෙක්පත OCP උල්ලං is නය කිරීමකි].
එල්එස්පී උල්ලං about නය කිරීම පිළිබඳව මා නිතර නිතර මුණගැසෙන වැරදි මතයකට මෙම කරුණ ආමන්ත්රණය කරයි. එහි සඳහන් වන්නේ “දරුවෙකු තුළ දෙමව්පියන්ගේ හැසිරීම වෙනස් වූයේ නම් එය එල්එස්පී උල්ලං lates නය කරයි.” කෙසේ වෙතත්, එය එසේ නොවේ - දරුවෙකු තම දෙමව්පියන්ගේ කොන්ත්රාත්තුව උල්ලං does නය නොකරන තාක් කල්.