আপনি কি লিসকোব সাবস্টিটিউশন নীতিটি (SOLID এর 'এল') একটি ভাল সি # উদাহরণ দিয়ে ব্যাখ্যা করতে পারবেন যে নীতিটির সমস্ত দিককে সরলীকৃত উপায়ে আবৃত করা হয়েছে? যদি সত্যিই সম্ভব হয়।
আপনি কি লিসকোব সাবস্টিটিউশন নীতিটি (SOLID এর 'এল') একটি ভাল সি # উদাহরণ দিয়ে ব্যাখ্যা করতে পারবেন যে নীতিটির সমস্ত দিককে সরলীকৃত উপায়ে আবৃত করা হয়েছে? যদি সত্যিই সম্ভব হয়।
উত্তর:
(এই উত্তরটি 2013-05-13 এ আবার লেখা হয়েছে, মন্তব্যগুলির নীচে আলোচনাটি পড়ুন)
এলএসপি বেস ক্লাসের চুক্তি অনুসরণ করতে চলেছে।
উদাহরণস্বরূপ আপনি সাব ক্লাসে নতুন ব্যতিক্রম ছুঁড়ে ফেলতে পারবেন না কারণ বেস ক্লাসটি ব্যবহার করে এমনটি আশা করে না। ArgumentNullException
যদি কোনও আর্গুমেন্ট অনুপস্থিত থাকে এবং উপ-শ্রেণিটি যুক্তিটি বাতিল করতে দেয়, তবে একটি এলএসপি লঙ্ঘনও যদি বেস ক্লাসটি ছুড়ে দেয় তবে একই কাজটি ঘটে।
এখানে এলএসপি লঙ্ঘনকারী শ্রেণীর কাঠামোর উদাহরণ রয়েছে:
public interface IDuck
{
void Swim();
// contract says that IsSwimming should be true if Swim has been called.
bool IsSwimming { get; }
}
public class OrganicDuck : IDuck
{
public void Swim()
{
//do something to swim
}
bool IsSwimming { get { /* return if the duck is swimming */ } }
}
public class ElectricDuck : IDuck
{
bool _isSwimming;
public void Swim()
{
if (!IsTurnedOn)
return;
_isSwimming = true;
//swim logic
}
bool IsSwimming { get { return _isSwimming; } }
}
এবং কলিং কোড
void MakeDuckSwim(IDuck duck)
{
duck.Swim();
}
আপনি দেখতে পাচ্ছেন, হাঁসের দুটি উদাহরণ রয়েছে। একটি জৈব হাঁস এবং একটি বৈদ্যুতিক হাঁস। বৈদ্যুতিন হাঁসটি কেবল চালু থাকলেই সাঁতার কাটতে পারে। এটি এলএসপি নীতিটি ভঙ্গ করে যেহেতু সাঁতার কাটাতে সক্ষম হতে হবে IsSwimming
( যেটিও চুক্তির অংশ) বেস ক্লাসের মতো সেট করা হবে না।
আপনি অবশ্যই এটির মতো কিছু করে সমাধান করতে পারেন
void MakeDuckSwim(IDuck duck)
{
if (duck is ElectricDuck)
((ElectricDuck)duck).TurnOn();
duck.Swim();
}
তবে এটি ওপেন / ক্লোজড নীতিটি ভঙ্গ করবে এবং সর্বত্র প্রয়োগ করতে হবে (এবং তবুও অস্থির কোড উত্পন্ন হয়)।
সঠিক সমাধানটি হ'ল Swim
পদ্ধতিতে হাঁসটিকে স্বয়ংক্রিয়ভাবে চালু করা এবং এটি করে বৈদ্যুতিন হাঁসটিকে IDuck
ইন্টারফেসের দ্বারা নির্ধারিত ঠিক মতো আচরণ করা
হালনাগাদ
কেউ একটি মন্তব্য যোগ করেছেন এবং এটি সরান। এটির একটি বৈধ পয়েন্ট ছিল যা আমি সম্বোধন করতে চাই:
Swim
প্রকৃত বাস্তবায়ন ( ElectricDuck
) এর সাথে কাজ করার সময় পদ্ধতির অভ্যন্তরে হাঁসকে ঘুরিয়ে দেওয়ার সমাধানের পার্শ্ব প্রতিক্রিয়া হতে পারে । তবে এটি একটি সুস্পষ্ট ইন্টারফেস বাস্তবায়ন ব্যবহার করে সমাধান করা যেতে পারে । imho সম্ভবত Swim
এটি IDuck
ইন্টারফেসটি ব্যবহার করার সময় এটি সাঁতার কাটবে বলে আশা করা হচ্ছে যেহেতু এটি চালু না করে আপনি সমস্যা পেতে পারেন
আপডেট 2
আরও স্পষ্ট করতে কিছু অংশ পুনরায় চাপিয়েছেন।
if duck is ElectricDuck
অংশটি উল্লেখ করছেন তবে আমার উত্তরটি আবার পড়ুন । আমি গত বৃহস্পতিবার
as
কীওয়ার্ড সম্পর্কে অবগত নন , যা আসলে তাদের প্রচুর টাইপ-চেকিং থেকে বাঁচায়। আমি নিম্নলিখিতগুলির মতো কিছু ভাবছি:if var electricDuck = duck as ElectricDuck; if(electricDuck != null) electricDuck.TurnOn();
if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program (e.g., correctness).
এলএসপি একটি ব্যবহারিক পদ্ধতির
আমি যেখানেই এলএসপির সি # উদাহরণ সন্ধান করেছি, লোকেরা কাল্পনিক ক্লাস এবং ইন্টারফেস ব্যবহার করেছে। আমি আমাদের সিস্টেমে একটিতে প্রয়োগ করেছি এমন এলএসপির ব্যবহারিক বাস্তবায়ন এখানে।
পরিস্থিতি: ধরুন আমাদের কাছে 3 টি ডাটাবেস রয়েছে (বন্ধকী গ্রাহকগণ, কারেন্ট অ্যাকাউন্টস গ্রাহক এবং সঞ্চয়ী অ্যাকাউন্ট গ্রাহক) যা গ্রাহকের ডেটা সরবরাহ করে এবং প্রদত্ত গ্রাহকের শেষ নামটির জন্য আমাদের গ্রাহকের বিবরণ প্রয়োজন। প্রদত্ত পদবি দেওয়া নামের পরিবর্তে আমরা সেই 3 টি ডাটাবেস থেকে 1 টিরও বেশি গ্রাহক বিশদ পেতে পারি।
বাস্তবায়ন:
ব্যবসায় মডেল স্তর:
public class Customer
{
// customer detail properties...
}
ডেটা অ্যাক্সেস লেয়ার:
public interface IDataAccess
{
Customer GetDetails(string lastName);
}
উপরে ইন্টারফেস বিমূর্ত শ্রেণি দ্বারা প্রয়োগ করা হয়
public abstract class BaseDataAccess : IDataAccess
{
/// <summary> Enterprise library data block Database object. </summary>
public Database Database;
public Customer GetDetails(string lastName)
{
// use the database object to call the stored procedure to retrieve the customer details
}
}
এই বিমূর্ত শ্রেণীর সমস্ত 3 ডাটাবেসের জন্য একটি সাধারণ পদ্ধতি "গেটডেটেলস" রয়েছে যা নীচে প্রদর্শিত প্রতিটি ডাটাবেস ক্লাস দ্বারা প্রসারিত
মর্টেজ গ্রাহক ডেটা অ্যাক্সেস:
public class MortgageCustomerDataAccess : BaseDataAccess
{
public MortgageCustomerDataAccess(IDatabaseFactory factory)
{
this.Database = factory.GetMortgageCustomerDatabase();
}
}
বর্তমান অ্যাকাউন্ট গ্রাহক ডেটা অ্যাক্সেস:
public class CurrentAccountCustomerDataAccess : BaseDataAccess
{
public CurrentAccountCustomerDataAccess(IDatabaseFactory factory)
{
this.Database = factory.GetCurrentAccountCustomerDatabase();
}
}
অ্যাকাউন্ট গ্রাহক ডেটা অ্যাক্সেস সঞ্চয় করে:
public class SavingsAccountCustomerDataAccess : BaseDataAccess
{
public SavingsAccountCustomerDataAccess(IDatabaseFactory factory)
{
this.Database = factory.GetSavingsAccountCustomerDatabase();
}
}
এই 3 ডেটা অ্যাক্সেস ক্লাসগুলি সেট হয়ে গেলে, এখন আমরা ক্লায়েন্টের দিকে আমাদের দৃষ্টি আকর্ষণ করি। বিজনেস লেয়ারে আমাদের কাস্টমার সার্ভিস ম্যানেজার ক্লাস থাকে যা গ্রাহকদের বিবরণটিকে তার ক্লায়েন্টগুলিতে ফিরিয়ে দেয়।
ব্যবসায় স্তর:
public class CustomerServiceManager : ICustomerServiceManager, BaseServiceManager
{
public IEnumerable<Customer> GetCustomerDetails(string lastName)
{
IEnumerable<IDataAccess> dataAccess = new List<IDataAccess>()
{
new MortgageCustomerDataAccess(new DatabaseFactory()),
new CurrentAccountCustomerDataAccess(new DatabaseFactory()),
new SavingsAccountCustomerDataAccess(new DatabaseFactory())
};
IList<Customer> customers = new List<Customer>();
foreach (IDataAccess nextDataAccess in dataAccess)
{
Customer customerDetail = nextDataAccess.GetDetails(lastName);
customers.Add(customerDetail);
}
return customers;
}
}
ইতিমধ্যে এটি এখন জটিল হয়ে যাওয়ায় নির্ভরযোগ্যতা ইনজেকশনটি এটিকে সহজ রাখার জন্য দেখায় নি।
এখন যদি আমাদের কাছে নতুন গ্রাহকের বিশদ ডেটাবেস থাকে তবে আমরা কেবলমাত্র একটি নতুন শ্রেণি যুক্ত করতে পারি যা বেসডেটাঅ্যাক্সেস প্রসারিত করে এবং এটির ডাটাবেস অবজেক্ট সরবরাহ করে।
অবশ্যই আমাদের অংশগ্রহণকারী সকল ডেটাবেজে অভিন্ন স্টোরেজ পদ্ধতিগুলি দরকার।
শেষ CustomerServiceManager
অবধি, ক্লাসের ক্লায়েন্ট কেবলমাত্র getCustomerDetails পদ্ধতি কল করবে, শেষ নামটি পাস করবে এবং কীভাবে এবং কোথা থেকে ডেটা আসছে তা নিয়ে চিন্তা করা উচিত নয়।
আশা করি এটি আপনাকে এলএসপি বোঝার জন্য ব্যবহারিক পদ্ধতির সুযোগ দেবে।
লিসকভ সাবস্টিটিউট নীতি প্রয়োগ করার কোড এখানে the
public abstract class Fruit
{
public abstract string GetColor();
}
public class Orange : Fruit
{
public override string GetColor()
{
return "Orange Color";
}
}
public class Apple : Fruit
{
public override string GetColor()
{
return "Red color";
}
}
class Program
{
static void Main(string[] args)
{
Fruit fruit = new Orange();
Console.WriteLine(fruit.GetColor());
fruit = new Apple();
Console.WriteLine(fruit.GetColor());
}
}
এলএসভির বিবৃতিতে বলা হয়েছে: "উত্পন্ন ক্লাসগুলি তাদের বেস ক্লাসগুলির জন্য (বা ইন্টারফেস) পরিবর্তনের যোগ্য হওয়া উচিত" এবং "বেস ক্লাসগুলির (বা ইন্টারফেস) উল্লেখগুলি পদ্ধতিগুলি না জেনে বা বিবরণ না জেনে উদ্ভূত শ্রেণির পদ্ধতি ব্যবহার করতে সক্ষম হতে হবে "