শুনেছি লিসকভ সাবস্টিটিউশন প্রিন্সিপাল (এলএসপি) অবজেক্ট অরিয়েন্টেড ডিজাইনের একটি মৌলিক নীতি। এটি কী এবং এর ব্যবহারের কয়েকটি উদাহরণ কী?
শুনেছি লিসকভ সাবস্টিটিউশন প্রিন্সিপাল (এলএসপি) অবজেক্ট অরিয়েন্টেড ডিজাইনের একটি মৌলিক নীতি। এটি কী এবং এর ব্যবহারের কয়েকটি উদাহরণ কী?
উত্তর:
এলএসপি চিত্রিত করার একটি দুর্দান্ত উদাহরণ (আমি সম্প্রতি পডকাস্টে আঙ্কেল বব দ্বারা প্রদত্ত) এটি ছিল যে কখনও কখনও প্রাকৃতিক ভাষায় যে শব্দটি ডান মনে হয় সেটি কোডটিতে বেশ কার্যকর হয় না।
গণিতে, Square
ক Rectangle
। আসলে এটি একটি আয়তক্ষেত্রের বিশেষত্ব special "এটি একটি" আপনাকে উত্তরাধিকারের সাথে মডেল করতে চায় makes তবে কোডে যদি আপনি Square
উদ্ভূত হন Rectangle
, তবে আপনি যেখান থেকে Square
আশা করেন তা একটি ব্যবহারযোগ্য হবে Rectangle
। এটি কিছু অদ্ভুত আচরণ করে তোলে।
আপনার বেস ক্লাসে আপনার পদ্ধতি SetWidth
এবং SetHeight
পদ্ধতিগুলি কল্পনা করুন Rectangle
; এটি পুরোপুরি যৌক্তিক বলে মনে হয়। তবে যদি আপনার Rectangle
উল্লেখকে একটি তীক্ষ্ন Square
, তারপর SetWidth
এবং SetHeight
অর্থে দেখা যায় না কারণ এক সেটিং জন এটি মেলে পরিবর্তন হবে। এই ক্ষেত্রে Square
সঙ্গে Liskov উপকল্পন টেস্ট ব্যর্থ Rectangle
এবং থাকার বিমূর্ততা Square
থেকে উত্তরাধিকারী Rectangle
একটি খারাপ নেই।
আপনার সমস্ত মূল্যবান সলিড নীতি মোটিভেশনাল পোস্টারগুলি পরীক্ষা করা উচিত ।
Square.setWidth(int width)
ভালো বাস্তবায়িত হয়েছে: this.width = width; this.height = width;
? এই ক্ষেত্রে এটি গ্যারান্টিযুক্ত যে প্রস্থটি উচ্চতার সমান।
লিসকভ সাবস্টিটিউশন নীতি (এলএসপি, LSP) অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিংয়ের একটি ধারণা যা বলে:
বেস ক্লাসে পয়েন্টার বা রেফারেন্স ব্যবহার করে এমন ফাংশনগুলি অজান্তেই উত্পন্ন ক্লাসের অবজেক্টগুলি ব্যবহার করতে সক্ষম হতে হবে।
তার হৃদয়ে এলএসপি ইন্টারফেস এবং চুক্তিগুলি সম্পর্কে পাশাপাশি কোনও শ্রেণি কখন বাড়ানো উচিত তা কীভাবে সিদ্ধান্ত নেবে সে সম্পর্কে আপনার লক্ষ্য অর্জনের জন্য রচনা সম্পর্কিত আরও কৌশল ব্যবহার করুন।
এই পয়েন্টটি চিত্রিত করার জন্য আমি সবচেয়ে কার্যকর উপায়টি হেড ফার্স্ট ওওএ এবং ডি-তে ছিল । তারা কৌশল গেমগুলির জন্য একটি কাঠামো তৈরির জন্য একটি প্রকল্পে আপনি বিকাশকারী যেখানে একটি দৃশ্য উপস্থাপন করেন।
তারা এমন একটি শ্রেণি উপস্থাপন করে যা এমন বোর্ডের প্রতিনিধিত্ব করে যা দেখতে দেখতে:
দ্বি-মাত্রিক অ্যারেটিতে টাইলের অবস্থান নির্ধারণের জন্য সমস্ত পদ্ধতি এক্স এবং ওয়াই সমন্বয়কে পরামিতি হিসাবে গ্রহণ করে Tiles
। এটি কোনও গেম ডেভেলপারকে গেমের সময়কালে বোর্ডে ইউনিট পরিচালনা করতে দেয়।
বইটি প্রয়োজনীয়তাগুলি পরিবর্তন করতে গিয়ে বলেছে যে গেমের ফ্রেমের কাজের সাথে ফ্লাইট রয়েছে এমন গেমগুলিকে সামঞ্জস্য করতে 3 ডি গেম বোর্ডকেও সমর্থন করতে হবে। সুতরাং একটি ThreeDBoard
শ্রেণি প্রবর্তিত হয় যা প্রসারিত হয় Board
।
প্রথম নজরে এটি একটি ভাল সিদ্ধান্ত বলে মনে হচ্ছে। Board
উভয় Height
এবং Width
বৈশিষ্ট্য ThreeDBoard
সরবরাহ করে এবং Z অক্ষ সরবরাহ করে।
এটা কোথায় প্রায় ভেঙে পড়েছে যখন আপনি সব অপরের থেকে উত্তরাধিকারসূত্রে সদস্যদের তাকান হয় Board
। জন্য পদ্ধতি AddUnit
, GetTile
, GetUnits
ইত্যাদি, সমস্ত উভয় X এবং Y পরামিতি গ্রহণ Board
বর্গ কিন্তু ThreeDBoard
সেইসাথে একটি টু Z প্যারামিটার প্রয়োজন।
সুতরাং আপনাকে অবশ্যই জেড প্যারামিটার দিয়ে আবার সেই পদ্ধতিগুলি প্রয়োগ করতে হবে। জেড প্যারামিটারটির Board
ক্লাসের কোনও প্রসঙ্গ নেই এবং বর্গ থেকে উত্তরাধিকার সূত্রে প্রাপ্ত পদ্ধতিগুলি Board
তাদের অর্থ হারিয়ে ফেলে। ক্লাসটিকে ThreeDBoard
তার বেস শ্রেণি হিসাবে ব্যবহার করার চেষ্টা করার কোডের একটি ইউনিট Board
ভাগ্যের বাইরে চলে যাবে।
হতে পারে আমাদের অন্য পদ্ধতির সন্ধান করা উচিত। ব্যাপ্ত পরিবর্তে Board
, ThreeDBoard
গঠিত হবে Board
বস্তু। Board
জেড অক্ষের প্রতি ইউনিট প্রতি এক বস্তু।
এটি আমাদের এনক্যাপসুলেশন এবং পুনঃব্যবহারের মতো ভাল অবজেক্ট ওরিয়েন্টেড নীতিগুলি ব্যবহার করতে দেয় এবং এলএসপি লঙ্ঘন করে না।
সাবস্টিটিউটেবিলিটি হ'ল অবজেক্ট-ওরিয়েন্টেড প্রোগ্রামিংয়ে একটি নীতি যা উল্লেখ করে যে একটি কম্পিউটার প্রোগ্রামে, যদি এস টি-এর একটি উপপ্রকার হয়, তবে টাইপ টি-এর বস্তুগুলি এস টাইপের বস্তুর সাথে প্রতিস্থাপন করা যেতে পারে type
আসুন জাভাতে একটি সাধারণ উদাহরণটি করি:
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 { }
ইন্টারফেস মেলে যদিও এখন আমাদের একটি সমস্যা আছে। কারণটি হ'ল আমরা চৌকো এবং আয়তক্ষেত্রের গাণিতিক সংজ্ঞা থেকে উদ্ভূত আক্রমণকারীদের লঙ্ঘন করেছি। গেটার্স এবং সেটাররা যেভাবে কাজ করে, Rectangle
তাদের নীচের আক্রমণকারীটিকে সন্তুষ্ট করা উচিত:
void invariant(Rectangle r) {
r.setHeight(200)
r.setWidth(100)
assert(r.getHeight() == 200 and r.getWidth() == 100)
}
তবে, এই আক্রমণকারীকে অবশ্যই সঠিক প্রয়োগের দ্বারা লঙ্ঘন করতে হবে Square
, সুতরাং এটি কোনও বৈধ বিকল্প নয় Rectangle
।
লিসকো সাবস্টিটিউশন নীতিতে রবার্ট মার্টিনের একটি দুর্দান্ত কাগজ রয়েছে । এটি সূক্ষ্ম এবং না-সূক্ষ্ম উপায়গুলি নিয়ে আলোচনা করে যাতে নীতির লঙ্ঘন হতে পারে।
কাগজের কিছু প্রাসঙ্গিক অংশ (দ্বিতীয় উদাহরণটি ভারীভাবে ঘনীভূত রয়েছে তা নোট করুন):
এলএসপি লঙ্ঘনের একটি সহজ উদাহরণ
এই নীতিটির সবচেয়ে লঙ্ঘনকারী লঙ্ঘনগুলির মধ্যে একটি হ'ল একটি অবজেক্টের ধরণের উপর ভিত্তি করে কোনও ফাংশন নির্বাচন করতে সি ++ রান-টাইম টাইপ ইনফরমেশন (আরটিটিআই) ব্যবহার। অর্থাৎ,
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
তৈরি করা হবে তখন এটি অবশ্যই পরিবর্তন করা উচিত । প্রকৃতপক্ষে, অনেকে এই ফাংশনটির কাঠামোটিকে অরিজেন্ট ওরিয়েন্টেড ডিজাইনের জন্য অ্যানথেমা হিসাবে দেখেন।স্কোয়ার এবং আয়তক্ষেত্র, আরও সূক্ষ্ম লঙ্ঘন।
তবে এলএসপি লঙ্ঘনের আরও অনেক সূক্ষ্ম ও উপায় রয়েছে।
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
, যেহেতু একটি বর্গের প্রস্থ এবং উচ্চতা অভিন্ন। এটি একটি উল্লেখযোগ্য সূত্র হওয়া উচিত যা ডিজাইনে সমস্যা রয়েছে। যাইহোক, সমস্যার পাশ কাটাবার একটি উপায় রয়েছে। আমরা ওভাররাইড করতে পারি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.
যদি কোনও শিশু-শ্রেণির প্রাক-শর্তটি পিতামাতার শ্রেণির প্রাক-শর্তের চেয়ে শক্তিশালী হয় তবে আপনি প্রাক-শর্তটি লঙ্ঘন না করে কোনও সন্তানের কোনও পিতা-মাতার স্থান পরিবর্তন করতে পারবেন না। অতএব এলএসপি।
এলএসপি প্রয়োজনীয় যেখানে কিছু কোড মনে করে যে এটি কোনও প্রকারের 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
এলএসপিতে প্রকারের T
(যেমন, Rectangle
) পদ্ধতিগুলির জন্য প্রত্যাশা (অর্থাত্ আক্রমণকারীদের) প্রয়োজন হয় , পরিবর্তে টাইপ S
(যেমন Square
) এর পদ্ধতিগুলি কল করার সময় লঙ্ঘন করা হবে না।
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
এমনকি অপরিবর্তনীয় ক্ষেত্রগুলির সাথে একটি ধরণের এখনও অদলপ্রদর্শন রয়েছে , যেমন অপরিবর্তনীয় আয়তক্ষেত্রাকার সেটারগুলি মাত্রাগুলি স্বাধীনভাবে পরিবর্তিত হওয়ার প্রত্যাশা করে , তবে অপরিবর্তনীয় স্কয়ার সেটারগুলি এই প্রত্যাশা লঙ্ঘন করে।
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
এটির তুলনামূলক বিস্ফোরণ না হয় Ti
, তবে একটি উপ- Xi
টাইপ - যা Si
নির্ধারিত হবে তার একটি উপ-প্রকার নয় Ti
।
অতিরিক্তভাবে, ভাষাগুলির জন্য (যেমন স্কেলা বা সিলোন) যে জাতীয় বহুবিকতা পরামিতিগুলির (যেমন জেনেরিকস) সংজ্ঞা-সাইট ভেরিয়েন্স টিকা রয়েছে, প্রকারের প্রতিটি ধরণের প্যারামিটারের জন্য বৈকল্পিক বর্ণনার সহ-বা বিপরীত দিক T
অবশ্যই বিপরীত বা একই দিক হতে হবে যথাক্রমে প্রতিটি ইনপুট প্যারামিটার বা আউটপুট (প্রতিটি পদ্ধতির T
) যা টাইপ প্যারামিটারের ধরণ রয়েছে।
অতিরিক্তভাবে, প্রতিটি ইনপুট প্যারামিটার বা আউটপুট যাতে ফাংশনের ধরণ থাকে তার জন্য প্রয়োজনীয় বৈচিত্র্য দিকটি বিপরীত করা হয়। এই বিধি পুনরাবৃত্তির সাথে প্রয়োগ করা হয়।
আক্রমণকারীদের গণনা করা যায় সেখানে সাব টাইপিং উপযুক্ত ।
কীভাবে আক্রমণকারীদের মডেল করা যায় সে সম্পর্কে অনেক চলমান গবেষণা চলছে, যাতে তারা সংকলক দ্বারা প্রয়োগ করা হয়।
টাইপস্টেট (পৃষ্ঠা 3 দেখুন) টাইপ করার জন্য রাষ্ট্র আক্রমণকারীদের অরথোগোনাল ঘোষণা করে এবং প্রয়োগ করে। বিকল্পভাবে, আগ্রাসনগুলিকে জোর দিয়ে বিভিন্ন ধরণের রূপান্তর করে প্রয়োগ করা যেতে পারে । উদাহরণস্বরূপ, এটি বন্ধ করার আগে কোনও ফাইল খোলা আছে তা জোর দিয়ে, তারপরে ফাইল.ওপেন () একটি ওপেন ফাইলে টাইপ করতে পারে, যার মধ্যে একটি ক্লোজ () পদ্ধতি রয়েছে যা ফাইলটিতে নেই। একটি টিক-টাক-টো এপিআই হ'ল সংকলন সময়ে আক্রমণকারীদের প্রয়োগ করতে টাইপিং নিয়োগের আরেকটি উদাহরণ হতে পারে। টাইপ সিস্টেম এমনকি টুরিং-সম্পূর্ণ, যেমন হতে পারে Scala । নির্ভরশীলভাবে টাইপ করা ভাষা এবং উপপাদ্য প্রবাদগুলি উচ্চ-অর্ডার টাইপিংয়ের মডেলগুলিকে আনুষ্ঠানিক করে।
এক্সটেনশনের উপর দিয়ে বিমূর্তকরণের জন্য শব্দার্থবিজ্ঞানের প্রয়োজনীয়তার কারণে , আমি প্রত্যাশা করি যে মডেল ইনভেরেন্টস, অর্থাৎ ইউনিফাইড হাই-অর্ডার ডোনোটেশনাল শব্দার্থে টাইপিং নিয়োগ টাইপস্টেটের চেয়ে উচ্চতর। 'এক্সটেনশন' এর অর্থ সীমাহীন, নিয়ন্ত্রিত, মডিউলার বিকাশের আনউন্ডাউন্ডড, পারমিটেড কম্পোজিশন। যেহেতু এটি আমার কাছে একীকরণের বিরোধী এবং এইভাবে স্বাধীনতার ডিগ্রি বলে মনে হচ্ছে, ভাগ করা শব্দার্থক শব্দটির প্রকাশের জন্য দুটি পারস্পরিক নির্ভরশীল মডেল (যেমন প্রকার এবং টাইপস্টেট) থাকতে হবে, যা এক্সটেনসিবল কম্পোজিশনের জন্য একে অপরের সাথে একত্রিত হতে পারে না । উদাহরণস্বরূপ, এক্সপ্রেশন সমস্যা-জাতীয় এক্সটেনশানটি সাব টাইপিং, ফাংশন ওভারলোডিং এবং প্যারামেট্রিক টাইপিং ডোমেনগুলিতে একীভূত হয়েছিল।
আমার তাত্ত্বিক অবস্থানটি হ'ল জ্ঞানের অস্তিত্ব থাকার জন্য (বিভাগ "কেন্দ্রিয়করণ অন্ধ এবং অযোগ্য" দেখুন), এমন কোনও সাধারণ মডেল কখনই পাওয়া যাবে না যা টুরিং-সম্পূর্ণ কম্পিউটার ভাষায় সমস্ত সম্ভাব্য আক্রমণকারীদের 100% কভারেজ প্রয়োগ করতে পারে। জ্ঞানের অস্তিত্ব থাকার জন্য, অপ্রত্যাশিত সম্ভাবনাগুলি অনেক বেশি বিদ্যমান, অর্থাৎ ব্যাধি এবং এনট্রপি অবশ্যই সর্বদা বৃদ্ধি পাবে। এটি হ'ল এন্ট্রোপিক শক্তি। সম্ভাব্য এক্সটেনশনের সমস্ত সম্ভাব্য গণনা প্রমাণ করার জন্য, সমস্ত সম্ভাব্য এক্সটেনশনের একটি অগ্রাধিকার গণনা করা।
এই কারণেই হ্যালটিং উপপাদ্য বিদ্যমান, অর্থাত্ এটি ট্যুরিং-সম্পূর্ণ প্রোগ্রামিং ভাষার প্রতিটি প্রোগ্রাম শেষ করে কিনা তা অনস্বীকার্য। এটি প্রমাণিত হতে পারে যে কিছু নির্দিষ্ট প্রোগ্রামের সমাপ্তি ঘটে (এমন একটি যা সমস্ত সম্ভাবনা সংজ্ঞায়িত এবং গণনা করা হয়)। তবে প্রমাণ করা অসম্ভব যে এই প্রোগ্রামটির সমস্ত সম্ভাব্য বর্ধন বন্ধ হয়ে যায়, যদি না সেই প্রোগ্রামের সম্প্রসারণের সম্ভাবনাগুলি টুরিং সম্পূর্ণ না হয় (যেমন নির্ভরশীল টাইপিংয়ের মাধ্যমে)। টুরিং-সম্পূর্ণতার জন্য মৌলিক প্রয়োজনীয়তা যেহেতু সীমাহীন পুনরাবৃত্তি , তাই গডেলের অসম্পূর্ণতা উপপাদাগুলি এবং রাসেলের প্যারাডক্সটি কীভাবে বর্ধনের ক্ষেত্রে প্রযোজ্য তা বোঝা স্বজ্ঞাত।
এই উপপাদ্যগুলির একটি ব্যাখ্যা এন্ট্রপিক শক্তির একটি সাধারণ ধারণাগত বোঝার সাথে তাদের অন্তর্ভুক্ত করে:
আপনি লিসকভকে লঙ্ঘন করছেন কিনা তা নির্ধারণের জন্য একটি চেক তালিকা রয়েছে।
চেক তালিকা:
ইতিহাসের সীমাবদ্ধতা : কোনও পদ্ধতিকে ওভাররাইড করার সময় আপনাকে বেস শ্রেণিতে কোনও অ-পরিবর্তনযোগ্য সম্পত্তি পরিবর্তন করার অনুমতি দেওয়া হয় না। এই কোডটি একবার দেখুন এবং আপনি দেখতে পাচ্ছেন যে নামটি সংশোধনযোগ্য (ব্যক্তিগত সেট) হিসাবে সংজ্ঞায়িত করা হয়েছে তবে সাবটাইপ এমন একটি নতুন পদ্ধতি প্রবর্তন করে যা এটি (প্রতিবিম্বের মাধ্যমে) সংশোধন করার অনুমতি দেয়:
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 এবং রিটার্ন ধরনের কোভ্যারিয়েন্স । তবে এটি সি # তে সম্ভব নয় (আমি একটি সি # বিকাশকারী) তাই আমি তাদের সম্পর্কে যত্ন নেব না।
রেফারেন্স:
আমি প্রতিটি উত্তরে আয়তক্ষেত্র এবং স্কোয়ারগুলি দেখতে পাচ্ছি এবং কীভাবে এলএসপিকে লঙ্ঘন করব।
আমি কীভাবে এলএসপিকে বাস্তব-বিশ্বের উদাহরণের সাথে রূপান্তর করতে পারি তা দেখাতে চাই:
<?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;
}
}
এই নকশাটি এলএসপিকে সম্মতি জানায় কারণ ব্যবহারের জন্য আমরা যে প্রয়োগটি বেছে নিই তা আচরণটি অপরিবর্তিত রয়েছে।
এবং হ্যাঁ, আপনি এই কনফিগারেশনে এলএসপি লঙ্ঘন করতে পারেন এর মতো একটি সাধারণ পরিবর্তন করে:
<?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 !
}
}
এখন সাবটাইপগুলি একইভাবে ব্যবহার করা যাবে না কারণ তারা আর একই ফল প্রকাশ করে না।
Database::selectQuery
দ্বারা সমর্থিত এসকিউএল-এর কেবলমাত্র উপসেটটি সমর্থন করার শব্দার্থকে সীমাবদ্ধ করি । এটি কঠোরভাবে ব্যবহারিক ... এটি বলেছে যে, উদাহরণটি এখানে ব্যবহার করা অন্যদের তুলনায় এখনও সহজ sp
এলএসপি ক্লজগুলির চুক্তি সম্পর্কে একটি নিয়ম: যদি একটি বেস শ্রেণি একটি চুক্তি সন্তুষ্ট করে, তবে এলএসপি উত্পন্ন শ্রেণি দ্বারা অবশ্যই সেই চুক্তিটি সন্তুষ্ট করতে হবে।
সিউডো-পাইথনে
class Base:
def Foo(self, arg):
# *... do stuff*
class Derived(Base):
def Foo(self, arg):
# *... do stuff*
এলএসপিকে সন্তুষ্ট করে যদি প্রতিবার আপনি কোনও ডাইরাইভড অবজেক্টে ফোও কল করেন, এটি যতক্ষণ আর্গ একই থাকে ততক্ষণ কোনও বেস অবজেক্টে ফু কে কল করার মতো একই ফলাফল দেয়।
2 + "2"
)। সম্ভবত আপনি "স্ট্যাটিকালি টাইপড" দিয়ে "দৃ strongly় টাইপড" কে বিভ্রান্ত করছেন?
দীর্ঘ গল্প সংক্ষিপ্ত, আসুন আয়তক্ষেত্রগুলি এবং স্কোয়ার স্কোয়ারগুলি ছেড়ে দেওয়া যাক, পিতামাতার ক্লাস প্রসারিত করার সময় ব্যবহারিক উদাহরণ, আপনাকে হয় সঠিক প্যারেন্ট এপিআই প্রেরণ করতে হবে বা এটি প্রসারিত করতে হবে।
ধরা যাক আপনার একটি বেস আইটেমস রিপোসিটোরি রয়েছে।
class ItemsRepository
{
/**
* @return int Returns number of deleted rows
*/
public function delete()
{
// perform a delete query
$numberOfDeletedRows = 10;
return $numberOfDeletedRows;
}
}
এবং একটি উপশ্রেণী এটি প্রসারিত করছে:
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 যখন নষ্ট হয়ে গেছে বদলে পিতা বা মাতা একটি সঙ্গে বর্গ সাব বর্গ বিরতি এপিআই চুক্তির ।
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 :(
}
}
আপনি আমার কোর্সে রক্ষণাবেক্ষণযোগ্য সফ্টওয়্যার লেখার বিষয়ে আরও শিখতে পারেন: https://www.udemy.com/enterprise-php/
বেস ক্লাসে পয়েন্টার বা রেফারেন্স ব্যবহার করে এমন ফাংশনগুলি অজান্তেই উত্পন্ন ক্লাসের অবজেক্টগুলি ব্যবহার করতে সক্ষম হতে হবে।
আমি যখন প্রথম এলএসপি সম্পর্কে পড়েছিলাম, আমি ধরে নিয়েছিলাম যে এটি খুব কঠোর অর্থে বোঝানো হয়েছিল, এটি মূলত ইন্টারফেস বাস্তবায়ন এবং টাইপ-নিরাপদ ingালাইয়ের সাথে সমান করে। যার অর্থ হ'ল এলএসপি হয় ভাষা দ্বারা নিশ্চিত করা হয় না। উদাহরণস্বরূপ, এই কঠোর অর্থে থ্রিডিবোর্ড অবশ্যই বোর্ডের জন্য বিকল্প হিসাবে যথাযথ, যতই সংকলকটি উদ্বিগ্ন।
ধারণাটি আরও পড়ার পরে যদিও আমি দেখতে পেলাম যে এলএসপি সাধারণত তার চেয়ে বেশি বিস্তৃতভাবে ব্যাখ্যা করা হয়।
সংক্ষেপে, ক্লায়েন্ট কোডটির "জেনে" বোঝার জন্য এর অর্থ কী যে পয়েন্টারের পিছনে থাকা অবজেক্টটি পয়েন্টার টাইপের পরিবর্তে ডাইরাইটেড টাইপের হয় যা টাইপ-সুরক্ষার মধ্যে সীমাবদ্ধ নয়। এলএসপির আনুগত্যটি বস্তুর প্রকৃত আচরণের অনুসন্ধানের মাধ্যমেও পরীক্ষামূলক। অর্থাত, পদ্ধতির কলগুলির ফলাফলগুলিতে কোনও অবজেক্টের অবস্থা এবং পদ্ধতির যুক্তিগুলির প্রভাব পরীক্ষা করা, বা বস্তু থেকে ফেলে দেওয়া ব্যতিক্রমগুলির প্রকারগুলি পরীক্ষা করা।
আবার উদাহরণের দিকে ফিরে যাওয়া, তত্ত্বে বোর্ড পদ্ধতিগুলিকে থ্রিডিবোর্ডে ঠিকঠাক কাজ করার জন্য তৈরি করা যেতে পারে। তবে অনুশীলনে, থ্রিডিবোর্ড যুক্ত করার উদ্দেশ্যে যে কার্যকারিতাটি ক্লিচটি সঠিকভাবে পরিচালনা করতে পারে না তার আচরণের পার্থক্য প্রতিরোধ করা খুব কঠিন হবে client
এই জ্ঞানটি হাতে রেখে, এলএসপি আনুগত্যের মূল্যায়ন করা উত্তরাধিকারের পরিবর্তে বিদ্যমান কার্যকারিতা বাড়ানোর জন্য আরও উপযুক্ত পদ্ধতি যখন নির্ধারণ করা যায় তখন তা নির্ধারণে দুর্দান্ত সরঞ্জাম হতে পারে।
প্রযুক্তিবিদগতভাবে এলএসপিতে যা রয়েছে তা প্রত্যেকেরই আমি কভার করেছি বলে অনুমান করি: আপনি মূলত সাব টাইপের বিবরণ থেকে দূরে থাকতে এবং নিরাপদে সুপারটাইপগুলি নিরাপদে ব্যবহার করতে সক্ষম হতে চান।
সুতরাং লিসকভের 3 টি অন্তর্নিহিত নিয়ম রয়েছে:
স্বাক্ষর বিধি: সাব টাইপের সিনট্যাক্টিক্যালি সুপার টাইপের প্রতিটি ক্রিয়াকলাপের বৈধ প্রয়োগ হওয়া উচিত। কিছু সংকলক আপনার জন্য যাচাই করতে সক্ষম হবে। কম ব্যতিক্রম ছোঁড়া এবং সুপারটাইপ পদ্ধতিগুলির মতো কমপক্ষে অ্যাক্সেসযোগ্য হওয়া সম্পর্কে একটি সামান্য নিয়ম আছে।
পদ্ধতির বিধি: এই ক্রিয়াকলাপগুলির বাস্তবায়ন শব্দার্থগতভাবে দৃ is়।
প্রোপার্টি বিধি: এটি পৃথক ফাংশন কলের বাইরে goes
এই সমস্ত বৈশিষ্ট্য সংরক্ষণ করা দরকার এবং অতিরিক্ত সাব টাইপ কার্যকারিতা সুপারটাইপ বৈশিষ্ট্য লঙ্ঘন করা উচিত নয়।
যদি এই তিনটি বিষয় যত্ন নেওয়া হয় তবে আপনি অন্তর্নিহিত জিনিসগুলি থেকে সরে এসেছেন এবং আপনি আলগাভাবে সংযুক্ত কোডটি লিখছেন।
সূত্র: জাভাতে প্রোগ্রাম ডেভলপমেন্ট - বারবারা লিসকভ
এলএসপি ব্যবহারের একটি গুরুত্বপূর্ণ উদাহরণটি সফ্টওয়্যার পরীক্ষায় ।
যদি আমার A এর একটি ক্লাস থাকে যা B এর LSP- অনুবর্তী সাবক্লাস হয়, তবে আমি B এর পরীক্ষার স্যুটটি A এর সাথে পুনরায় ব্যবহার করতে পারি A.
সাবক্লাস এ পুরোপুরি পরীক্ষা করার জন্য, আমার সম্ভবত আরও কয়েকটি পরীক্ষার কেস যুক্ত করা দরকার, তবে সর্বনিম্ন আমি সুপারক্লাস বি এর সমস্ত পরীক্ষার ক্ষেত্রে পুনরায় ব্যবহার করতে পারি।
ম্যাকগ্রিগর যাকে "পরীক্ষার জন্য সমান্তরাল শ্রেণিবিন্যাস" বলে ডাকে তা বোঝার একটি উপায়: আমার ATest
ক্লাসটি উত্তরাধিকার সূত্রে প্রাপ্ত হবে BTest
। ইনজেকশনের কিছু ফর্মের পরে পরীক্ষার কেস বি টাইপের পরিবর্তে টাইপ এ এর বস্তুর সাথে কাজ করে তা নিশ্চিত করার জন্য প্রয়োজনীয় (একটি সাধারণ টেম্পলেট পদ্ধতি প্যাটার্নটি করবে)।
নোট করুন যে সমস্ত সাবক্লাস বাস্তবায়নের জন্য সুপার-টেস্ট স্যুটটিকে পুনরায় ব্যবহার করা আসলে এই পরীক্ষার একটি উপায় যা এই সাবক্লাস বাস্তবায়নগুলি এলএসপি-সম্মতিযুক্ত। সুতরাং, কেউ তর্ক করতে পারে যে যে কোনও একটি subclass প্রসঙ্গে সুপারক্লাস পরীক্ষা স্যুট চালানো উচিত ।
স্ট্যাকওভারফ্লো প্রশ্নের উত্তরটিও দেখুন " আমি কোনও ইন্টারফেসের বাস্তবায়ন পরীক্ষা করতে পুনরায় পুনরায় ব্যবহারযোগ্য পরীক্ষাগুলি প্রয়োগ করতে পারি? "
আসুন জাভাতে চিত্রিত করুন:
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!*/
}
সবকিছু এখন পরিকল্পনা মতো চলছে না! হ্যাঁ, একটি সাইকেলটি একটি পরিবহন ডিভাইস, তবে এর কোনও ইঞ্জিন নেই এবং তাই, স্টার্টইঙ্গিন () পদ্ধতিটি প্রয়োগ করা যায় না।
লিসকভ সাবস্টিটিউশন নীতি লঙ্ঘনের ফলে এ ধরণের সমস্যা দেখা দেয় এবং এগুলি সাধারণত এমন কোনও পদ্ধতি দ্বারা স্বীকৃত হতে পারে যা কিছুই করে না, এমনকি বাস্তবায়নও করা যায় না।
এই সমস্যার সমাধান হ'ল উত্তরাধিকারের শ্রেণিবিন্যাস, এবং আমাদের ক্ষেত্রে আমরা ইঞ্জিনের সাথে এবং তার ছাড়াই পরিবহন ডিভাইসের ক্লাসকে আলাদা করে সমস্যার সমাধান করব। সাইকেলটি একটি পরিবহন যন্ত্র হলেও এটিতে ইঞ্জিন নেই। এই উদাহরণে পরিবহন ডিভাইসের আমাদের সংজ্ঞাটি ভুল। এটিতে ইঞ্জিন থাকা উচিত নয়।
আমরা আমাদের ট্রান্সপোর্টেশন ডিভাইস ক্লাসটি নিম্নরূপ রিফ্যাক্টর করতে পারি:
class TrasportationDevice
{
String name;
String getName() { ... }
void setName(String n) { ... }
double speed;
double getSpeed() { ... }
void setSpeed(double d) { ... }
}
এখন আমরা নন-মোটরযুক্ত ডিভাইসগুলির জন্য পরিবহন যন্ত্রটি প্রসারিত করতে পারি।
class DevicesWithoutEngines extends TransportationDevice
{
void startMoving() { ... }
}
এবং মোটরযুক্ত ডিভাইসগুলির জন্য পরিবহন ডিভাইস প্রসারিত করুন। ইঞ্জিন অবজেক্ট যুক্ত করার জন্য এখানে আরও উপযুক্ত।
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() { ... }
}
এলএসপির এই সূত্রটি বেশ শক্তিশালী:
টাইপ এস এর প্রতিটি বস্তুর ও 1 এর জন্য টি টাইপ টির একটি অবজেক্ট ও 2 থাকে যে সমস্ত প্রোগ্রামের জন্য টি এর শর্তাবলী পি ডি-এনড হয়, যখন ও 1 টি ও 2 এর পরিবর্তে পি এর আচরণটি অপরিবর্তিত থাকে তবে এস টি টির একটি উপ-টাইপ হয় is
যার মূলত এসটি হ'ল অন্যটি, টি হিসাবে ঠিক একই জিনিসটির সম্পূর্ণ প্রয়োগকে বাস্তবায়িত করে And
সুতরাং, মূলত, দেরি-বাঁধাইয়ের যে কোনও ব্যবহার এলএসপিকে লঙ্ঘন করে। যখন আমরা এক প্রকারের কোনও জিনিসের জন্য অন্য ধরণের একেকটির বিকল্প রাখি তখন ভিন্ন আচরণ অর্জন করা ওওর পুরো বিষয়!
উইকিপিডিয়া দ্বারা উদ্ধৃত সূত্রটি আরও ভাল কারণ সম্পত্তি প্রসঙ্গে নির্ভর করে এবং প্রোগ্রামের পুরো আচরণটি অগত্যা অন্তর্ভুক্ত করে না।
খুব সাধারণ বাক্যে আমরা বলতে পারি:
শিশু শ্রেণী অবশ্যই এর বেস শ্রেণির বৈশিষ্ট্যগুলি লঙ্ঘন করবে না। এটি অবশ্যই এটির সাথে সক্ষম হতে হবে। আমরা এটি সাব টাইপিংয়ের মতোই বলতে পারি।
লিসকভের সাবস্টিটিউশন নীতি (এলএসপি)
সারাক্ষণ আমরা একটি প্রোগ্রাম মডিউল ডিজাইন করি এবং আমরা কিছু শ্রেণি শ্রেণিবিন্যাস তৈরি করি। তারপরে আমরা কিছু উত্পন্ন ক্লাস তৈরি করে কিছু শ্রেণি প্রসারিত করি।
আমাদের অবশ্যই নিশ্চিত করতে হবে যে নতুন উত্পন্ন ক্লাসগুলি কেবল পুরানো শ্রেণীর কার্যকারিতা প্রতিস্থাপন না করে প্রসারিত হবে। অন্যথায়, নতুন ক্লাসগুলি বিদ্যমান প্রোগ্রাম মডিউলগুলিতে ব্যবহৃত হলে অনাকাঙ্ক্ষিত প্রভাব তৈরি করতে পারে।
লিসকভের সাবস্টিটিউশন নীতিতে বলা হয়েছে যে কোনও প্রোগ্রাম মডিউল যদি বেস ক্লাস ব্যবহার করে তবে প্রোগ্রাম মডিউলের কার্যকারিতা প্রভাবিত না করে বেস ক্লাসের রেফারেন্সটি ডেরাইভড ক্লাসের সাথে প্রতিস্থাপন করা যেতে পারে।
উদাহরণ:
নীচে ক্লাসিক উদাহরণ দেওয়া আছে যার জন্য লিসকোভের সাবস্টিটিউশন নীতি লঙ্ঘন করা হয়েছে। উদাহরণস্বরূপ, 2 টি ক্লাস ব্যবহৃত হয়: আয়তক্ষেত্র এবং স্কোয়ার। আসুন ধরে নেওয়া যাক রেক্টেঙ্গেল অবজেক্টটি অ্যাপ্লিকেশনটির কোথাও ব্যবহৃত হয়েছে। আমরা অ্যাপ্লিকেশনটি প্রসারিত করি এবং স্কোয়ার শ্রেণি যুক্ত করি। কিছু শর্তের উপর ভিত্তি করে বর্গাকার শ্রেণিটি কারখানার প্যাটার্ন দ্বারা ফিরে আসে এবং কোন ধরণের অবজেক্টটি ফিরে আসবে তা সঠিকভাবে আমরা জানি না। তবে আমরা জানি এটি একটি আয়তক্ষেত্র। আমরা আয়তক্ষেত্রের অবজেক্টটি পাই, প্রস্থটি 5 এবং উচ্চতায় 10 এ সেট করব এবং অঞ্চলটি পাই। প্রস্থ 5 এবং উচ্চতা 10 সহ একটি আয়তক্ষেত্রের জন্য ক্ষেত্রফল 50 হওয়া উচিত Instead পরিবর্তে, ফলাফলটি 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.
}
}
উপসংহার:
এই নীতিটি ওপেন ক্লোজড নীতিমালার কেবলমাত্র একটি এক্সটেনশান এবং এর অর্থ হল যে আমাদের অবশ্যই নিশ্চিত হওয়া উচিত যে নতুন উত্পন্ন শ্রেণিগুলি তাদের আচরণ পরিবর্তন না করে বেস ক্লাসগুলি বাড়িয়ে দিচ্ছে।
আরও দেখুন: ওপেন ক্লোজ নীতি
আরও ভাল কাঠামোর জন্য অনুরূপ কিছু ধারণা: কনফিগারেশন ওপরে কনভেনশন
কিছু সংযোজন:
আমি অবাক হই যে কেন বেসর শ্রেণীর ইনভেরেন্ট, পূর্বশর্ত এবং পোস্ট শর্তাদি সম্পর্কে কেউ কেন লিখেনি? বেস বর্গ বি দ্বারা সম্পূর্ণভাবে টেকসইযোগ্য হওয়ার জন্য ডিগ্রিযুক্ত ক্লাস ডি এর জন্য, ক্লাস ডি অবশ্যই কিছু শর্ত মেনে চলতে হবে:
সুতরাং প্রাপ্ত উত্সকে অবশ্যই বেস শ্রেণীর দ্বারা আরোপিত উপরের তিনটি শর্ত সম্পর্কে সচেতন হতে হবে। অতএব, সাব টাইপিংয়ের নিয়মগুলি পূর্ব নির্ধারিত। যার অর্থ, 'আইএস এ' সম্পর্ক কেবল তখনই মেনে নেওয়া হবে যখন নির্দিষ্ট নিয়মগুলি সাব টাইপ অনুসারে মেনে চলবে। এই নিয়মগুলি, আক্রমণকারী, পূর্বের শর্ত এবং পোস্টকন্ডিশনের আকারে একটি আনুষ্ঠানিক ' ডিজাইন চুক্তি ' দ্বারা সিদ্ধান্ত নেওয়া উচিত ।
আমার ব্লগে এই সম্পর্কিত আরও আলোচনা: লিসকভ সাবস্টিটিউশন নীতি
সহজ পদ LSP রাজ্যের যে একই সুপারক্লাস অবজেক্ট সক্ষম হওয়া উচিত হবে অদলবদল কিছু ভঙ্গ ছাড়া একে অপরের সাথে।
উদাহরণস্বরূপ, যদি আমাদের কাছে ক্লাস থেকে উদ্ভূত একটি Cat
এবং একটি Dog
ক্লাস থাকে Animal
তবে প্রাণী শ্রেণীর ব্যবহার করে যে কোনও ফাংশন ব্যবহার করতে Cat
বা Dog
স্বাভাবিকভাবে আচরণ করতে সক্ষম হতে হবে be
বোর্ডের অ্যারের শর্তে থ্রিডিবোর্ড কার্যকর করা কি কার্যকর হবে?
সম্ভবত আপনি বোর্ড হিসাবে বিভিন্ন প্লেনে থ্রিডিবোর্ডের স্লাইসগুলি আচরণ করতে চাইতে পারেন। সেক্ষেত্রে আপনি বোর্ডের একাধিক বাস্তবায়নের অনুমতি দেওয়ার জন্য একটি ইন্টারফেস (বা বিমূর্ত শ্রেণি) বিমূর্ত করতে চাইতে পারেন।
বাহ্যিক ইন্টারফেসের ক্ষেত্রে, আপনি টুডিবোর্ড এবং থ্রিডিবোর্ড উভয়ের জন্য বোর্ড ইন্টারফেস তৈরি করতে চাইতে পারেন (যদিও উপরের পদ্ধতির কোনওটিই মাপসই নয়)।
একটি বর্গক্ষেত্র একটি আয়তক্ষেত্র যেখানে প্রস্থটি দৈর্ঘ্যের সমান হয়। বর্গক্ষেত্রটি প্রস্থ এবং উচ্চতার জন্য দুটি পৃথক আকার নির্ধারণ করে তবে এটি বর্গাকার আক্রমণকারীকে লঙ্ঘন করে। পার্শ্ব প্রতিক্রিয়া প্রবর্তন করে এটি প্রায় কাজ করা হয়। তবে আয়তক্ষেত্রটি পূর্ব শর্ত 0 <উচ্চতা এবং 0 <প্রস্থ সহ একটি সেটসাইজ (উচ্চতা, প্রস্থ) থাকে। উত্সযুক্ত উপপ্রকার পদ্ধতিটির উচ্চতা == প্রস্থ প্রয়োজন; একটি শক্তিশালী পূর্বশর্ত (এবং এটি lsp লঙ্ঘন করে)। এটি দেখায় যে বর্গক্ষেত্রটি আয়তক্ষেত্র হলেও এটি বৈধ উপপ্রকার নয় কারণ পূর্ব শর্তটি শক্তিশালী। চারপাশের কাজগুলি (সাধারণভাবে একটি খারাপ জিনিস) একটি পার্শ্ব প্রতিক্রিয়া সৃষ্টি করে এবং এটি পোস্টের অবস্থাকে দুর্বল করে (যা এলএসপি লঙ্ঘন করে)। বেসে সেটউইথের পোস্ট শর্ত 0 <প্রস্থ রয়েছে। উত্পন্ন এটি উচ্চতা == প্রস্থের সাথে দুর্বল করে।
অতএব একটি আকার পরিবর্তনযোগ্য বর্গক্ষেত্র কোন আকার পরিবর্তনযোগ্য আয়তক্ষেত্র নয়।
এই নীতিটি বারবারা লিসকভ 1987 সালে প্রবর্তন করেছিলেন এবং একটি সুপারক্লাসের আচরণ এবং এর উপ-প্রকারের প্রতি মনোনিবেশ করে ওপেন-ক্লোজড নীতিটি প্রসারিত করে।
যখন আমরা এটি লঙ্ঘনের পরিণতি বিবেচনা করি তখন এর গুরুত্বটি সুস্পষ্ট হয়ে যায়। নিম্নলিখিত শ্রেণীর ব্যবহার করে এমন একটি অ্যাপ্লিকেশন বিবেচনা করুন।
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
}
যদি আমরা এই ফাংশনে কোনও বর্গক্ষেত্রের কোনও রেফারেন্স পাস করি তবে আমরা LSP লঙ্ঘন করব কারণ ফাংশনটি তার আর্গুমেন্টগুলির ডেরিভেটিভসের জন্য কাজ করে না। বৈশিষ্ট্যগুলির প্রস্থ এবং উচ্চতা বহুতল নয় কারণ এগুলি আয়তক্ষেত্রগুলিতে ভার্চুয়াল হিসাবে ঘোষিত হয়নি (বর্গক্ষেত্রটি দূষিত হবে কারণ উচ্চতা পরিবর্তন হবে না)।
তবে সেটার বৈশিষ্ট্যগুলিকে ভার্চুয়াল হিসাবে ঘোষণা করে আমরা আরেকটি লঙ্ঘনের মুখোমুখি হব, ওসিপি। প্রকৃতপক্ষে, একটি উত্পন্ন শ্রেণিবদ্ধ বর্গক্ষেত্র তৈরি বেস শ্রেণীর আয়তক্ষেত্রের পরিবর্তন ঘটায়।
আমি এখনও অবধি এলএসপির পক্ষে সবচেয়ে স্পষ্ট ব্যাখ্যা পেয়েছি "লিসকভ সাবস্টিটিউশন নীতিমালায় বলা হয়েছে যে উদ্ভূত শ্রেণীর অবজেক্টটি সিস্টেমের কোনও ত্রুটি না নিয়ে বা বেস শ্রেণীর আচরণ পরিবর্তন না করেই বেস শ্রেণীর কোনও বিষয় প্রতিস্থাপন করতে সক্ষম হওয়া উচিত "থেকে এখানে । নিবন্ধটি এলএসপি লঙ্ঘন এবং এটি ঠিক করার জন্য কোড উদাহরণ দেয়।
ধরা যাক আমরা আমাদের কোডে একটি আয়তক্ষেত্র ব্যবহার করি
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
এবং তাই এটি আপনার কোডে অপ্রত্যাশিত ত্রুটি ঘটায়।
এই জন্য একটি উদাহরণ ছিল "পূর্বশর্ত একটি উপপ্রকার জোরদার যাবে না" অংশগ্রহণ উইকি নিবন্ধে । সুতরাং সংক্ষেপে, LSP লঙ্ঘন সম্ভবত কোনও সময়ে আপনার কোডে ত্রুটি ঘটবে।
এলএসপি বলে যে '' অবজেক্টগুলি তাদের সাব টাইপগুলি দ্বারা প্রতিস্থাপনযোগ্য হওয়া উচিত ''। অন্যদিকে, এই নীতিটি নির্দেশ করে
চাইল্ড ক্লাসগুলি কখনই পিতামাতার শ্রেণীর ধরণের সংজ্ঞা ভঙ্গ করা উচিত নয়।
এবং নিম্নলিখিত উদাহরণটি এলএসপির আরও ভাল ধারণা পেতে সহায়তা করে।
এলএসপি ছাড়াই:
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();
}
আমি আপনাকে নিবন্ধটি পড়তে উত্সাহিত করছি: লসকোভ সাবস্টিটিউশন নীতিমালা (এলএসপি) লঙ্ঘন করা ।
লিসকভ সাবস্টিটিউশন নীতিমালা কী, আপনি সেখানে একটি ব্যাখ্যা খুঁজে পেতে পারেন, সাধারণ ক্লুগুলি আপনাকে অনুমান করতে সহায়তা করে যে আপনি ইতিমধ্যে এটি লঙ্ঘন করেছেন এবং এমন একটি পদ্ধতির উদাহরণ যা আপনাকে শ্রেণীর শ্রেণিবদ্ধতাকে আরও সুরক্ষিত করতে সহায়তা করবে।
লিসকভ সাবস্টিটিউশন প্রিন্সিপল (মার্ক সিমেন বই থেকে) বলেছে যে আমাদের কোনও ইন্টারফেসের একটি প্রয়োগকে অন্য কোনও ক্লায়েন্ট বা বাস্তবায়ন না ভেঙে প্রতিস্থাপন করতে সক্ষম হওয়া উচিত। এটি এই নীতিটি যা ভবিষ্যতে ঘটে যাওয়া প্রয়োজনীয়তাগুলি সমাধান করতে সক্ষম করে, এমনকি আমরা যদি পারি ' আজ তাদের পূর্বেই আশা করি।
আমরা যদি কম্পিউটারটি প্রাচীর (প্রয়োগ) থেকে আনপ্লাগ করি, তবে প্রাচীরের আউটলেট (ইন্টারফেস) বা কম্পিউটার (ক্লায়েন্ট) বিচ্ছিন্ন হয় না (আসলে এটি যদি ল্যাপটপের কম্পিউটার হয় তবে এটি তার ব্যাটারিতেও কিছু সময়ের জন্য চালাতে পারে) । সফ্টওয়্যার সহ, তবে ক্লায়েন্ট প্রায়শই কোনও পরিষেবা উপলব্ধ হওয়ার প্রত্যাশা করে। যদি পরিষেবাটি সরিয়ে ফেলা হয় তবে আমরা একটি নালরফেরান এক্সেক্সেশন পাই। এই ধরণের পরিস্থিতি মোকাবেলা করার জন্য, আমরা একটি ইন্টারফেসের একটি বাস্তবায়ন তৈরি করতে পারি যা "কিছুই না" করে। এটি নুল অবজেক্ট নামে পরিচিত একটি নকশার প্যাটার্ন, [৪] এবং এটি দেওয়াল থেকে কম্পিউটারকে আনপ্লাগ করার সাথে মোটামুটি মিলিয়ে যায়। যেহেতু আমরা আলগা দম্পতি ব্যবহার করছি, আমরা এমন বাস্তবের সাথে বাস্তব বাস্তবায়ন প্রতিস্থাপন করতে পারি যা সমস্যার কারণ না করে কিছুই করে না।
লিকভের সাবস্টিটিউশন নীতিতে বলা হয়েছে যে কোনও প্রোগ্রাম মডিউল যদি বেস ক্লাস ব্যবহার করে তবে প্রোগ্রাম মডিউলের কার্যকারিতা প্রভাবিত না করে বেস ক্লাসের রেফারেন্সটি ডেরাইভড শ্রেণীর সাথে প্রতিস্থাপন করা যেতে পারে।
অভিপ্রায় - উত্পন্ন প্রকারগুলি অবশ্যই তাদের বেস ধরণের জন্য সম্পূর্ণ বিকল্প সক্ষম হতে হবে।
উদাহরণ - জাভাতে সহ-বৈকল্পিক রিটার্নের প্রকারগুলি।
এই পোস্টের একটি অংশ এখানে সুন্দরভাবে বিষয়গুলি পরিষ্কার করে:
[..] কিছু নীতি বোঝার জন্য, কখন এটি লঙ্ঘন করা হয়েছে তা উপলব্ধি করা গুরুত্বপূর্ণ important এই আমি এখন কি করব।
এই নীতি লঙ্ঘন মানে কি? এটি বোঝায় যে কোনও বস্তু কোনও ইন্টারফেসের মাধ্যমে প্রকাশিত বিমূর্ততার দ্বারা আরোপিত চুক্তিটি পূরণ করে না। অন্য কথায়, এর অর্থ হ'ল আপনি নিজের বিমূর্ততা ভুল চিহ্নিত করেছেন।
নিম্নলিখিত উদাহরণ বিবেচনা করুন:
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);
}
}
এটি কি এলএসপির লঙ্ঘন? হ্যাঁ. এটি কারণ অ্যাকাউন্টের চুক্তিটি আমাদের বলে যে কোনও অ্যাকাউন্ট প্রত্যাহার করা হবে, তবে এটি সর্বদা ক্ষেত্রে হয় না। সুতরাং, এটি ঠিক করার জন্য আমার কী করা উচিত? আমি কেবল চুক্তিটি সংশোধন করেছি:
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);
}
ভাল, এখন চুক্তি সন্তুষ্ট।
এই সূক্ষ্ম লঙ্ঘন প্রায়শই নিযুক্ত কংক্রিটের মধ্যে পার্থক্য বলার ক্ষমতা সহ একটি ক্লায়েন্টকে চাপিয়ে দেয়। উদাহরণস্বরূপ, প্রথম অ্যাকাউন্টের চুক্তি দেওয়া, এটি নিম্নলিখিতগুলির মতো দেখাতে পারে:
class Client
{
public function go(Account $account, Money $money)
{
if ($account instanceof DefaultAccount && !$account->hasEnoughMoney($money)) {
return;
}
$account->withdraw($money);
}
}
এবং, এটি স্বয়ংক্রিয়ভাবে মুক্ত-বদ্ধ নীতি লঙ্ঘন করে [অর্থাত্ অর্থ প্রত্যাহারের প্রয়োজনীয়তার জন্য। কারণ আপনি কখনই জানেন না যে চুক্তি লঙ্ঘনকারী কোনও জিনিসের পর্যাপ্ত অর্থ না থাকলে কী হয়। সম্ভবত এটি কিছুই দেয় না, সম্ভবত একটি ব্যতিক্রম ছুঁড়ে দেওয়া হবে। সুতরাং আপনাকে এটি পরীক্ষা করতে হবে hasEnoughMoney()
- এটি কোনও ইন্টারফেসের অংশ নয়। সুতরাং এই বাধ্যতামূলক কংক্রিট-শ্রেণি-নির্ভর চেকটি একটি ওসিপি লঙ্ঘন]।
এই পয়েন্টটি এমন একটি ভুল ধারণাও সম্বোধন করে যা আমি প্রায়শই এলএসপি লঙ্ঘনের বিষয়ে মুখোমুখি হই। এটি বলেছে যে "যদি কোনও সন্তানের মধ্যে পিতামাতার আচরণ পরিবর্তন হয় তবে তা এলএসপি লঙ্ঘন করে।" তবে, এটি তা নয় - যতক্ষণ না কোনও শিশু তার পিতামাতার চুক্তি লঙ্ঘন করে না।