বিস্তৃতভাবে, সমস্ত প্রোগ্রামিং ভাষা দুটি দৃষ্টান্তে শ্রেণীবদ্ধ করা যেতে পারে:
ইম্পেরেটিভ / অবজেক্ট-ওরিয়েন্টেড প্রোগ্রামিং - নির্দেশাবলীর ক্রম অনুসরণ করে যা লাইন-বাই-লাইন কার্যকর করা হয়।
ঘোষণামূলক / কার্যকরী প্রোগ্রামিং - অনুক্রমিক নয় তবে প্রোগ্রামের উদ্দেশ্যের সাথে নিজেকে আরও বেশি উদ্বিগ্ন করে। পুরো প্রোগ্রামটি একটি ফাংশনের মতো যার আরও সাব-ফাংশন রয়েছে, প্রতিটি একটি নির্দিষ্ট কাজ সম্পাদন করে।
একজন জুনিয়র ডেভেলপার হিসেবে, আমি এটা কঠিনভাবে উপলব্ধি করছি (পড়ুন: আমি 1000 লাইনের কোডের দিকে নার্ভাসভাবে তাকিয়ে ছিলাম) যে এটি শুধুমাত্র কার্যকরী কোড লেখার জন্য নয়, শব্দার্থগতভাবে সহজ এবং নমনীয় কোডও।
উভয় দৃষ্টান্তে ক্লিন কোড লেখার একাধিক সর্বোত্তম অনুশীলন থাকলেও, আমি প্রোগ্রামিংয়ের অবজেক্ট-ওরিয়েন্টেড প্যারাডাইম সম্পর্কিত সলিড ডিজাইন নীতিগুলি সম্পর্কে কথা বলতে যাচ্ছি।
S — একক দায়িত্ব
ও—ওপেন-ক্লোজড প্রিন্সিপল
এল — লিসকভ প্রতিস্থাপন নীতি
আমি - ইন্টারফেস বিভাজন
D — নির্ভরতা বিপরীত
এই ধারণাগুলি উপলব্ধি করতে যে কোনও অসুবিধার মূল কারণ এই নয় যে তাদের প্রযুক্তিগত গভীরতা অগাধ, বরং কারণ এগুলি বিমূর্ত নির্দেশিকা যা অবজেক্ট-ওরিয়েন্টেড প্রোগ্রামিং-এ পরিষ্কার কোড লেখার জন্য সাধারণীকৃত। আসুন এই ধারণাগুলিকে বাড়িতে চালিত করার জন্য কিছু উচ্চ-স্তরের ক্লাস ডায়াগ্রাম দেখি।
এগুলি সঠিক ক্লাস ডায়াগ্রাম নয় তবে একটি ক্লাসে কোন পদ্ধতিগুলি উপস্থিত রয়েছে তা বোঝার জন্য এটি মৌলিক ব্লুপ্রিন্ট।
আমাদের নিবন্ধ জুড়ে একটি ক্যাফে একটি উদাহরণ বিবেচনা করা যাক.
একটি ক্লাস পরিবর্তন করার শুধুমাত্র একটি কারণ থাকা উচিত
এই ক্লাসটি বিবেচনা করুন যা ক্যাফে দ্বারা প্রাপ্ত অনলাইন অর্ডারগুলি পরিচালনা করে।
এতে দোষ কি?
এই একক শ্রেণী একাধিক ফাংশনের জন্য দায়ী। যদি আপনাকে অন্যান্য অর্থপ্রদানের পদ্ধতি যোগ করতে হয়? আপনার কাছে নিশ্চিতকরণ পাঠানোর একাধিক উপায় থাকলে কী হবে? অর্ডার প্রক্রিয়াকরণের জন্য দায়ী ক্লাসে অর্থপ্রদানের যুক্তি পরিবর্তন করা খুব ভাল ডিজাইন নয়। এটি অত্যন্ত অ-নমনীয় কোডের দিকে নিয়ে যায়।
একটি ভাল উপায় হল এই নির্দিষ্ট কার্যকারিতাগুলিকে কংক্রিট ক্লাসে আলাদা করা এবং সেগুলির একটি উদাহরণ কল করা, যেমনটি নীচের চিত্রে দেখানো হয়েছে।
সত্তা সম্প্রসারণের জন্য উন্মুক্ত হওয়া উচিত কিন্তু পরিবর্তনের জন্য বন্ধ।
ক্যাফেতে, আপনাকে বিকল্পগুলির একটি তালিকা থেকে আপনার কফির জন্য মশলাগুলি বেছে নিতে হবে এবং একটি ক্লাস রয়েছে যা এটি পরিচালনা করে।
ক্যাফে একটি নতুন মশলা যোগ করার সিদ্ধান্ত নিয়েছে, মাখন. লক্ষ্য করুন কীভাবে নির্বাচিত মশলা অনুযায়ী দাম পরিবর্তিত হবে এবং মূল্য গণনার যুক্তি কফি ক্লাসে রয়েছে। আমাদের শুধুমাত্র প্রতিবার একটি নতুন মশলা ক্লাস যোগ করতে হবে না যা প্রধান শ্রেণীতে সম্ভাব্য কোড পরিবর্তনগুলি তৈরি করে, কিন্তু প্রতিবার যুক্তিটিকে ভিন্নভাবে পরিচালনাও করে।
একটি ভাল উপায় হল একটি মশলা ইন্টারফেস তৈরি করা যা শিশু ক্লাসগুলিকে পরিবর্তন করতে পারে যা এর পদ্ধতিগুলিকে ওভাররাইড করে। এবং প্রধান শ্রেণী শুধুমাত্র পরামিতি পাস করতে এবং প্রতিটি অর্ডারের জন্য পরিমাণ এবং মূল্য পেতে মশলা ইন্টারফেস ব্যবহার করতে পারে।
এর দুটি সুবিধা রয়েছে:
1. আপনি ভিন্ন বা এমনকি একাধিক মশলা (মোচা এবং চকলেট সহ কফি স্বর্গীয় শোনাচ্ছে) পেতে আপনার অর্ডারটি গতিশীলভাবে পরিবর্তন করতে পারেন।
2. কন্ডিমেন্ট ক্লাসের কফি ক্লাসের সাথে একটি সম্পর্ক আছে, এর পরিবর্তে। তাই আপনার কফিতে থাকতে পারে-একটি মোচা/মাখন/দুধ আপনার কফির চেয়ে-একটি মোচা/মাখন/দুধ ধরনের কফি।
প্রতিটি সাবক্লাস বা প্রাপ্ত শ্রেণী তার বেস বা মূল শ্রেণীর জন্য প্রতিস্থাপনযোগ্য হওয়া উচিত।
এর মানে হল যে সাব-ক্লাসটি সরাসরি প্যারেন্ট ক্লাস প্রতিস্থাপন করতে সক্ষম হওয়া উচিত; এটা একই কার্যকারিতা থাকতে হবে. আমি এটি বুঝতে অসুবিধা পেয়েছি কারণ এটি কিছু জটিল গণিত সূত্রের মতো শোনাচ্ছে। তবে আমি এই নিবন্ধে এটি পরিষ্কার করার চেষ্টা করব।
ক্যাফে কর্মীদের বিবেচনা করুন. বারিস্তা, ম্যানেজার এবং সার্ভার আছে। তারা সব একই কার্যকারিতা আছে.
তাই আমরা নাম, অবস্থান, getName, getPostion, takeOrder(), serve() সহ একটি বেস স্টাফ ক্লাস তৈরি করতে পারি।
প্রতিটি কংক্রিট ক্লাস, ওয়েটার, বারিস্তা এবং ম্যানেজার এটি থেকে আহরণ করতে পারে এবং অবস্থানের জন্য প্রয়োজন অনুসারে তাদের বাস্তবায়নের জন্য একই পদ্ধতিগুলিকে ওভাররাইড করতে পারে।
এই উদাহরণে, লিসকভ সাবস্টিটিউশন প্রিন্সিপল (এলএসপি) ব্যবহার করা হয়েছে নিশ্চিত করে যে স্টাফের যেকোন প্রাপ্ত শ্রেণী কোডের সঠিকতাকে প্রভাবিত না করে বেস স্টাফ শ্রেণীর সাথে বিনিময়যোগ্যভাবে ব্যবহার করা যেতে পারে।
উদাহরণস্বরূপ, ওয়েটার ক্লাস স্টাফ ক্লাসকে প্রসারিত করে এবং ওয়েটারের ভূমিকার জন্য নির্দিষ্ট অতিরিক্ত কার্যকারিতা অন্তর্ভুক্ত করতে টেকঅর্ডার এবং সার্ভঅর্ডার পদ্ধতিগুলিকে ওভাররাইড করে। যাইহোক, আরও গুরুত্বপূর্ণভাবে, কার্যকারিতার মধ্যে পার্থক্য থাকা সত্ত্বেও, যেকোন কোড যা স্টাফ শ্রেণীর একটি বস্তুর প্রত্যাশা করে ওয়েটার শ্রেণীর একটি বস্তুর সাথেও সঠিকভাবে কাজ করতে পারে।
public class Cafe { public void serveCustomer (Staff staff) { staff.takeOrder(); staff.serveOrder(); } } public class Main { public static void main (String[] args) { Cafe cafe = new Cafe(); Staff staff1 = new Staff( "John" , "Staff" ); Waiter waiter1 = new Waiter( "Jane" ); restaurant.serveCustomer(staff1); // Works correctly with Staff object
restaurant.serveCustomer(waiter1); // Works correctly with Waiter object
} }
এখানে ক্যাফে ক্লাসের মেথড serveCustomer() একটি স্টাফ অবজেক্টকে প্যারামিটার হিসেবে গ্রহণ করে। serveCustomer() পদ্ধতি গ্রাহককে পরিবেশন করার জন্য স্টাফ অবজেক্টের takeOrder() এবং serveOrder() পদ্ধতিকে কল করে।
মেইন ক্লাসে, আমরা একটি স্টাফ অবজেক্ট এবং একটি ওয়েটার অবজেক্ট তৈরি করি। তারপরে আমরা ক্যাফে ক্লাসের serveCustomer() পদ্ধতিটিকে দুবার বলি - একবার স্টাফ অবজেক্টের সাথে এবং একবার ওয়েটার অবজেক্টের সাথে।
যেহেতু ওয়েটার ক্লাসটি স্টাফ ক্লাস থেকে উদ্ভূত হয়েছে, যেকোন কোড যা স্টাফ ক্লাসের একটি অবজেক্ট আশা করে ওয়েটার ক্লাসের একটি বস্তুর সাথেও সঠিকভাবে কাজ করতে পারে। এই ক্ষেত্রে, ক্যাফে ক্লাসের serveCustomer() পদ্ধতি স্টাফ অবজেক্ট এবং ওয়েটার অবজেক্ট উভয়ের সাথেই সঠিকভাবে কাজ করে, যদিও ওয়েটার অবজেক্টে ওয়েটারের ভূমিকার জন্য নির্দিষ্ট অতিরিক্ত কার্যকারিতা রয়েছে।
ক্লাসগুলিকে তারা যে পদ্ধতিগুলি ব্যবহার করে না তার উপর নির্ভর করতে বাধ্য করা উচিত নয়।
তাই ক্যাফেতে এই বহুমুখী ভেন্ডিং মেশিন রয়েছে যা কফি, চা, স্ন্যাকস এবং সোডা বিতরণ করতে পারে।
এখানে ভূল কিসের? প্রযুক্তিগতভাবে কিছুই না। যদি আপনাকে কফি বিতরণের মতো যেকোন ফাংশনের জন্য ইন্টারফেসটি বাস্তবায়ন করতে হয়, তবে আপনাকে চা, সোডা এবং স্ন্যাকসের জন্যও অন্যান্য পদ্ধতি প্রয়োগ করতে হবে। এটি অপ্রয়োজনীয় এবং এই ফাংশনগুলি একে অপরের কার্যকারিতার সাথে সম্পর্কিত নয়। এই ফাংশন প্রতিটি তাদের মধ্যে খুব কম সমন্বয় আছে.
সংহতি কি? এটি একটি ফ্যাক্টর যা নির্ধারণ করে কতটা দৃঢ়ভাবে একটি ইন্টারফেসের পদ্ধতি একে অপরের সাথে সম্পর্কিত।
এবং ভেন্ডিং মেশিনের ক্ষেত্রে, পদ্ধতিগুলি খুব কমই পরস্পর নির্ভরশীল। আমরা পদ্ধতিগুলিকে আলাদা করতে পারি যেহেতু তাদের খুব কম সংহতি রয়েছে।
এখন, যে কোনো ইন্টারফেস যা একটি জিনিস বাস্তবায়নের উদ্দেশ্যে, শুধুমাত্র takeMoney() প্রয়োগ করতে হবে যা সমস্ত ফাংশনের জন্য সাধারণ। এটি একটি ইন্টারফেসে সম্পর্কহীন ফাংশনগুলিকে আলাদা করে তাই একটি ইন্টারফেসে সম্পর্কহীন ফাংশনগুলি জোরপূর্বক প্রয়োগ করা এড়িয়ে যায়।
উচ্চ স্তরের মডিউলগুলি নিম্ন স্তরের মডিউলগুলির উপর নির্ভর করা উচিত নয়। বিশদ বিমূর্ততার উপর নির্ভর করতে হবে।
ক্যাফেতে এয়ার কন্ডিশনার (কুলার) বিবেচনা করুন। এবং আপনি যদি আমার মতো হন তবে এটি সর্বদা সেখানে জমে থাকে। আসুন রিমোট কন্ট্রোল এবং এসি ক্লাস দেখি।
এখানে, রিমোট কন্ট্রোল হল উচ্চ স্তরের মডিউল যা এসির উপর নির্ভর করে, নিম্ন স্তরের উপাদান। যদি আমি একটি ভোট পাই, আমিও একটি হিটার চাই :P তাই শীতল না হয়ে সাধারণ তাপমাত্রা নিয়ন্ত্রণের জন্য, আসুন দূরবর্তী এবং তাপমাত্রা নিয়ন্ত্রণকে ডিকপল করি৷ কিন্তু রিমোট কন্ট্রোল ক্লাস শক্তভাবে এসির সাথে মিলিত হয়, যা একটি কংক্রিট বাস্তবায়ন। নির্ভরতাকে দ্বিগুণ করার জন্য, আমরা একটি ইন্টারফেস তৈরি করতে পারি যার ফাংশন আছে শুধুমাত্র 45-65 F-এর রেঞ্জের মধ্যে বাড়াতে Temp() এবং reduceTemp()।
আপনি দেখতে পাচ্ছেন, উচ্চ-স্তরের এবং নিম্ন-স্তরের মডিউল উভয়ই একটি ইন্টারফেসের উপর নির্ভর করে যা তাপমাত্রা বৃদ্ধি বা হ্রাস করার কার্যকারিতাকে বিমূর্ত করে।
কংক্রিট ক্লাস, এসি, প্রযোজ্য তাপমাত্রা পরিসীমা সহ পদ্ধতিগুলি প্রয়োগ করে।
এখন আমি সম্ভবত সেই হিটারটি পেতে পারি যা আমি হিটার নামক একটি ভিন্ন কংক্রিট শ্রেণিতে বিভিন্ন তাপমাত্রার রেঞ্জ বাস্তবায়ন করতে চাই।
উচ্চ-স্তরের মডিউল, রিমোট কন্ট্রোল শুধুমাত্র রানের সময় সঠিক পদ্ধতিতে কল করার বিষয়ে চিন্তা করতে হবে।