ফ্যাক্টরি প্যাটার্নের পোস্টমর্টেম (Factory Method এবং Abstract Factory)


চলুন আমরা প্রথমে গল্প দিয়ে শুরু করি । সাথে এক জগ চা নিয়ে বসলে ভালো হয় ।গল্পের সাথে সাথে আমরা কোড করব মাঝে মাঝে জগ দিয়ে চা ও খাবো । তো গল্পটা হচ্ছে এ রকম । বাংলাদেশ থেকে যে কয়েকজন গুগলে চাকুরী করে ১০ বছর পর তারা গুগল ছেড়ে দেশে চলে এল । তারা আর সেখানে চাকুরী করবে না । রুদ্ধদ্বার বৈঠকে সিদ্ধান্ত হলো যে তারা তাদের গ্যান বাংলাদেশের তরুন সফটওয়্যার ইঞ্জিনিয়ারদের মাঝে ছড়িয়ে দিবে । কিন্তু কিভাবে ??

তারা সিদ্ধান্ত নিলো যে তারা এমন একটি এপ্লিকেশন বানাবে যাতে করে যারা শিক্ষার্থী তারা অনলাইনে রেজিস্ট্রেশনের মাধ্যমে নির্দিষ্ট বিষয়ের উপর লেকচার দেখতে পারবে । ইতিমধ্যে এই টিমের সাথে যোগ দিয়েছে কয়েক জনসদ্য পাশ করা সফো (সফটওয়্যার ইঞ্জিনিয়ার)

যেই ভাবা সেই কাজ । এই টিমের টিম লিডার ছোট্ট একটা মডিউল বানানোর জন্য দায়িত্ব দিলো সদ্য পাশ করা একজন তরুণ, ধরা যাক তার নাম যদু ।

যদু বেশ উৎসাহী হয়ে অনেক ভেবেচিন্তে তাদের CourseManagement  মডিউলটির জন্য কোড লিখা শুরু করলো । সে প্রথমে ভাবলো যে তাদের CourseManagement মডিউলটিতে অনেকগুলো  কোর্স থাকবে । প্রত্যক্টি কোর্সের Name , Duration থাকবে , এবং এদের জন্য আলাদা আলাদা CourseDuration এবং CourseMatarial বানাতে হবে । সো জিনিস গুলো একটি Abstract Class এ নিয়ে যাওয়াই ভালো । তারপর সে লিখে ফেললো তার কাংখিত Abstract Class টি । ঠিক এভাবে:

   public abstract class AbstractCourse
    {
        protected String Name { get; set; }
        protected Double Duraion { get; set; }
        public abstract void CreateCourseMaterial();
        public abstract void CreateCourseSchedule();
        public abstract void DisplayCourseDetails();
    }

এরপর সে Java এবং .Net এর দুটি কোর্স বানালো AbstractCourse কে ইমপ্লিমেন্ট করে ঠিক এভাবে । জাভার জন্য এভাবেঃ

public class OJava : AbstractCourse
    {
        public OJava()
        {
            Name = "Online Java";
            Duraion = 2.00;
        }
        public override void CreateCourseMaterial()
        {
            Console.WriteLine("Creating Online Course Material for Java");
        }

        public override void CreateCourseSchedule()
        {
            Console.WriteLine("Creating Online Course Schedule for Java");
        }

        public override void DisplayCourseDetails()
        {
            Console.WriteLine("Course Name:{0} Duration:{1} hour", Name, Duraion);
        }
    }

ডট নেটের জন্য এভাবেঃ

public class ONet : AbstractCourse
    {
        public ONet()
        {
            Name = "Online .Net";
            Duraion = 1.30;
        }
        public override void CreateCourseMaterial()
        {
            Console.WriteLine("Creating Online Course Material for .NET");
        }
        public override void CreateCourseSchedule()
        {
            Console.WriteLine("Creating Online Course Schedule for .NET");
        }
        public override void DisplayCourseDetails()
        {
            Console.WriteLine("Course Name:{0} Duration:{1} hour", Name, Duraion);
        }
    }

এর পর যদু জাভা এবং ডট নেট এর Instance বানালো এবং এর জন্য ইউজার ইন্টারফেস বানানো শুরু করলো । ধরা যাক যদুর Client Code এ রকমঃ

public class Program
    {
        public static void Main(string[] args)
        {
            AbstractCourse abstractCourse = null;

            String courseName = Console.ReadLine();

            if (courseName.Equals("Net", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new ONet();
            }
            else if (courseName.Equals("Java", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new OJava();
            }

            abstractCourse.CreateCourseMaterial();
            abstractCourse.CreateCourseSchedule();
            abstractCourse.DisplayCourseDetails();

        }

    }

এ প্রোগ্রাম রান করে ইনপুট হিসেবে “java” দিলে এটি জাভার জন্য CourseMaterial , CourseSchedule এবং এর Details দেখাবে ।

পরের দিন যদু তার টিম লিডারের কাছে নিয়ে গেলো । টিম লিডার দেখার আগেই তিনি যদুকে বললেন তার ভবিষ্যৎ প্ল্যানের কথা । তিনি জানালেন যে ভবিষ্যতে তারা আরোও নতুন কিছু কোর্স চালু করবেন যেমন VB,C++,Python ইত্যাদি । সাথে হয়তো কিছু পুরাতন কোর্স বাতিল করবেন ।

তিনি আরোও জানালেন যে তারা শুধুমাত্র অনলাইনে কোর্স না নিয়ে অফলাইনেও কোর্স নিবেন । সেক্ষেত্রে শিক্ষার্থীকে শুধুমাত্র রেজিস্ট্রেশন এর মাধ্যমে কনফার্ম করতে হবে সে কোন কোর্স শিখতে চায় । এরপর সশরীরে লেকচার রুমে ঢুকলেই সে অফলাইনে যে কোনও কোর্সে অংশগ্রহন করতে পারবে ।

এ কথাগুলো শোনার পর যদু থ হয়ে দাঁড়িয়ে রইলো । তার কাছে যেনো মনে হলো যে কোনও বিল্ডারকে বলা হয়েছে যে দুই তলা এবং তিন তলার মাঝে আরেক তলা বানিয়ে দিতে যেনো দুই তলা এবং তিন তলার কোনও ক্ষতি না হয় ।

নতুন কোর্স যোগ করা মানে ইউজার ইন্টারফেস এর লজিক চেঞ্জ করা ,নতুন If else যোগ করা ।ভবিষ্যতে কোনও কোর্স বাদ দেয়া মানে If else লজিক বাদ দেয়া।

আসলে এই Approach এর সমস্যাটা কি ??

১) SOLID প্রিন্সিপাল এর OCP (Open Closed Principle) এর ভায়োলেশন ।

OCP বলে যে “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification” মানে Class গুলো এমন ভাবে লিখতে হবে যাতে করে এর ভেতরের কোনও কিছু পরিবর্তন না করে আমরা একে Reuse করতে পারি ।

এখানে আমরা যখন নতুন করে কোনও কোর্স যোগ করব অথবা কোনও কোর্স বাদ দিবো তখন কিন্তু আমাদের Existing Code এ Modification আনতে হবে । মানে If else যোগ করতে হবে অথবা বাদ দিতে হবে ।

২) আমাদের এপ্ললিকেশনের ধরা যাক ১০০০ যায়গায় আমাদের যে কোনও লজিকের ভিত্তিতে ১ টি কোর্স লাগবে তাহলে কি আমরা Client Code এ (উপরের main program ) একটি কোর্স পাওয়ার জন্য এতোগুলো If else ইউজ করে কোড লিখবো ?? অবশ্যই না ।

সমাধানঃ যদু অনেকক্ষণ চিন্তা করে দেখলো যে , সে এতক্ষণ Client Code থেকে Object বানাচ্ছে । যদি Object Creation এর দায়িত্বটা অন্য কাউকে দিয়ে দেয়া যায় তাহলেই হচ্ছে ।

সে CourseFactory নামে একটি ক্লাস বানালো এবং একটি মেথড বানালো যেটি কিনা ইনপুটের ভিত্তিতে AbstractCourse রিটার্ন করবে ।

public class CourseFactory
    {
        public static AbstractCourse CreateCourse(String courseName)
        {
            AbstractCourse abstractCourse = null;
            if (courseName.Equals("ONet", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new ONet();
            }
            else if (courseName.Equals("OJava", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new OJava();
            }
            else if (courseName.Equals("CJava", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new CJava();
            }
            else if (courseName.Equals("CNet", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new CNet();
            }
            abstractCourse.CreateCourseMaterial();
            abstractCourse.CreateCourseSchedule();
            return abstractCourse;
        }
    }

তাহলে আমাদের Client Code এর কাঁধ থেকে Object Creation এর দায়িত্ব কমে গেলো । আমাদের Client Code এখন দেখতে অনেক সুন্দর এবং পরিপাটিঃ 🙂

public class Program
    {
        public static void Main(string[] args)
        {
            String courseName = Console.ReadLine();
            AbstractCourse abstractCourse = CourseFactory.CreateCourse(courseName);
            abstractCourse.DisplayCourseDetails();
            Console.ReadKey();
        }
    }

এখন নতুন কোনও কোর্স যোগ হলে অথবা বাদ গেলে আমাদের Client Code এ কোনও চেঞ্জ করতে হবে না । যা করতে হবে সব ঐ CourseFactory এর মধ্যে । কেউ যদি নতুন কোর্স বানাতে চায় তাহলে CourseFactory তে বানালেই হবে , মোট কথা হচ্ছে CourseCreation Logic আমরা Reuse করতে পারবো 🙂

এটাই হচ্ছে Factory Method Pattern 🙂

এর পর যদু খুশি মনে টিম লিডারের কাছে গেলো এবং লেটেস্ট ডিজাইনটা দেখানোর আগেই অফলাইনের কোর্সগুলা যোগ করে আনতে বললো । যদু নিশ্চিন্তে আপন মনে Java এবং .Net এর অফলাইন ভার্সনের জন্য দুটি আলাদা কোর্স বানিয়ে CourseFactory তে লজিক এড করতে লাগলো । অফলাইনের জন্য Java কোর্স টা দেখতে এরকমঃ

public class CJava : AbstractCourse
    {
        public CJava()
        {
            Name = "Offline Java";
            Duraion = 3.00;
        }
        public override void CreateCourseMaterial()
        {
            Console.WriteLine("Creating Offline Course Material for Java");
        }

        public override void CreateCourseSchedule()
        {
            Console.WriteLine("Creating Offline Course Schedule for Java");
        }

        public override void DisplayCourseDetails()
        {
            Console.WriteLine("Course Name:{0} Duration:{1} hour", Name, Duraion);
        }
    }

এবং অফলাইনের জন্য Net কোর্স টি দেখতে ঠিক এরকম:

public class CNet : AbstractCourse
    {
        public CNet()
        {
            Name = "Offline .Net";
            Duraion = 2.30;

        }
        public override void CreateCourseMaterial()
        {
            Console.WriteLine("Creating Offline Course Material for .NET");
        }

        public override void CreateCourseSchedule()
        {
            Console.WriteLine("Creating Offline Course Schedule for .NET");
        }

        public override void DisplayCourseDetails()
        {
            Console.WriteLine("Course Name:{0} Duration:{1} hour", Name, Duraion);
        }
    }

এবং এই নতুন দুটি কোর্স CourseFactory যোগ করার পর আমাদের নতুন CourseFactory দেখেতে ঠিক এরকম হবেঃ

public class CourseFactory
    {
        public static AbstractCourse CreateCourse(String courseName)
        {
            AbstractCourse abstractCourse = null;
            if (courseName.Equals("ONet", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new ONet();
            }
            else if (courseName.Equals("OJava", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new OJava();
            }
            else if (courseName.Equals("CJava", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new CJava();
            }
            else if (courseName.Equals("CNet", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new CNet();
            }
            abstractCourse.CreateCourseMaterial();
            abstractCourse.CreateCourseSchedule();
            return abstractCourse;
        }
    }

যদু CourseFacotory তে নতুন দুটো কোর্স যোগ করার পর তার মনে হল যে , সে একই class এ ভিন্ন ধরনের দুটো কাজ করছে । প্রথমত সে অফলাইনের জন্য কোর্সগুলো যোগ করছে দ্বিতীয়ত সে অনলাইনের জন্য কোর্সগুলো যোগ করছে । সে OOP এর SOLID প্রিন্সিপলের কথা মনে করলো । এর একটি হচ্ছে SRP মানে Single Responsibility Principle মানে একটি ক্লাস/মেথড শুধুমাত্র একটি কাজ করবে । এখানে CourseFactory দুটি কাজ করছে ।যদু মনে মনে ঠিক করলো সে এবার নিজেই এই সমস্যা সল্ভ করবে ।

সে এই ভেবে CourseFactory কে ভেঙ্গে দুটি আলাদা আলাদা  Class বানালো । একটি class বানালো অনলাইন কোর্সের জন্য আরেকটি class বানালো অফলাইনের কোর্সের জন্য ।

অনলাইনের কোর্স বানানোর দায়িত্ব দিলো সে OnlineCourseFacotory কে এই ভাবেঃ

public class OnlineCourseFactory : AbstractCourseFactory
    {
        protected override AbstractCourse GetCourse(string courseName)
        {
            AbstractCourse abstractCourse = null;
            if (courseName.Equals("ONet", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new ONet();
            }
            else if (courseName.Equals("OJava", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new OJava();
            }
            return abstractCourse;
        }
    }

এবং অফলাইনের জন্য বানালো OfflineCourseFactory এই ভাবেঃ

 public class OfflineCourseFactory : AbstractCourseFactory
    {
        protected override AbstractCourse GetCourse(string courseName)
        {
            AbstractCourse abstractCourse = null;
            if (courseName.Equals("CJava", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new CJava();
            }
            else if (courseName.Equals("CNet", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new CNet();
            }
            return abstractCourse;
        }
    }

এরপর যদু তার ClientCode চেঞ্জ করলো এইভাবেঃ

public class Program
    {
        public static void Main(string[] args)
        {
            String courseName = Console.ReadLine();
            AbstractCourse abstractCourse = OfflineCourseFactory.CreateCourse(courseName);
            abstractCourse.DisplayCourseDetails();

            courseName = Console.ReadLine();
            abstractCourse = OnlineCourseFactory.CreateCourse(courseName);
            abstractCourse.DisplayCourseDetails();
            Console.ReadKey();
        }
    }

এর পর যদু খুশি মনে টিম লিডারের কাছে নিয়ে গেলো । টিমলিডার কোড রিভিউ করা শুরু করলো । এক পর্যায়ে  এসে আলাদা আলাদা CourseFactory দেখে সে থেমে গেলো । সে যদু কে বললো “এইটা কি করছো?? কিসসু হয় নাই” । যদু তার টিমলিডারের কাছে এ রকম ফিডব্যাক শুনে ঠায় দাড়ীয়ে রইলো । টিম লিডার এরপর যদুর কোডের দুর্বল পয়েন্টগুলো  এক এক করে বলতে লাগলো ।

টিমলিডার বলল যে “দেখো যদু তুমি যে আলাদা আলাদা Factory বানাইছো এইটার উপর তোমার কন্ট্রোল আছে এইটা কিভাবে শিওর করবা ?? মনে কর তোমার OfflineCourseFacotory এর CreateCourse মেথডটা অবজেক্ট বানাইয়া রিটার্ন করে । রিটার্ন করার আগে সে CourseMatarial এবং CourseSchedule বানাইয়া রিটার্ন করে এবং তুমি তোমার ClientCode এ কোর্সের ডিটেইলস দেখাও। ধর ভবিষ্যতে আমি আরোও একপ্রকার কোর্স যোগ করতে চাই মনে কর Database এর কোর্স , তো আমার আরেকটা ফ্যাক্টরি লাগবে , সেই ফ্যাক্টরির আরেকটা সেম মেথড থাকবে CreateCourse নামে । এই মেথড কোর্সের অবজেক্ট বানাইয়া রিটার্ন করবে । এখন তুমি জানো অবজেক্ট গুলা রিটার্ন করার আগে এর CourseMatarial এবং CourseSchedule বানাইয়া রিটার্ন করা লাগে , যখন তুমি থাকবানা তখন যদি আমার নতুন টাইপের কোর্স বানাইতে হয় তোমার জায়গায় যে আসবে সে যদি DatabaseCourseFactory থাইকা কোর্স বানাইয়া রিটার্ন করার আগে এর CourseSchedule এবং CourseMatarial না বানাইয়া রিটার্ন করে তাইলে তো DatabaseCourse গুলা এপ্লিকেশনে কাজই করবে না ।

আর একটা কথা তোমার এখানে দুই টাইপের কোর্স আছে যাদের মেথড একই এবং এরা একই রকমের কাজ করে । (CreateCourse()) তার মানে আমার ১০০০ টা কোর্স হইলে আমারে একই মেথড হাজার বার লিখতে হবে । এইটা ভালো কথা না। তোমার এই কোড আমাদের মান সম্মানের ১২টা বাজাবে । যাও এর চেয়ে ভালো কোনও সল্যুশন নিয়া আসো

ঝাড়ি খেয়ে যদু সমস্যাটা বুঝতে পারলো । সে বুঝতে পারলো যে কোনও উপায়ে যদি নতুন কোর্স বানানোর সময়ই নতুন ডেভেলপারকে ফোরস করা যায় যে কোনও কোর্স বানালেই অটোমেটিক তার CourseMatarial এবং CourseSchedule বানিয়ে কোর্সটি রিটার্ন করবে তাইলে আর সমস্যা থাকে না । আর যেহেতু একই কাজ বার বার করতে হচ্ছে সেহেতু এই কাজ গুলো একটি Abstract class এ করলেই কাজ হয়ে যাচ্ছে । সবাই এই class কে extend করে নতুন নতুন কোর্স বানাবে তাতে করে একই কাজ বারবার করতে হবে না আবার নতুন কোর্সটি রিটার্ন করার আগে অটোমেটিক ভাবে CourseMatarial এবং CourseSchedule বানিয়ে রিটার্ন করবে ।

তো যদুর সর্বশেষ সল্যুশনটি ঠিক এরকমঃ

public abstract class AbstractCourseFactory
    {
        public AbstractCourse CreateCourse(string courseName)
        {
            AbstractCourse abstractCourse = GetCourse(courseName);
            abstractCourse.CreateCourseMaterial();
            abstractCourse.CreateCourseSchedule();
            return abstractCourse;
        }
        protected abstract AbstractCourse GetCourse(string courseName);
    }

এখন OnlineCourseFactory এবং OfflineCourseFactory এবং ভবিষ্যতের কোর্সফ্যাক্টরি সবাই AbstractCourseFactory কে extend করেই নতুন নতুন কোর্স বানাবে ।

যদু OfflineCourseFactory এবং OnlineCourseFactory দুটি চেঞ্জ করে

নিচের মত করল-

OfflineCourseFactory দেখতে ঠিক এরকমঃ

public class OfflineCourseFactory : AbstractCourseFactory
    {
        protected override AbstractCourse GetCourse(string courseName)
        {
            AbstractCourse abstractCourse = null;
            if (courseName.Equals("CJava", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new CJava();
            }
            else if (courseName.Equals("CNet", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new CNet();
            }
            return abstractCourse;
        }
    }

OnlineCourseFactory দেখতে ঠিক এরকমঃ

 public class OnlineCourseFactory : AbstractCourseFactory
    {
        protected override AbstractCourse GetCourse(string courseName)
        {
            AbstractCourse abstractCourse = null;
            if (courseName.Equals("ONet", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new ONet();
            }
            else if (courseName.Equals("OJava", StringComparison.OrdinalIgnoreCase))
            {
                abstractCourse = new OJava();
            }
            return abstractCourse;
        }
    }

Client Code চেঞ্জ হয়ে ঠিক এরকম হবেঃ

public class Program
    {
        public static void Main(string[] args)
        {
            String courseName = Console.ReadLine();
            var offlineCourseFactory = new OfflineCourseFactory();
            AbstractCourse abstractCourse = offlineCourseFactory.CreateCourse(courseName);
            abstractCourse.DisplayCourseDetails();

            courseName = Console.ReadLine();
            var onlineCourseFactory  =new OnlineCourseFactory();
            abstractCourse = onlineCourseFactory.CreateCourse(courseName);
            abstractCourse.DisplayCourseDetails();

            Console.ReadKey();
        }
    }

যদুকে আর কখনও ঝাড়ি খেতে হয়নি এই কোডের জন্য ।

গল্প এখানেই শেষ ।আমাদের কোডও শেষ । আশাকরি আপনাদের জগের চাও অনেক আগেই শেষ হয়ে গেছে। এই যে এতক্ষণ বকবক করলাম এইটাই হচ্ছে Abstract Factory Pattern.

হ্যাপি কোডিং 🙂

Life Runs On Code 🙂

Advertisements

2 thoughts on “ফ্যাক্টরি প্যাটার্নের পোস্টমর্টেম (Factory Method এবং Abstract Factory)

  1. অনেক উদাহরণসমৃদ্ধ লেখা। বাংলায় এমন লেখা খুবই কম পায়া যায়। লেখালিখি চালিয়ে রাখুন

    ধন্যবাদ

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s