Strategy Design Pattern

সফটওয়্যার ডেভেলপমেন্টে অনেক সময় এমন পরিস্থিতি আসে যেখানে একটি নির্দিষ্ট কাজ করার জন্য আমাদের কাছে একাধিক উপায় বা অ্যালগরিদম থাকে। এবং আমরা চাই ক্লায়েন্ট বা ইউজারের প্রয়োজন অনুযায়ী রানটাইমে (Runtime) সেই উপায়গুলো পরিবর্তন করতে। এই ধরনের সমস্যা সমাধানের জন্য Strategy Design Pattern হলো সবচেয়ে নিখুঁত এবং কার্যকর সমাধান।

এটি একটি Behavioral Design Pattern, যা মূলত একগুচ্ছ অ্যালগরিদম বা লজিককে ডিফাইন করে, সেগুলোকে আলাদা আলাদা ক্লাসে এনক্যাপসুলেট (Encapsulate) করে এবং তাদের একে অপরের পরিপূরক বা ইন্টারচেঞ্জেবল (Interchangeable) করে তোলে।

বাস্তব জীবনে Strategy Pattern-এর ব্যবহার

কোথায় এবং কেন তুমি Strategy Pattern ব্যবহার করবে, তার কিছু চমৎকার বাস্তব উদাহরণ নিচে দেওয়া হলো:

  • Database Selection: আজকে তুমি MySQL ব্যবহার করছ, কিন্তু ভবিষ্যতে ক্লায়েন্ট হয়তো PostgreSQL, Oracle বা MongoDB চাইতে পারে। Strategy Pattern ব্যবহার করলে মূল কোড পরিবর্তন না করেই খুব সহজে ডেটাবেস সুইচ করা যায়।

  • Authentication System: তোমার অ্যাপ্লিকেশনে ইউজাররা OAuth, JWT, বা API Key—যেকোনো মাধ্যমে লগইন করতে পারে। প্রতিটি অথেনটিকেশন মেথডের জন্য আলাদা Strategy ক্লাস বানিয়ে নিলে সিস্টেম অনেক বেশি ফ্লেক্সিবল হয়।

  • Caching Mechanism: ডেটা ক্যাশ করার জন্য Redis, Memcached বা Local File System ব্যবহার করা যেতে পারে। পরিবেশ (Environment) অনুযায়ী কোন ক্যাশিং ব্যবহার করা হবে, তা নির্ধারণ করতে এই প্যাটার্ন অসাধারণ কাজ করে।

  • Payment Gateway Integration: ই-কমার্স সাইটে Stripe, PayPal, SSLCommerz বা Crypto পেমেন্ট অপশন থাকে। রানটাইমে ইউজারের পছন্দ অনুযায়ী পেমেন্ট মেথড প্রসেস করতে Strategy Pattern-এর বিকল্প নেই।

  • Sorting ও Compression Algorithms: ডেটা সর্ট করার জন্য (Bubble Sort, Quick Sort) অথবা ফাইল কম্প্রেস করার জন্য (ZIP, RAR, TAR) বিভিন্ন অ্যালগরিদম আলাদা স্ট্র্যাটেজি হিসেবে রাখা যায়।

সমস্যার শুরু: The "If-Else" Hell (Bad Practice)

ধরা যাক, 'John' একজন সফটওয়্যার ইঞ্জিনিয়ার, যিনি 'XYZ' নামের একটি ই-কমার্স কোম্পানিতে কাজ করেন। শুরুতে তাদের সিস্টেমে শুধুমাত্র PayPal পেমেন্ট ছিল। কোড ছিল খুব সাধারণ। কিন্তু ব্যবসার পরিধি বাড়ার সাথে সাথে Credit Card এবং Bank Transfer যুক্ত হলো।

John নিচের মতো করে পাইথনে (Python) কোড লিখলেন:

class PaymentProcessor:
    def pay(self, amount: float, method: str):
        # High Coupling and OCP Violation
        if method == "paypal":
            print(f"Processing payment of ${amount} using PayPal.")
        elif method == "credit_card":
            print(f"Processing payment of ${amount} using Credit Card.")
        elif method == "bank_transfer":
            print(f"Processing payment of ${amount} using Bank Transfer.")
        else:
            raise ValueError("Unsupported payment method!")

এখানে সমস্যা কী?

১. Open/Closed Principle (OCP) ভঙ্গ: নতুন কোনো পেমেন্ট মেথড (যেমন: Crypto) যোগ করতে হলে এই মূল ক্লাসের ভেতরে এসে নতুন elif যোগ করতে হবে।

২. Maintenance Nightmare: ৫-১০টি পেমেন্ট মেথড হয়ে গেলে এই কোডটি একটি বিশাল এবং জটিল ব্লকে পরিণত হবে, যা মেইনটেইন বা ডিবাগ করা প্রায় অসম্ভব।

৩. High Coupling: পেমেন্ট প্রসেসিং লজিক এবং পেমেন্টের ধরন—সবকিছু একই জায়গায় জট পাকিয়ে আছে (Low Cohesion)।

Strategy Pattern ইমপ্লিমেন্টেশন (Good Practice)

একদিন কোম্পানির CTO John কে বললেন, "আমরা Cryptocurrency পেমেন্ট যোগ করতে চাই এবং ভবিষ্যতে আরও গেটওয়ে আসবে। এটিকে ফ্লেক্সিবল করো।" John তখন Strategy Pattern ব্যবহার করে পুরো সিস্টেমকে রিফ্যাক্টর করলেন।

Step 1: Strategy Interface তৈরি করা

পাইথনে আমরা ABC (Abstract Base Class) ব্যবহার করে একটি ইন্টারফেস তৈরি করব, যা প্রতিটি পেমেন্ট মেথডকে অবশ্যই মেনে চলতে হবে।

from abc import ABC, abstractmethod

# The Strategy Interface
class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount: float):
        pass

Step 2: আলাদা আলাদা Payment Strategy তৈরি করা

এখন প্রতিটি পেমেন্ট মেথডের জন্য আমরা আলাদা স্বাধীন ক্লাস তৈরি করব। এগুলো মূল সিস্টেমের ওপর কোনো প্রভাব ফেলবে না।

# Concrete Strategy 1
class PayPalPayment(PaymentStrategy):
    def pay(self, amount: float):
        print(f"Processing ${amount} using PayPal.")

# Concrete Strategy 2
class CreditCardPayment(PaymentStrategy):
    def pay(self, amount: float):
        print(f"Processing ${amount} using Credit Card.")

# Concrete Strategy 3
class BankTransferPayment(PaymentStrategy):
    def pay(self, amount: float):
        print(f"Processing ${amount} using Bank Transfer.")

# New Strategy added without touching existing code
class CryptoPayment(PaymentStrategy):
    def pay(self, amount: float):
        print(f"Processing ${amount} using Cryptocurrency.")

Step 3: Context Class তৈরি করা

এই ক্লাসটি ইউজারের পছন্দ অনুযায়ী রানটাইমে নির্দিষ্ট Strategy গ্রহণ করবে এবং তা এক্সিকিউট করবে।

# The Context Class
class PaymentContext:
    def __init__(self, strategy: PaymentStrategy):
        self._strategy = strategy

    # Allows changing the strategy at runtime
    def set_payment_strategy(self, strategy: PaymentStrategy):
        self._strategy = strategy

    def execute_payment(self, amount: float):
        self._strategy.pay(amount)

Step 4: Client Code (Dynamic Payment Selection)

এখন মূল অ্যাপ্লিকেশনে কোনো if-else ছাড়াই ডাইনামিকভাবে পেমেন্ট মেথড পরিবর্তন করা যাবে।

if __name__ == "__main__":
    # Paying via PayPal
    context = PaymentContext(PayPalPayment())
    context.execute_payment(100.00)

    # Switching to Credit Card dynamically
    context.set_payment_strategy(CreditCardPayment())
    context.execute_payment(250.50)

    # Switching to Crypto dynamically
    context.set_payment_strategy(CryptoPayment())
    context.execute_payment(500.00)

Strategy Pattern এর গভীরে থাকা সুবিধাসমূহ

  • Dynamic Switching: রানটাইমে অবজেক্টের বিহেভিয়ার বা অ্যালগরিদম খুব সহজেই পরিবর্তন করা যায়।

  • No More If-Else Mess: বিশাল কন্ডিশনাল স্ট্রাকচার এড়ানো যায়, ফলে কোড অনেক বেশি ক্লিন এবং রিডেবল (Readable) হয়।

  • Easy to Extend (OCP): নতুন কোনো মেথড যোগ করতে চাইলে পুরনো কোডে হাত দেওয়ার দরকার নেই, শুধু একটি নতুন Strategy ক্লাস যুক্ত করে দিলেই হয়।

  • Testability: প্রতিটি অ্যালগরিদম বা লজিক আলাদা ক্লাসে থাকায় সেগুলোকে স্বাধীনভাবে ইউনিট টেস্ট (Unit Test) করা অত্যন্ত সহজ।

মডার্ন ফ্রেমওয়ার্কগুলোতে (Springboot, Django) Strategy Pattern এর ব্যবহার

আধুনিক ওয়েব ফ্রেমওয়ার্কগুলো কোর আর্কিটেকচারে Strategy Pattern-এর ওপর ব্যাপকভাবে নির্ভরশীল। নিচে তার একটি তালিকা দেওয়া হলো:

ফিচারের নাম

কীভাবে Strategy Pattern ব্যবহৃত হয়

Database ORM

ডাইনামিকভাবে ডেটাবেস ড্রাইভার (MySQL, PostgreSQL, SQLite, Oracle) নির্বাচন করতে সাহায্য করে।

Authentication

মাল্টিপল অথেনটিকেশন মেকানিজম (Session, Token, JWT, OAuth) সাপোর্ট করে এবং সহজে সুইচ করা যায়।

Caching System

প্রোডাকশন বা লোকাল এনভায়রনমেন্ট অনুযায়ী ক্যাশে ব্যাকএন্ড (File, Redis, Memcached, Database) পরিবর্তন করার সুযোগ দেয়।

Queue Workers

ব্যাকগ্রাউন্ড টাস্ক প্রসেসিংয়ের জন্য বিভিন্ন কিউ সিস্টেম (Database, Redis, AWS SQS, RabbitMQ) সাপোর্ট করে।

Logging

লগ স্টোর করার জন্য বিভিন্ন চ্যানেল (Single file, Daily, Slack, AWS CloudWatch) ব্যবহারের সুবিধা প্রদান করে।

File Storage

ফাইল আপলোডের জন্য লোকাল স্টোরেজ (Local Disk) বা ক্লাউড স্টোরেজ (Amazon S3, Google Cloud Storage)-এর মধ্যে অনায়াসে সুইচ করা যায়।

Strategy Pattern মূলত সফটওয়্যারের আর্কিটেকচারকে মডুলার এবং ভবিষ্যৎমুখী করে তোলে। ভালো ডিজাইন প্যাটার্ন মানেই হলো সহজ স্কেলেবিলিটি এবং চিন্তামুক্ত মেইনটেন্যান্স!