স্ট্রাটেজি ডিসাইন প্যাটার্ন - Strategy Design Pattern

(ডিসেম্বর ১২, ২০১৪, মঙ্গলবার)
স্ট্রাটেজি ডিসাইন প্যাটার্ন - Strategy Design Pattern


আজকে আরেকটা ডিসাইন প্যাটার্ন নিয়ে আলাপ করব। আগেই বলে নেই,  হেড ফার্স্ট ডিসাইন প্যাটার্ন বইয়ে কিন্তু এই প্যাটার্ন নিয়ে কোনো চ্যাপ্টার নাই।  কাজেই, রেফারেন্স হিসাবে ওয়েব লিংক-ই ভরসা।


কখনো কী এমন অবস্থায় পড়েছেন যে আপনার কোড-এ অনেক গুলা if- else if - else if -...else লেখা হয়ে গেছে? প্রতিটা ব্লক আসলে কম-বেশি একই জিনিস বা কাজ করে? আপনি আশংকা করছেন যে আপনাকে  ভবিষ্যতে আরো বেশি else- if ব্লক যোগ করতে হতে পারে? তাই যদি হয়, তাহলে সুখবর! আজকের ডিসাইন প্যাটার্ন সেই সমস্যার সমাধান করবে - পরিচয় করিয়ে দেই: স্ট্রাটেজি ডিসাইন প্যাটার্ন!


সমস্যাটার আরেকটু ভালো ভাবে ব্যাখ্যা করা যাক: ধরা যাক আমরা অনেকটা এই রকম কোড লিখে বসে আছি:


public void methodThatDoesThingsForUs (String option) {
   if (option.equals(“optionA”)) {  
// multiple lines of code for optionA
   }
   else if (option.equals(“optionB”)) {
// multiple lines of code for optionB
   }
   else {
  // multiple lines of code for optionC
   }
}


কোডের চেহারা ভালো করার উপায় হিসাবে প্রথমেই আপনার মাথায় কী আসছে? আমার মাথায় যেটা এসেছিল তা হচ্ছে, “// mulitple lines of code for optionX” লাইন গুলোকে মেথড কল দিয়ে বদলিয়ে ফেলা। তাতে করে কোডের চেহারা অনেকটা এইরকম হবে:


public void methodThatDoesThingsForUs (String option) {
   if (option.equals(“optionA”)) {  
optionAHandler();
   }
   else if (option.equals(“optionB”)) {
optionBHandler();
   }
   else {
  optionCHandler();
   }
}


কিন্তু কোডের চেহারা ভালো হওয়া ছাড়া আর কিন্তু কিছুই হয় নি।  সেই-ই তো নতুন কোনো অপশন আসলে আবার কোডে পরিবর্তন করতে হবে, নতুন মেথড যোগ করতে হবে, পুরো কোড আবার কম্পাইল করতে হবে,  ঠিক না?
আরো একটা উদাহরণ দিয়ে সমস্যাটা ভালো করে বোঝা যাক।  ধরা যাক, আপনি কোনো Checkout সিস্টেম বানাচ্ছেন। ওই যে, রাইফেলস স্কয়ারে’র আগোরা দোকানের টাকা দেয়ার যে লাইন থাকে না ? - ওই রকম।  এই রকম একটা চেকআউট সিস্টেমে টাকা দেয়ার আসলে অনেকগুলা উপায় থাকে। যেমন,  ক্যাশ টাকা,  ক্রেডিট বা ডেবিট কার্ড অথবা আগোরার নিজস্ব কার্ড ইত্যাদি। কে জানে, ভবিষ্যতে হয়ত আরো সহজ কোনো উপায় বের হবে।  আমেরিকাতে যেমন শুরু হয়ে গেছে। এখন কার্ড আর টান দিতে হয় না, মেশিনের সামনে নিয়ে নাড়ালেই নাকি হয়ে যায়! প্রতিটা অপশনের এলগরিদম ভিন্ন ভিন্ন হলেও কাজ কিন্তু একটাই: ব্যবহারকারীর ইচ্ছা (বা স্ট্রাটেজি) অনুযায়ী টাকা কেটে নেয়া।


কাজেই এই রকম একটা সিস্টেমের জন্য কিন্তু উপরের কোডের মত অপশন অনুযায়ী কাজ করতে হবে। সহজ কোনো উপায় কি আছে যেটা কোডে পরিবর্তন না করেই আমাদের নতুন নতুন অপশন যোগ করতে দেবে?


উত্তর : স্ট্রাটেজি ডিসাইন প্যাটার্ন!


প্রথমে স্ট্রাটেজি ডিসাইন প্যাটার্ন -এর UML ডায়াগ্রাম টা দেখে নেই  [১] :


খেয়াল করুন: Strategy কিন্তু ইটালিক হয়ে আছে।  UMLডায়াগ্রাম-এ কোনো বাক্স-এ ইটালিক লেখা থাকা মানে হচ্ছে তা হয় একটা Interface  বা abstract  ক্লাস। আমাদের ক্ষেত্রে এইটা ইন্টারফেস। অর্থাৎ, একটা ইন্টারফেস আছে যা একটা কমন মেথড সিগনেচার বলে দিচ্ছে। আমাদের চেকআউট সিস্টেমের বেলায় হয়তো এটা payment মেথড।


নিচের কংক্রিট ক্লাস গুলা প্রত্যেকে ইন্টারফেস কে implement করবে। আমাদের বেলায় হয়ত একটা ক্রেডিট কার্ড, একটা ডেবিট কার্ড আরেকটা ক্যাশ ক্লাস।


অন্য দিকে, Context ক্লাস কিন্তু সেই কম্পসিশন ব্যবহার করছে (ডেকরেট ডিসাইন প্যাটার্ন আলাপ করার সময় বলেছিলাম কেন কম্পসিশন ব্যবহার করা ভালো). Context  ক্লাস (একটু পরে কোডে আরো ভালোভাবে দেখব) আমাদের Strategy ইন্টারফেস কে রেফার করবে। আর যে কেউ এই ইন্টারফেস ইমপ্লেমেন্ট করে, তাকেই Context ক্লাস-এর অবজেক্ট  ইনভক করতে পারবে।


এবার পুরো কোড দেখা যাক :




ডানপাশের কোড ইন্টারফেসকে ইমপ্লেমেন্ট করে।  এক একটা ক্লাস এক একটা এলগরিদম ব্যবহার করে একই মেথড doSmth লেখে। বামপাশের কোড-ই আসল কাজটা করে।  Context ক্লাস -এ  সাধারণত একটা static constructor  আর আরেকটা static  doSmth () মেথড থাকে। Constructor শুরুতেই আমাদের প্রতিটা কংক্রিট ক্লাস-এর অবজেক্ট তৈরী করে, প্রত্যেকটার একটা করে আলাদা নাম দিয়ে  একটা HashMap -এ রেখে দেবে । Context ক্লাস -এর doSmth মেথড (অন্য নামের মেথড হলেও চলবে) String  টাইপ অর্থাৎ আমাদের নামের  একটা প্যারামিটার নিবে। HashMap থেকে নাম অনুযায়ী অবজেক্ট নিয়ে এসে (get  করে আর কি) ওই অবজেক্ট-এর  doSmth  মেথড কল করবে। ব্যস, হয়ে গেল!


এখন নতুন কোনো পেমেন্ট-এর উপায় (যেমন, পা নেড়ে বা চোখ টিপে টাকা দেয়া ;-)) যোগ করতে আমাদের নতুন একটা ক্লাস লিখতে হবে, যা কিনা Strategy  ইন্টারফেস ইমপ্লেমেন্ট করবে।  আর doSmth  মেথড তার নিজস্ব এলগরিদম এ তার কাজ করবে। আমাদের শুধু একটা লাইন কোড Context  ক্লাস-এ যোগ করতে হবে:


 “_map .add (“wink”, new CaseEyeWink());”


কম্পাইলের প্রশ্ন আসলে শুধু নতুন ক্লাস আর Context  ক্লাস কম্পাইল করলেই চলবে। বাকি কোনো কোডে আর হাত দিতে হবে না! ফলে, কোডে বাগ (code bug) তৈরী হবার সম্ভাবনাও কমবে। যতদুর মনে পড়ে, পুরো ব্যাপারটার কয়েকটা নাম আছে, এই সুযোগে তাও বলে দেই: Separation of concern বা low coupling :)


রেফারেন্স: [১] গিল ফিন্কের ব্লগ:/

ধন্যবাদ। -- ইশতিয়াক হোসেন, সিএসই , ডিইউ , সপ্তম ব্যাচ।

POM or TAP design pattern for test automation using Selenium WebDriver

POM or TAP design pattern for test automation using Selenium WebDriver August 21, Monday, 2017 I've written the same topic in Bangl...