সূত্র : http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes- কি- শোল্ড- আপনি- ইউস /
সি # একটি দুর্দান্ত ভাষা যা গত 14 বছরে পরিপক্ক এবং বিকশিত হয়েছে। এটি আমাদের বিকাশকারীদের পক্ষে দুর্দান্ত কারণ একটি পরিপক্ক ভাষা আমাদের ভাষার বৈশিষ্ট্যগুলির আধিক্য সরবরাহ করে।
তবে অনেক শক্তি নিয়ে অনেক বেশি দায়িত্ব হয়ে যায়। এই বৈশিষ্ট্যগুলির কয়েকটি অপব্যবহার করা যেতে পারে, বা কখনও কখনও আপনি কেন অন্য বৈশিষ্ট্যটি ব্যবহার করে বেছে নেবেন তা বোঝা শক্ত। কয়েক বছর ধরে, এমন একটি বৈশিষ্ট্য যা আমি অনেক বিকাশকারীদের সাথে লড়াই করতে দেখেছি তা হ'ল কখন কোন ইন্টারফেস ব্যবহার করতে বা কোনও বিমূর্ত শ্রেণীর ব্যবহারের জন্য বেছে নেওয়া। উভয়েরই রয়েছে সুবিধা এবং অসুবিধাগুলি এবং প্রতিটি ব্যবহারের সঠিক সময় এবং স্থান। তবে আমরা কীভাবে সিদ্ধান্ত নেব ???
উভয়ই প্রকারের মধ্যে সাধারণ কার্যকারিতা পুনরায় ব্যবহারের জন্য সরবরাহ করে। এখনই সর্বাধিক সুস্পষ্ট পার্থক্য হ'ল ইন্টারফেসগুলি তাদের কার্যকারিতাটির জন্য কোনও বাস্তবায়ন সরবরাহ করে না যখন বিমূর্ত ক্লাসগুলি আপনাকে কিছু "বেস" বা "ডিফল্ট" আচরণ বাস্তবায়নের অনুমতি দেয় এবং তারপরে প্রয়োজনীয় ডিগ্রিযুক্ত শ্রেণীর সাথে এই ডিফল্ট আচরণকে "ওভাররাইড" করার ক্ষমতা রাখে ।
এটি সমস্ত ভাল এবং ভাল এবং কোডের দুর্দান্ত পুনঃব্যবহারের জন্য সরবরাহ করে এবং ডিআরওয়াই (নিজেকে পুনরাবৃত্তি করবেন না) সফ্টওয়্যার বিকাশের নীতি অনুসরণ করে। অ্যাবস্ট্রাক্ট ক্লাসগুলি ব্যবহার করার জন্য দুর্দান্ত যখন আপনার "একটি" সম্পর্ক রয়েছে।
উদাহরণস্বরূপ: একটি সোনার পুনরুদ্ধারকারী "এক" কুকুরের ধরণ। একটি পোডল হয়। তারা উভয়ই কুকুরের মতো ছড়িয়ে দিতে পারে। তবে, আপনি বলতে চান যে পোডল পার্কটি "ডিফল্ট" কুকুরের ছালের চেয়ে উল্লেখযোগ্যভাবে আলাদা। অতএব, আপনার নীচের হিসাবে কিছু বাস্তবায়িত করার জন্য এটি বোধগম্য হতে পারে:
public abstract class Dog
{
public virtual void Bark()
{
Console.WriteLine("Base Class implementation of Bark");
}
}
public class GoldenRetriever : Dog
{
// the Bark method is inherited from the Dog class
}
public class Poodle : Dog
{
// here we are overriding the base functionality of Bark with our new implementation
// specific to the Poodle class
public override void Bark()
{
Console.WriteLine("Poodle's implementation of Bark");
}
}
// Add a list of dogs to a collection and call the bark method.
void Main()
{
var poodle = new Poodle();
var goldenRetriever = new GoldenRetriever();
var dogs = new List<Dog>();
dogs.Add(poodle);
dogs.Add(goldenRetriever);
foreach (var dog in dogs)
{
dog.Bark();
}
}
// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark
//
যেমন আপনি দেখতে পাচ্ছেন, আপনার কোড ডিআরওয়াই রাখার একটি দুর্দান্ত উপায় হবে এবং যখন কোনও ধরণের কোনও বিশেষ ক্ষেত্রে প্রয়োগের পরিবর্তে কেবলমাত্র ডিফল্ট বার্কের উপর নির্ভর করতে পারে তখন বেস শ্রেণীর প্রয়োগের কল করার অনুমতি দেওয়া হবে। গোল্ডেনরেট্রাইভার, বক্সার, ল্যাবের মতো ক্লাসগুলি কেবলমাত্র কুকুর বিমূর্ত শ্রেণির প্রয়োগের কারণে "ডিফল্ট" (বেস ক্লাস) বার্ককে কোনও মূল্য ছাড়াই অধিকার করতে পারে।
তবে আমি নিশ্চিত যে আপনি এটি ইতিমধ্যে জানতেন।
আপনি এখানে রয়েছেন কারণ আপনি কেন বুঝতে চান যে আপনি কেন কোনও বিমূর্ত ক্লাসের মাধ্যমে বা তার বিপরীতে কোনও ইন্টারফেস চয়ন করতে পারেন। একটি বিমূর্ত শ্রেণীর চেয়ে আপনি ইন্টারফেস চয়ন করতে পারেন এর একটি কারণ হ'ল যখন আপনার কোনও ডিফল্ট বাস্তবায়ন না থাকে বা প্রতিরোধ করতে চান। এটি সাধারণত কারণ যে ধরণের ইন্টারফেস প্রয়োগ করা হয় এটি "একটি" সম্পর্কের সাথে সম্পর্কিত নয়। আসলে, প্রতিটি প্রকারের "সক্ষম" বা কিছু করার বা "কিছু করার জন্য" অ্যাবিলিটি "রয়েছে তা ব্যতীত তাদের কিছু যুক্ত থাকতে হবে না।
এখন হেক মানে কি? ঠিক আছে, উদাহরণস্বরূপ: একটি মানুষ হাঁস নয় ... এবং হাঁস মানুষ নয়। বেশ স্পষ্ট. তবে, হাঁস এবং একজন মানুষের উভয়েরই সাঁতার কাটার "ক্ষমতা" রয়েছে (এই কারণে যে মানুষ তার সাঁতারের পাঠ 1 ম শ্রেণিতে পাস করেছে :)) এছাড়াও, যেহেতু হাঁস কোনও মানব বা তদ্বিপরীত নয়, তাই এটি "একটি" একটি "রিলেশনশীপ নয়, পরিবর্তে একটি" সক্ষম "সম্পর্ক এবং এটি চিত্রিত করার জন্য আমরা একটি ইন্টারফেস ব্যবহার করতে পারি:
// Create ISwimable interface
public interface ISwimable
{
public void Swim();
}
// Have Human implement ISwimable Interface
public class Human : ISwimable
public void Swim()
{
//Human's implementation of Swim
Console.WriteLine("I'm a human swimming!");
}
// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
public void Swim()
{
// Duck's implementation of Swim
Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
}
}
//Now they can both be used in places where you just need an object that has the ability "to swim"
public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
somethingThatCanSwim.Swim();
}
public void Main()
{
var human = new Human();
var duck = new Duck();
var listOfThingsThatCanSwim = new List<ISwimable>();
listOfThingsThatCanSwim.Add(duck);
listOfThingsThatCanSwim.Add(human);
foreach (var something in listOfThingsThatCanSwim)
{
ShowHowYouSwim(something);
}
}
// So at runtime the correct implementation of something.Swim() will be called
// Output:
// Quack! Quack! I'm a Duck swimming!
// I'm a human swimming!
উপরের কোডের মতো ইন্টারফেসগুলি ব্যবহার করে আপনি কোনও বস্তুকে এমন পদ্ধতিতে পাস করতে পারবেন যা কিছু করতে "সক্ষম" হয়। কোডটি কীভাবে এটি করে তা যত্নশীল করে না ... এটি কেবলমাত্র জানা যায় যে এটি সেই বস্তুর উপর সাঁতার পদ্ধতিটি কল করতে পারে এবং সেই বস্তুটি জানতে পারে কোন ধরণের আচরণের ভিত্তিতে রান-টাইমে কোন আচরণ গ্রহণ করা হয়।
আবারও এটি আপনার কোডটিকে ডিআরওয়াই রাখতে সহায়তা করে যাতে আপনাকে একাধিক পদ্ধতি লিখতে হবে না যা একই কোর ফাংশনটি (শো-হিউম্যানসুইমস (হিউম্যান), শোহউডাকসুইমস (হাঁস), ইত্যাদি) প্রবর্তনের জন্য বস্তুকে কল করছে calling
এখানে একটি ইন্টারফেস ব্যবহার করে কলিং পদ্ধতিগুলি কোন ধরণের কী বা কীভাবে আচরণটি বাস্তবায়িত হয় তা নিয়ে চিন্তা করার দরকার নেই। এটি কেবলমাত্র জানে যে ইন্টারফেসটি প্রদত্ত, প্রতিটি বস্তুকে সাঁতার পদ্ধতিটি প্রয়োগ করতে হবে সুতরাং এটি নিজের কোডে কল করা নিরাপদ এবং সাঁতারের পদ্ধতির আচরণটি তার নিজের শ্রেণীর মধ্যে পরিচালিত করার অনুমতি দেয়।
সারসংক্ষেপ:
সুতরাং আমার থাম্বের মূল নিয়মটি একটি বিমূর্ত শ্রেণিটি ব্যবহার করা হয় যখন আপনি শ্রেণি শ্রেণিবদ্ধের জন্য "ডিফল্ট" কার্যকারিতা বাস্তবায়ন করতে চান বা / এবং আপনি যে ক্লাসগুলি বা ধরণের ভাগ করে যাচ্ছেন "একটি" সম্পর্ক (প্রাক্তন পোডল ") একটি ”কুকুরের ধরণ)।
অন্যদিকে একটি ইন্টারফেস ব্যবহার করুন যখন আপনার "একটি" সম্পর্ক নেই তবে এমন কিছু রয়েছে যা কিছু করার বা কিছু করার "ক্ষমতা" ভাগ করে দেয় (প্রাক্তন হাঁস "মানুষ নয়" তবে হাঁস এবং মানুষের ভাগ সাঁতার "ক্ষমতা")।
অ্যাবস্ট্রাক্ট ক্লাস এবং ইন্টারফেসের মধ্যে লক্ষ্য করার আরেকটি পার্থক্য হ'ল কোনও শ্রেণি এক থেকে অনেকগুলি ইন্টারফেস প্রয়োগ করতে পারে তবে একটি শ্রেণি কেবলমাত্র একটি বিমূর্ত শ্রেণি (বা সেই বিষয়ে কোনও শ্রেণি) থেকে উত্তরাধিকারী হতে পারে। হ্যাঁ, আপনি নীড় ক্লাস করতে পারেন এবং একটি উত্তরাধিকারের শ্রেণিবদ্ধতা থাকতে পারে (যা অনেকগুলি প্রোগ্রাম করে এবং এটি হওয়া উচিত) তবে আপনি একটি বর্ধিত শ্রেণীর সংজ্ঞাতে দুটি শ্রেণির উত্তরাধিকারী হতে পারবেন না (এই নিয়মটি সি # তে প্রযোজ্য some অন্য কয়েকটি ভাষায় আপনি সাধারণত এটি করতে সক্ষম হন) কেবলমাত্র এই ভাষাগুলিতে ইন্টারফেসের অভাবের কারণে)।
ইন্টারফেস ব্যবহার করার সময় ইন্টারফেস বিভাজন নীতি (আইএসপি) মেনে চলাও মনে রাখবেন। আইএসপি জানিয়েছে যে কোনও ক্লায়েন্টকে যে পদ্ধতিগুলি ব্যবহার করে না তার উপর নির্ভর করতে বাধ্য করা উচিত। এই কারণে ইন্টারফেসগুলি নির্দিষ্ট কার্যগুলিতে ফোকাস করা উচিত এবং সাধারণত খুব ছোট (উদাঃ আইডিস্পোজেবল, আইকোম্পারেবল)।
আরেকটি টিপ হ'ল আপনি যদি কার্যকারিতার ক্ষুদ্র, সংক্ষিপ্ত বিট বিকাশ করছেন, ইন্টারফেস ব্যবহার করুন। যদি আপনি বড় ক্রিয়ামূলক ইউনিট ডিজাইন করেন তবে একটি বিমূর্ত শ্রেণি ব্যবহার করুন।
আশা করি কিছু লোকের জন্য এটি পরিষ্কার হয়ে যায়!
এছাড়াও যদি আপনি আরও ভাল উদাহরণগুলি ভাবতে পারেন বা কিছু উল্লেখ করতে চান তবে দয়া করে নীচের মন্তব্যে এটি করুন!