ডিজাইনের নিদর্শনগুলি পড়ার সময় এই বাক্যটিতে একজন হোঁচট খায়।
তবে আমি এটি বুঝতে পারি না, কেউ কি আমার জন্য এটি ব্যাখ্যা করতে পারে?
ডিজাইনের নিদর্শনগুলি পড়ার সময় এই বাক্যটিতে একজন হোঁচট খায়।
তবে আমি এটি বুঝতে পারি না, কেউ কি আমার জন্য এটি ব্যাখ্যা করতে পারে?
উত্তর:
ইন্টারফেসগুলি কেবল চুক্তি বা স্বাক্ষর এবং এগুলি বাস্তবায়ন সম্পর্কে কিছুই জানে না।
ইন্টারফেসের বিরুদ্ধে কোডিংয়ের অর্থ, ক্লায়েন্ট কোডটি সর্বদা একটি ইন্টারফেস অবজেক্ট রাখে যা একটি কারখানা দ্বারা সরবরাহ করা হয়। কারখানায় প্রত্যাবর্তিত কোনও উদাহরণ টাইপ ইন্টারফেসের হবে যা কোনও ফ্যাক্টরি প্রার্থী শ্রেণি অবশ্যই প্রয়োগ করেছে। এইভাবে ক্লায়েন্ট প্রোগ্রাম বাস্তবায়ন সম্পর্কে চিন্তিত নয় এবং ইন্টারফেসের স্বাক্ষরটি নির্ধারণ করে যে সমস্ত ক্রিয়াকলাপ কী করা যায়। রান-টাইমে কোনও প্রোগ্রামের আচরণ পরিবর্তন করতে এটি ব্যবহার করা যেতে পারে। এটি আপনাকে রক্ষণাবেক্ষণের দৃষ্টিকোণ থেকে আরও অনেক ভাল প্রোগ্রাম লিখতে সহায়তা করে।
এখানে আপনার জন্য একটি প্রাথমিক উদাহরণ।
public enum Language
{
English, German, Spanish
}
public class SpeakerFactory
{
public static ISpeaker CreateSpeaker(Language language)
{
switch (language)
{
case Language.English:
return new EnglishSpeaker();
case Language.German:
return new GermanSpeaker();
case Language.Spanish:
return new SpanishSpeaker();
default:
throw new ApplicationException("No speaker can speak such language");
}
}
}
[STAThread]
static void Main()
{
//This is your client code.
ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English);
speaker.Speak();
Console.ReadLine();
}
public interface ISpeaker
{
void Speak();
}
public class EnglishSpeaker : ISpeaker
{
public EnglishSpeaker() { }
#region ISpeaker Members
public void Speak()
{
Console.WriteLine("I speak English.");
}
#endregion
}
public class GermanSpeaker : ISpeaker
{
public GermanSpeaker() { }
#region ISpeaker Members
public void Speak()
{
Console.WriteLine("I speak German.");
}
#endregion
}
public class SpanishSpeaker : ISpeaker
{
public SpanishSpeaker() { }
#region ISpeaker Members
public void Speak()
{
Console.WriteLine("I speak Spanish.");
}
#endregion
}
এটি কেবল একটি মৌলিক উদাহরণ এবং নীতিটির প্রকৃত ব্যাখ্যা এই উত্তরের সুযোগের বাইরে।
আমি উপরের উদাহরণটি আপডেট করেছি এবং একটি বিমূর্ত Speaker
বেস ক্লাস যুক্ত করেছি। এই আপডেটে, আমি সমস্ত স্পিকারগুলিতে "SayHello" এ একটি বৈশিষ্ট্য যুক্ত করেছি। সমস্ত স্পিকার "হ্যালো ওয়ার্ল্ড" কথা বলে। সুতরাং এটি একই ফাংশন সহ একটি সাধারণ বৈশিষ্ট্য। শ্রেণীর চিত্রটি দেখুন এবং আপনি যে Speaker
বিমূর্ত শ্রেণিটি প্রয়োগ করেছেন তা দেখতে পাবেনISpeaker
ইন্টারফেস এবং চিহ্ন Speak()
বিমূর্ত যেমন যা মানে প্রতিটি স্পিকার বাস্তবায়ন বাস্তবায়ন জন্য দায়ী যে Speak()
পদ্ধতি যেহেতু এটি থেকে পরিবর্তিত হয় Speaker
থেকে Speaker
। তবে সমস্ত স্পিকার সর্বসম্মতিক্রমে "হ্যালো" বলে। সুতরাং বিমূর্ত স্পিকার শ্রেণিতে আমরা একটি পদ্ধতি সংজ্ঞায়িত করি যা "হ্যালো ওয়ার্ল্ড" বলে এবং প্রতিটি Speaker
বাস্তবায়ন SayHello()
পদ্ধতিটি গ্রহণ করে ।
SpanishSpeaker
হ্যালো বলতে পারে না এমন একটি ক্ষেত্রে বিবেচনা করুন যাতে সেক্ষেত্রে আপনি SayHello()
স্প্যানিশ স্পিকারের জন্য পদ্ধতিটি ওভাররাইড করতে পারেন এবং যথাযথ ব্যতিক্রম বাড়াতে পারেন।
দয়া করে নোট করুন, আমরা ইন্টারফেস আইএসপিকারে কোনও পরিবর্তন আনিনি। এবং ক্লায়েন্ট কোড এবং স্পিকারফ্যাক্টরিও অপরিবর্তিত থাকে অপরিবর্তিত। এবং এটিই আমরা অর্জন করি প্রোগ্রামিং-থেকে-ইন্টারফেসের ।
এবং আমরা কেবল এইভাবে মূল প্রোগ্রামটি অপরিবর্তিত রেখে প্রতিটি বাস্তবায়নে একটি বেস অ্যাবস্ট্রাক্ট শ্রেণীর স্পিকার এবং কিছু ছোটখাট পরিবর্তন যুক্ত করে এই আচরণটি অর্জন করতে পারি। এটি যে কোনও অ্যাপ্লিকেশনের একটি কাঙ্ক্ষিত বৈশিষ্ট্য এবং এটি আপনার অ্যাপ্লিকেশনটিকে সহজেই রক্ষণাবেক্ষণযোগ্য করে তোলে।
public enum Language
{
English, German, Spanish
}
public class SpeakerFactory
{
public static ISpeaker CreateSpeaker(Language language)
{
switch (language)
{
case Language.English:
return new EnglishSpeaker();
case Language.German:
return new GermanSpeaker();
case Language.Spanish:
return new SpanishSpeaker();
default:
throw new ApplicationException("No speaker can speak such language");
}
}
}
class Program
{
[STAThread]
static void Main()
{
//This is your client code.
ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English);
speaker.Speak();
Console.ReadLine();
}
}
public interface ISpeaker
{
void Speak();
}
public abstract class Speaker : ISpeaker
{
#region ISpeaker Members
public abstract void Speak();
public virtual void SayHello()
{
Console.WriteLine("Hello world.");
}
#endregion
}
public class EnglishSpeaker : Speaker
{
public EnglishSpeaker() { }
#region ISpeaker Members
public override void Speak()
{
this.SayHello();
Console.WriteLine("I speak English.");
}
#endregion
}
public class GermanSpeaker : Speaker
{
public GermanSpeaker() { }
#region ISpeaker Members
public override void Speak()
{
Console.WriteLine("I speak German.");
this.SayHello();
}
#endregion
}
public class SpanishSpeaker : Speaker
{
public SpanishSpeaker() { }
#region ISpeaker Members
public override void Speak()
{
Console.WriteLine("I speak Spanish.");
}
public override void SayHello()
{
throw new ApplicationException("I cannot say Hello World.");
}
#endregion
}
List
ধরণের হিসাবে ব্যবহার করেন তবে আপনার এখনও ধরে নেওয়া যেতে পারে যে বারবার কল করে এলোমেলো অ্যাক্সেস দ্রুত হয় get(i)
।
কোনও বিষয়বস্তু এবং তার ক্লায়েন্টদের মধ্যে একটি চুক্তি হিসাবে একটি ইন্টারফেস ভাবেন। এটি হ'ল ইন্টারফেসটি কোনও বস্তু করতে পারে এমন জিনিসগুলি নির্দিষ্ট করে এবং সেই জিনিসগুলি অ্যাক্সেস করার জন্য স্বাক্ষর করে।
বাস্তবায়ন হ'ল আসল আচরণ। উদাহরণস্বরূপ বলুন আপনার কাছে একটি পদ্ধতি সাজানো () রয়েছে। আপনি কুইকসোর্ট বা মার্জসোর্ট বাস্তবায়ন করতে পারেন। ইন্টারফেস পরিবর্তিত হয় না ক্লায়েন্ট কোড কলিং সাজানোর ক্ষেত্রে এটি বিবেচনা করা উচিত।
জাভা এপিআই এবং। নেট ফ্রেমওয়ার্কের মতো লাইব্রেরি ইন্টারফেসের ভারী ব্যবহার করে কারণ লক্ষ লক্ষ প্রোগ্রামার সরবরাহকৃত বস্তুগুলি ব্যবহার করে। এই লাইব্রেরির স্রষ্টাদের খুব সতর্ক থাকতে হবে যে তারা এই লাইব্রেরির ক্লাসে ইন্টারফেসটি পরিবর্তন করবেন না কারণ এটি গ্রন্থাগারটি ব্যবহার করে সমস্ত প্রোগ্রামারকে প্রভাবিত করবে। অন্যদিকে তারা তাদের পছন্দমতো বাস্তবায়ন পরিবর্তন করতে পারে।
যদি, একজন প্রোগ্রামার হিসাবে, আপনি প্রয়োগের বিরুদ্ধে কোড করেন তবে যত তাড়াতাড়ি এটি পরিবর্তন করে আপনার কোড কাজ করা বন্ধ করে দেয়। সুতরাং ইন্টারফেসের উপকারগুলি এইভাবে ভাবেন:
এর অর্থ হল আপনার কোডটি লেখার চেষ্টা করা উচিত যাতে এটি সরাসরি প্রয়োগের পরিবর্তে একটি বিমূর্ততা (বিমূর্ত শ্রেণি বা ইন্টারফেস) ব্যবহার করে।
সাধারণত বাস্তবায়নটি আপনার কোডটিতে কনস্ট্রাক্টর বা কোনও পদ্ধতির কলের মাধ্যমে প্রবেশ করা হয়। সুতরাং, আপনার কোডটি ইন্টারফেস বা বিমূর্ত শ্রেণীর সম্পর্কে জানে এবং এই চুক্তিতে সংজ্ঞায়িত যে কোনও কিছু কল করতে পারে। যেহেতু প্রকৃত অবজেক্ট (ইন্টারফেস / অ্যাবস্ট্রাক্ট ক্লাসের প্রয়োগ) ব্যবহৃত হয়, কলগুলি বস্তুটিতে অপারেটিং হয়।
এটি Liskov Substitution Principle
(এলএসপি) এর একটি উপসেট , SOLID
নীতিগুলির এল ।
.NET- র একটি উদাহরণ কোডের IList
পরিবর্তে List
বা এর পরিবর্তে হবে Dictionary
, যাতে আপনি IList
আপনার কোডে আদান-প্রদানযোগ্য এমন কোনও ক্লাস ব্যবহার করতে পারেন :
// myList can be _any_ object that implements IList
public int GetListCount(IList myList)
{
// Do anything that IList supports
return myList.Count();
}
বেস ক্লাস লাইব্রেরির (বিসিএল) আরও একটি উদাহরণ হ'ল ProviderBase
বিমূর্ত শ্রেণি - এটি কিছু অবকাঠামো সরবরাহ করে, এবং গুরুত্বপূর্ণভাবে হ'ল আপনি যদি এর বিপরীতে কোড করেন তবে সমস্ত সরবরাহকারী বাস্তবায়ন আন্তঃআযোগযোগ্যভাবে ব্যবহার করা যেতে পারে।
আপনি যদি জ্বলন-কার যুগে কার ক্লাস লিখতেন, তবে এই শ্রেণীর অংশ হিসাবে আপনি তেল চ্যাঞ্জ () প্রয়োগ করতে পারবেন এমন এক দুর্দান্ত সম্ভাবনা রয়েছে। তবে, যখন বৈদ্যুতিন গাড়িগুলি চালু হয়, আপনি সমস্যার মধ্যে পড়বেন কারণ এই গাড়িগুলির জন্য কোনও তেল-পরিবর্তন জড়িত নেই, এবং কোনও প্রয়োগ নেই।
সমস্যার সমাধানটি হ'ল গাড়ি শ্রেণিতে একটি পারফরম্যান্স মেইনটেনেন্স () ইন্টারফেস রাখা এবং যথাযথ প্রয়োগের ভিতরে বিশদটি লুকানো। প্রতিটি গাড়ির ধরণের সঞ্চালন-সংরক্ষণ () এর জন্য নিজস্ব প্রয়োগকরণ সরবরাহ করবে। গাড়ির মালিক হিসাবে আপনাকে যা করতে হবে তা হ'ল পারফরম্যান্স মেইনটেইন () এবং যখন পরিবর্তন হয় তখন অভিযোজন সম্পর্কে চিন্তা করবেন না।
class MaintenanceSpecialist {
public:
virtual int performMaintenance() = 0;
};
class CombustionEnginedMaintenance : public MaintenanceSpecialist {
int performMaintenance() {
printf("combustionEnginedMaintenance: We specialize in maintenance of Combustion engines \n");
return 0;
}
};
class ElectricMaintenance : public MaintenanceSpecialist {
int performMaintenance() {
printf("electricMaintenance: We specialize in maintenance of Electric Cars \n");
return 0;
}
};
class Car {
public:
MaintenanceSpecialist *mSpecialist;
virtual int maintenance() {
printf("Just wash the car \n");
return 0;
};
};
class GasolineCar : public Car {
public:
GasolineCar() {
mSpecialist = new CombustionEnginedMaintenance();
}
int maintenance() {
mSpecialist->performMaintenance();
return 0;
}
};
class ElectricCar : public Car {
public:
ElectricCar() {
mSpecialist = new ElectricMaintenance();
}
int maintenance(){
mSpecialist->performMaintenance();
return 0;
}
};
int _tmain(int argc, _TCHAR* argv[]) {
Car *myCar;
myCar = new GasolineCar();
myCar->maintenance(); /* I dont know what is involved in maintenance. But, I do know the maintenance has to be performed */
myCar = new ElectricCar();
myCar->maintenance();
return 0;
}
অতিরিক্ত ব্যাখ্যা: আপনি একাধিক গাড়ির মালিক এমন একটি গাড়ী মালিক। আপনি যে পরিষেবাটি আউটসোর্স করতে চান তা তৈরি করেন। আমাদের ক্ষেত্রে আমরা সমস্ত গাড়ির রক্ষণাবেক্ষণের কাজ আউটসোর্স করতে চাই।
আপনি পরিষেবা সরবরাহকারীর সাথে গাড়ির ধরণটি সংযুক্ত করার বিষয়ে চিন্তা করতে চান না। আপনি ঠিক রাখবেন কখন আপনি রক্ষণাবেক্ষণের সময়সূচী করতে চান এবং এটিতে চান। উপযুক্ত পরিষেবা সংস্থার ঝাঁপ দাও এবং রক্ষণাবেক্ষণের কাজটি সম্পাদন করা উচিত।
বিকল্প পদ্ধতি।
আপনি কাজটি আহবান করুন এবং নিজেই করুন। এখানে আপনি উপযুক্ত রক্ষণাবেক্ষণের কাজটি করতে যাচ্ছেন।
২ য় পদ্ধতির অবক্ষয়টি কী? রক্ষণাবেক্ষণের সর্বোত্তম উপায় সন্ধানের ক্ষেত্রে আপনি বিশেষজ্ঞ নাও হতে পারেন। আপনার কাজটি গাড়ি চালানো এবং এটি উপভোগ করা। এটি রক্ষণাবেক্ষণের ব্যবসায়ের মধ্যে থাকতে হবে না।
এটি প্রথম পদ্ধতির ক্ষতি কি? কোনও কোম্পানির সন্ধানের ওভারহেড রয়েছে etc. ইত্যাদি আপনি যদি ভাড়া গাড়ি সংস্থার না হন তবে এটি চেষ্টা করার পক্ষে উপযুক্ত হবে না।
এই বিবৃতি সংযোগ সম্পর্কে। অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং ব্যবহারের একটি সম্ভাব্য কারণ হ'ল পুনরায় ব্যবহার। সুতরাং উদাহরণস্বরূপ আপনি দুটি অ্যালগরিদম A এবং B এর মধ্যে ভাগ করে নিতে আপনার অ্যালগরিদমকে বিভক্ত করতে পারেন এটি পরবর্তীতে অন্য অ্যালগরিদম তৈরির জন্য কার্যকর হতে পারে যা দুটি অবজেক্টের মধ্যে একটি বা অন্যটিকে পুনরায় ব্যবহার করতে পারে। তবে, যখন এই বস্তুগুলি যোগাযোগ করে (বার্তা পাঠান - কল পদ্ধতিগুলি), তারা একে অপরের মধ্যে নির্ভরতা তৈরি করে। তবে আপনি যদি অন্যটি বাদ দিয়ে একটি ব্যবহার করতে চান তবে আপনাকে উল্লেখ করতে হবে যে আমরা বি প্রতিস্থাপন করলে অবজেক্ট এ এর জন্য অন্য কিছু অবজেক্ট সি করা উচিত Those সেই বর্ণনাকে ইন্টারফেস বলে। এটি ইন্টারফেসের উপর নির্ভর করে বিভিন্ন বস্তুর সাথে পরিবর্তন ছাড়াই অবজেক্ট এ যোগাযোগ করতে দেয়। আপনি যে বিবৃতিটি উল্লেখ করেছেন তাতে বলা হয়েছে যে আপনি যদি অ্যালগরিদমের কিছু অংশ (বা আরও সাধারণভাবে কোনও প্রোগ্রাম) পুনঃব্যবহার করার পরিকল্পনা করেন তবে আপনার ইন্টারফেস তৈরি করা উচিত এবং তাদের উপর নির্ভর করা উচিত,
অন্যরা যেমন বলেছে, এর অর্থ হল যে আপনার কলিং কোডটি কেবল একটি বিমূর্ত পিতা-মাতার সম্পর্কে জানা উচিত, প্রকৃত বাস্তবায়নকারী ক্লাসটি নয় যা কাজ করবে।
এটি বুঝতে সাহায্য করে তা হ'ল আপনার সর্বদা একটি ইন্টারফেসে প্রোগ্রাম করা উচিত। এর অনেকগুলি কারণ রয়েছে তবে দুটি ব্যাখ্যা করা সহজ
1) পরীক্ষা।
ধরা যাক আমার এক ক্লাসে আমার সম্পূর্ণ ডাটাবেস কোড রয়েছে। যদি আমার প্রোগ্রামটি কংক্রিটের ক্লাস সম্পর্কে জানে, তবে আমি কেবলমাত্র আমার কোডটি that শ্রেণীর বিরুদ্ধে চালিয়ে পরীক্ষা করতে পারি। আমি -> এর সাথে "কথা বলার" অর্থ বোঝাতে চাইছি।
ওয়ার্কারক্লাস -> ডালক্লাস যাইহোক, আসুন মিক্সটিতে একটি ইন্টারফেস যুক্ত করুন।
ওয়ার্কারক্লাস -> আইডিএল -> ডালক্লাস।
সুতরাং ড্যালক্লাস আইডিএল ইন্টারফেস প্রয়োগ করে এবং কেবলমাত্র শ্রমিক শ্রেণীর মাধ্যমে এই কলগুলি।
এখন যদি আমরা কোডের জন্য পরীক্ষা লিখতে চাই, আমরা পরিবর্তে একটি সাধারণ বর্গ তৈরি করতে পারি যা কেবল একটি ডাটাবেসের মতো কাজ করে।
ওয়ার্কারক্লাস -> আইডিএল -> আইফেকডাল।
2) পুনরায় ব্যবহার
উপরের উদাহরণ অনুসরণ করে, আসুন আমরা এসকিউএল সার্ভার থেকে (যা আমাদের কংক্রিট ডালক্লাস ব্যবহার করে) মনোগোডিবিতে যেতে চাই। এটি বড় কাজ করবে তবে আমরা যদি কোনও ইন্টারফেসে প্রোগ্রাম করে থাকি না তবে তা নয়। সেক্ষেত্রে আমরা কেবল নতুন ডিবি ক্লাস লিখি, এবং পরিবর্তন করি (কারখানার মাধ্যমে)
ওয়ার্কারক্লাস -> আইডিএল -> ডালক্লাস
প্রতি
ওয়ার্কারক্লাস -> আইডিএল -> মঙ্গোডিবিসি ক্লাস