Strategy (স্ট্র্যাটেজি) একটি আচরণগত ডিজাইন প্যাটার্ন যা বিনিময়যোগ্য অ্যালগরিদমের একটি পরিবার সংজ্ঞায়িত করে এবং তাদের প্রতিটিকে একটি পৃথক ক্লাসে (Strategy) রাখে। প্যাটার্নটি চলার পথে অ্যালগরিদম নির্বাচনের অনুমতি দেয়: ক্লায়েন্ট কোড একটি সাধারণ Strategy ইন্টারফেসের মাধ্যমে কাজ করে এবং নির্দিষ্ট বাস্তবায়ন রানটাইমে প্রতিস্থাপিত হয়। iOS-এ, প্যাটার্নটি Protocol + স্ট্র্যাটেজি ক্লাসের মাধ্যমে বাস্তবায়িত হয়, Android-এ — Interface + বাস্তবায়নের মাধ্যমে। Strategy 23টি GoF প্যাটার্নের মধ্যে একটি, যা পেমেন্ট প্রসেসিং, ভ্যালিডেশন, সর্টিং এবং ডেটা ফিল্টারিংয়ের জন্য ব্যাপকভাবে ব্যবহৃত হয়। আরও বিস্তারিত জানতে — মূল GoF বিবরণ দেখুন।
মূল বিষয়
Strategy 23টি GoF (Gang of Four) প্যাটার্নের মধ্যে একটি, যা "Design Patterns: Elements of Reusable Object-Oriented Software" (1994) বইয়ে বর্ণিত। প্যাটার্নটি রানটাইমে অ্যালগরিদম নির্বাচনের সমস্যা সমাধান করে। একাধিক শর্তসাপেক্ষ বিবৃতি (if-else, switch) সহ একটি ক্লাস লেখার পরিবর্তে, Strategy প্রতিটি অ্যালগরিদমকে একটি সাধারণ ইন্টারফেসের সাথে পৃথক ক্লাসে নিষ্কাশনের প্রস্তাব দেয়। কনটেক্সট (যে ক্লাস স্ট্র্যাটেজি ব্যবহার করে) Strategy ইন্টারফেসের রেফারেন্স ধারণ করে এবং নির্বাহ কংক্রিট স্ট্র্যাটেজির কাছে অর্পণ করে।
প্যাটার্নের কাঠামো তিনটি উপাদান অন্তর্ভুক্ত করে: Context (কনটেক্সট) Strategy-এর রেফারেন্স ধারণ করে এবং এর মেথড কল করে; Strategy (ইন্টারফেস) সব অ্যালগরিদমের জন্য একটি সাধারণ মেথড ঘোষণা করে; ConcreteStrategy (কংক্রিট স্ট্র্যাটেজি) ইন্টারফেস বাস্তবায়ন করে এবং কংক্রিট অ্যালগরিদম ধারণ করে। ক্লায়েন্ট কাঙ্ক্ষিত স্ট্র্যাটেজি তৈরি করে এবং এটি কনস্ট্রাক্টর, সেটার বা মেথড প্যারামিটারের মাধ্যমে কনটেক্সটে পাঠায়। কনটেক্সট জানে না কোন নির্দিষ্ট স্ট্র্যাটেজি নির্বাহ হচ্ছে — এটি শুধু ইন্টারফেসের সাথে কাজ করে।
| উপাদান | ভূমিকা | উদাহরণ |
|---|---|---|
| Context | Strategy-এর রেফারেন্স ধারণ করে | PaymentProcessor, Sorter |
| Strategy | অ্যালগরিদমের জন্য সাধারণ ইন্টারফেস | Protocol PaymentStrategy |
| ConcreteStrategy | অ্যালগরিদমের কংক্রিট বাস্তবায়ন | CardPayment, PayPalPayment |
Open/Closed নীতি — Strategy-এর প্রধান সুবিধা। সিস্টেম সম্প্রসারণের জন্য উন্মুক্ত (নতুন স্ট্র্যাটেজি যোগ করা যায়) এবং পরিবর্তনের জন্য বন্ধ (কনটেক্সট কোড পরিবর্তনের প্রয়োজন নেই)। প্যাটার্ন ছাড়া, নতুন অ্যালগরিদম যোগ করতে বিদ্যমান ক্লাস পরিবর্তন করা প্রয়োজন, যা OCP লঙ্ঘন করে এবং রিগ্রেশন ত্রুটির ঝুঁকি বাড়ায়। Strategy ক্লাসের আকারও কমায়: switch-case সহ 200-লাইনের ক্লাসের পরিবর্তে, আপনি প্রতিটি 20 লাইনের 6টি ক্লাস পান।
Swift-এ Strategy Protocol (স্ট্র্যাটেজি ইন্টারফেস) এবং স্ট্র্যাটেজি ক্লাস বা স্ট্রাক্টের মাধ্যমে বাস্তবায়িত হয়। Swift প্রোটোকল অ্যাসোসিয়েটেড টাইপ এবং জেনেরিক সীমাবদ্ধতা সমর্থন করে, যা স্ট্র্যাটেজি ডিজাইন করার সময় নমনীয়তা প্রদান করে। কনটেক্সট সাধারণত একটি ViewModel ক্লাস বা সার্ভিস যা init-এ বা প্রপার্টির মাধ্যমে স্ট্র্যাটেজি গ্রহণ করে। প্যাটার্নটি iOS প্রকল্পে ইভেন্ট হ্যান্ডলিং, অ্যানিমেশন, ডেটা ফরম্যাটিং এবং UI স্ট্র্যাটেজির জন্য ব্যাপকভাবে ব্যবহৃত হয়।
// 1. Protocol Strategy
protocol PaymentStrategy {
func pay(amount: Decimal) async throws -> PaymentResult
}
// 2. Concrete Strategies
struct CardPaymentStrategy: PaymentStrategy {
let cardNumber: String
let cvv: String
func pay(amount: Decimal) async throws -> PaymentResult {
// ব্যাংকিং API-তে অনুরোধ পাঠানো
return PaymentResult(status: .success, transactionId: "tx_\(UUID())")
}
}
struct PayPalPaymentStrategy: PaymentStrategy {
let email: String
func pay(amount: Decimal) async throws -> PaymentResult {
// PayPal SDK-তে পুনঃনির্দেশ
return PaymentResult(status: .success, transactionId: "pp_\(UUID())")
}
}
// 3. Context
class PaymentProcessor {
private var strategy: PaymentStrategy
init(strategy: PaymentStrategy) {
self.strategy = strategy
}
func setStrategy(_: PaymentStrategy) {
strategy = strategy
}
func processPayment(amount: Decimal) async throws -> PaymentResult {
return try await strategy.pay(amount: amount)
}
}
// ব্যবহার
let processor = PaymentProcessor(strategy: CardPaymentStrategy(cardNumber: "4111...", cvv: "123"))
let result = try await processor.processPayment(amount: 99.99)
SwiftUI-এ Strategy — প্যাটার্নটি স্বাভাবিকভাবে MVVM-এর সাথে সংহত হয়। ViewModel-এ একটি স্ট্র্যাটেজি প্রপার্টি থাকে এবং ব্যবহারকারীর কর্মে এর মেথড কল করে। SwiftUI View @Published বা @State-এর মাধ্যমে ডেটা গ্রহণ করে — স্ট্র্যাটেজি View থেকে বাস্তবায়নের বিবরণ লুকিয়ে রাখে। উদাহরণস্বরূপ, ইনপুট ফিল্ডের ধরনের উপর ভিত্তি করে টেক্সট ভ্যালিডেশন স্ট্র্যাটেজি (emailValidator, phoneValidator) পরিবর্তন করা হয়। SwiftUI-এর সাথে Strategy একত্রিত করলে UIKit থেকে ইনহেরিট না করেই নমনীয়তা পাওয়া যায়।
Kotlin-এ Strategy ভাষা স্তরে Interface এবং সরলীকরণের জন্য ফাংশনাল ইন্টারফেস (SAM) ব্যবহার করে। Kotlin lambda সমর্থন করে, যা আলাদা স্ট্র্যাটেজি ক্লাস ঘোষণা না করেই ফাংশন হিসাবে অ্যালগরিদম পাস করার অনুমতি দেয়। Android-এ, প্যাটার্নটি ViewModel এবং Use Cases-এ ডেটা লোডিং, ক্যাশিং এবং ত্রুটি হ্যান্ডলিং অ্যালগরিদম বিচ্ছিন্ন করতে ব্যবহৃত হয়। Clean Architecture সহ Android প্রকল্পগুলি ফ্ল্যাগ (mock, real, cache) অনুসারে বিভিন্ন রিপোজিটরি বাস্তবায়ন ইনজেক্ট করতে Strategy ব্যবহার করে।
// 1. Interface Strategy
interface PaymentStrategy {
suspend fun pay(amount: BigDecimal): PaymentResult
}
// 2. Concrete Strategies
class CardPaymentStrategy(
private val cardNumber: String,
private val cvv: String
) : PaymentStrategy {
override suspend fun pay(amount: BigDecimal): PaymentResult {
// Retrofit-এর মাধ্যমে ব্যাংকিং API
return PaymentResult(success = true, transactionId = "tx_${UUID.randomUUID()}")
}
}
class PayPalPaymentStrategy(
private val email: String
) : PaymentStrategy {
override suspend fun pay(amount: BigDecimal): PaymentResult {
// PayPal SDK ইন্টিগ্রেশন
return PaymentResult(success = true, transactionId = "pp_${UUID.randomUUID()}")
}
}
// 3. Context
class PaymentProcessor(
private val strategy: PaymentStrategy
) {
fun setStrategy(strategy: PaymentStrategy): PaymentProcessor {
return PaymentProcessor(strategy)
}
suspend fun processPayment(amount: BigDecimal): PaymentResult {
return strategy.pay(amount)
}
}
// ViewModel-এ ব্যবহার
class CheckoutViewModel : ViewModel() {
private var processor = PaymentProcessor(CardPaymentStrategy("4111...", "123"))
fun payWithCard() {
viewModelScope.launch {
val result = processor.processPayment(BigDecimal("99.99"))
// ফলাফল প্রক্রিয়াকরণ
}
}
}
Hilt/Dagger-এর সাথে Strategy — Android প্রকল্পে, স্ট্র্যাটেজি প্রায়ই DI-এর মাধ্যমে ইনজেক্ট করা হয়। Hilt @Binds বা @Provides-এর মাধ্যমে PaymentStrategy-এর কংক্রিট বাস্তবায়ন প্রদান করে। এটি কনটেক্সট কোড পরিবর্তন না করেই স্ট্র্যাটেজি পরিবর্তনের অনুমতি দেয় — অন্য বিল্ডের (debug/release) জন্য কেবল DI মডিউল পরিবর্তন করুন। উদাহরণস্বরূপ, ডিবাগিংয়ের জন্য MockPaymentStrategy ইনজেক্ট করা হয়, উৎপাদনের জন্য বাস্তব ব্যাংকিং স্ট্র্যাটেজি। Strategy + DI-এর সমন্বয় সর্বোচ্চ নমনীয়তা প্রদান করে।
Strategy vs State — কাঠামোগতভাবে প্যাটার্নগুলি অভিন্ন: উভয়ই একটি ইন্টারফেস এবং কংক্রিট ক্লাসের সাথে কম্পোজিশন ব্যবহার করে। পার্থক্য উদ্দেশ্যে: Strategy একটি স্বাধীন অ্যালগরিদম নির্বাচন করে, State তার অবস্থার উপর নির্ভর করে অবজেক্টের আচরণ নিয়ন্ত্রণ করে। State-এ, কনটেক্সট নিজেই স্ট্র্যাটেজি পরিবর্তন করে যখন অবস্থা পরিবর্তিত হয়; Strategy-এ, কনটেক্সট সুইচিং নিয়ন্ত্রণ করে না — ক্লায়েন্ট স্পষ্টভাবে অ্যালগরিদম সেট করে। স্ট্র্যাটেজি একে অপর সম্পর্কে জানে না, অন্যদিকে অবস্থাগুলি একে অপরের মধ্যে রূপান্তরিত হতে পারে।
Strategy vs Command — Command একটি একক কর্মকে অবজেক্ট হিসাবে এনক্যাপসুলেট করে, Strategy বিনিময়যোগ্য অ্যালগরিদমের একটি সেট এনক্যাপসুলেট করে। Command হল "কী করতে হবে" (একটি execute কল), Strategy হল "কীভাবে করতে হবে" (বহু ধাপের অ্যালগরিদম)। Command কিউ, বিলম্বিত নির্বাহ, পূর্বাবস্থা/পুনরায় করার জন্য ব্যবহৃত হয়। Strategy রানটাইমে কাজ সম্পাদনের পদ্ধতি বেছে নিতে ব্যবহৃত হয়। Command-কে Strategy-এর সাথে প্যারামিটারাইজ করা যেতে পারে, উভয় প্যাটার্ন একত্রিত করে।
| বৈশিষ্ট্য | Strategy | State | Command | Template Method |
|---|---|---|---|---|
| উদ্দেশ্য | বিনিময়যোগ্য অ্যালগরিদম | অবস্থা অনুযায়ী আচরণ | অনুরোধ এনক্যাপসুলেশন | অ্যালগরিদমের কঙ্কাল |
| পরিবর্তন | ক্লায়েন্ট দ্বারা স্পষ্টভাবে | কনটেক্সট দ্বারা স্বয়ংক্রিয়ভাবে | ক্লায়েন্ট বা কিউ দ্বারা | ইনহেরিটেন্স দ্বারা |
| স্তর | অবজেক্ট (কম্পোজিশন) | অবজেক্ট (কম্পোজিশন) | অবজেক্ট | ক্লাস (ইনহেরিটেন্স) |
Strategy vs Template Method — উভয় প্যাটার্নই অ্যালগরিদম সংজ্ঞায়িত করে কিন্তু ভিন্ন উপায়ে। Template Method ইনহেরিটেন্স ব্যবহার করে: একটি বেস ক্লাস অ্যালগরিদমের কঙ্কাল (টেমপ্লেট মেথড) সংজ্ঞায়িত করে, উপশ্রেণীগুলি পৃথক ধাপগুলি ওভাররাইড করে। Strategy কম্পোজিশন ব্যবহার করে: অ্যালগরিদম সম্পূর্ণরূপে একটি পৃথক ক্লাসে বাহ্যিকীকৃত হয়। Template Method নির্দিষ্ট অ্যালগরিদম কাঠামোর ক্ষেত্রে সহজ, Strategy — যখন অ্যালগরিদম সম্পূর্ণ ভিন্ন এবং গতিশীলভাবে পরিবর্তিত হতে পারে।
পেমেন্ট প্রসেসিং — Strategy-এর ক্লাসিক উদাহরণ। অনলাইন স্টোরের শপিং কার্টে আইটেমের তালিকা থাকে এবং পেমেন্ট পদ্ধতি ব্যবহারকারী দ্বারা নির্বাচিত হয়। প্রতিটি পদ্ধতি (কার্ড, PayPal, Apple Pay, Google Pay, ক্রিপ্টোকারেন্সি) সাধারণ pay(amount) স্বাক্ষর সহ একটি পৃথক স্ট্র্যাটেজি। PaymentProcessor কনটেক্সট জানে না কিভাবে পেমেন্ট প্রক্রিয়া করা হয় — এটি সাধারণ মেথড কল করে। নতুন পেমেন্ট পদ্ধতি যোগ করতে কার্ট কোড পরিবর্তনের প্রয়োজন হয় না।
ডেটা ভ্যালিডেশন — Strategy একই ফিল্ডের বিভিন্ন ভ্যালিডেশন নিয়মের জন্য ব্যবহৃত হয়। EmailValidatorStrategy, PhoneValidatorStrategy, AgeValidatorStrategy validate(input) মেথড সহ সাধারণ ValidationStrategy ইন্টারফেস বাস্তবায়ন করে। রেজিস্ট্রেশন ফর্ম প্রতিটি ফিল্ড পরীক্ষা করার জন্য স্ট্র্যাটেজির সেট ব্যবহার করে। ভ্যালিডেশন স্ট্র্যাটেজিগুলি একটি চেইনে (Chain of Responsibility) একত্রিত বা একটি লুপে একসাথে প্রয়োগ করা যেতে পারে। এটি দীর্ঘ if-else পরীক্ষাগুলিকে পলিমরফিক ভ্যালিডেটরের সংগ্রহ দিয়ে প্রতিস্থাপন করে।
// সর্টিং স্ট্র্যাটেজি
protocol SortingStrategy {
func sort<T>(_ items: [T]) -> [T] where T: Comparable
}
struct QuickSortStrategy: SortingStrategy {
func sort<T>(_ items: [T]) -> [T] { /* quicksort */ items }
}
struct MergeSortStrategy: SortingStrategy {
func sort<T>(_ items: [T]) -> [T] { /* mergesort */ items }
}
class SortedDataSource<T> {
private var strategy: SortingStrategy
func display(_ items: [T]) { let sorted = strategy.sort(items) }
}
প্রমাণীকরণ — মোবাইল অ্যাপে, প্রদানকারীর উপর নির্ভর করে প্রমাণীকরণ স্ট্র্যাটেজি পরিবর্তন করা হয়। AuthStrategy login(), logout(), getToken() মেথড সহ EmailPasswordAuth, GoogleAuth, AppleAuth, BiometricAuth-এর জন্য বাস্তবায়িত হয়। AuthManager কনটেক্সট DI বা ফ্যাক্টরির মাধ্যমে স্ট্র্যাটেজি গ্রহণ করে। এটি লগইন স্ক্রিন পরিবর্তন না করেই নতুন প্রমাণীকরণ প্রদানকারী যোগ করার অনুমতি দেয়। Strategy প্যাটার্ন অনেক OAuth লাইব্রেরি এবং Firebase Authentication-এর ভিত্তি।
সচরাচর জিজ্ঞাসা
Strategy ন্যায্য যখন আপনার 3+ অ্যালগরিদম থাকে যা পরিবর্তিত বা প্রসারিত হতে পারে। যদি 2টি অ্যালগরিদম থাকে এবং তারা স্থিতিশীল হয় — সরল if-else কম ব্যয়বহুল। Strategy ব্যবহার করুন যখন অ্যালগরিদম অ্যাপ্লিকেশনের বিভিন্ন অংশে ব্যবহৃত হয়, যখন রানটাইমে অ্যালগরিদম পরিবর্তন করতে হয়, বা যখন প্রতিটি অ্যালগরিদমের নিজস্ব নির্ভরতা এবং পরীক্ষার প্রয়োজন হয়।
না, এগুলি অনুরূপ কাঠামোর ভিন্ন প্যাটার্ন। Strategy — ক্লায়েন্ট স্পষ্টভাবে অ্যালগরিদম নির্বাচন করে, এবং স্ট্র্যাটেজিগুলি স্বাধীন। State — অবজেক্ট নিজেই তার আচরণ পরিবর্তন করে যখন তার অভ্যন্তরীণ অবস্থা পরিবর্তিত হয়, এবং অবস্থাগুলি একে অপরের মধ্যে রূপান্তরিত হতে পারে। State-এ, কনটেক্সট অবস্থা পরিবর্তন পরিচালনা করে; Strategy-এ, ক্লায়েন্ট কোড তা করে।
হ্যাঁ, Swift এবং Kotlin-এ স্ট্র্যাটেজি closure বা lambda হিসাবে পাস করা যেতে পারে। Swift: typealias PaymentHandler = (Decimal) async throws -> PaymentResult. Kotlin: typealias PaymentFun = suspend (BigDecimal) -> PaymentResult. এটি সরল ক্ষেত্রে কোড সহজ করে কিন্তু নামকরণ এবং ডকুমেন্টেশন হারায়। 1-2 অ্যালগরিদমের জন্য, closure যথেষ্ট; 4+ এর জন্য, পৃথক ক্লাস ভাল।
প্রতিটি স্ট্র্যাটেজি mock নির্ভরতা সহ পৃথক ইউনিট পরীক্ষা দ্বারা পরীক্ষা করা হয়। কনটেক্সট mock স্ট্র্যাটেজি দিয়ে পরীক্ষা করা হয় — যাচাই করে যে কনটেক্সট স্ট্র্যাটেজি মেথড কল করে এবং সঠিক প্যারামিটার পাঠায়। Swift-এ, XCTest + প্রোটোকল ব্যবহার করুন; Kotlin-এ, MockK বা Mockito ব্যবহার করুন। প্রধান সুবিধা: প্রতিটি স্ট্র্যাটেজি জটিল সেটআপ ছাড়াই বিচ্ছিন্নভাবে পরীক্ষা করা হয়।
হ্যাঁ, Strategy হল 23টি প্যাটার্নের মধ্যে একটি যা "Design Patterns: Elements of Reusable Object-Oriented Software" (Gamma, Helm, Johnson, Vlissides, 1994) বইয়ে বর্ণিত। এটি আচরণগত প্যাটার্নের গ্রুপের অন্তর্গত। উপনাম: Policy। Smalltalk-80-এ মূল উদাহরণ কোড GoF-এর মূল সংস্করণে উপলব্ধ।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন