জাভাতে ডিজাইন প্যাটার্নস (Design Patterns) হল সেই সমাধান যা সাধারণ সমস্যার পুনরাবৃত্তি সমাধান করতে ব্যবহৃত হয়। বিভিন্ন ডিজাইন প্যাটার্নস সিস্টেমের কাঠামো এবং সম্পর্ক নির্ধারণে সাহায্য করে। এখানে কিছু সাধারণ ডিজাইন প্যাটার্নের উদাহরণ দেওয়া হলো:
১. Singleton Pattern
Singleton Pattern নিশ্চিত করে যে একটি ক্লাসের শুধুমাত্র একটি ইনস্ট্যান্স থাকবে এবং তা গ্লোবাল অ্যাক্সেস পাবে।
public class Singleton {
// একটি স্ট্যাটিক ভেরিয়েবল যেখানে ক্লাসের একমাত্র ইনস্ট্যান্স রাখা হবে
private static Singleton instance;
// কনস্ট্রাক্টর প্রাইভেট করতে হবে যাতে বাইরের কোড নতুন ইনস্ট্যান্স তৈরি করতে না পারে
private Singleton() {}
// একটি পাবলিক মেথড যা ক্লাসের একমাত্র ইনস্ট্যান্স রিটার্ন করবে
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public void showMessage() {
System.out.println("Hello from Singleton!");
}
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
singleton.showMessage();
}
}
ব্যাখ্যা:
এখানে Singleton ক্লাসটি নিশ্চিত করে যে এটি থেকে কেবলমাত্র একটি ইনস্ট্যান্স তৈরি হবে এবং সেই ইনস্ট্যান্সটি গ্লোবাল অ্যাক্সেসযোগ্য হবে।
২. Factory Pattern
Factory Pattern একটি প্যাটার্ন যেখানে অবজেক্ট তৈরি করার জন্য একটি আলাদা ক্লাস বা মেথড ব্যবহার করা হয়। এটি অবজেক্ট তৈরির প্রক্রিয়াটি ক্লাসের বাইরে রেখে ডায়নামিকভাবে কনফিগার করা যায়।
// ইন্টারফেস
interface Animal {
void sound();
}
// Concrete Classes
class Dog implements Animal {
public void sound() {
System.out.println("Dog barks");
}
}
class Cat implements Animal {
public void sound() {
System.out.println("Cat meows");
}
}
// Factory Class
class AnimalFactory {
public static Animal getAnimal(String animalType) {
if (animalType == null) {
return null;
}
if (animalType.equalsIgnoreCase("Dog")) {
return new Dog();
} else if (animalType.equalsIgnoreCase("Cat")) {
return new Cat();
}
return null;
}
}
public class FactoryPatternExample {
public static void main(String[] args) {
Animal dog = AnimalFactory.getAnimal("Dog");
dog.sound();
Animal cat = AnimalFactory.getAnimal("Cat");
cat.sound();
}
}
ব্যাখ্যা:
এখানে AnimalFactory ক্লাসের মাধ্যমে আমরা আলাদা আলাদা প্রাণী (Dog, Cat) তৈরি করি, যা আমাদের কোডে অবজেক্ট তৈরি করার প্রক্রিয়াটি সহজ করে দেয়।
৩. Observer Pattern
Observer Pattern হল একটি আচরণগত ডিজাইন প্যাটার্ন যেখানে একটি সাবজেক্ট তার সকল অবজারভারকে অবহিত করে যদি কোনো পরিবর্তন ঘটে।
import java.util.ArrayList;
import java.util.List;
// Observer Interface
interface Observer {
void update(String message);
}
// Concrete Observer
class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
public void update(String message) {
System.out.println(name + " received message: " + message);
}
}
// Subject Interface
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// Concrete Subject
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private String message;
public void setMessage(String message) {
this.message = message;
notifyObservers();
}
public void registerObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
observers.remove(observer);
}
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(message);
}
}
}
public class ObserverPatternExample {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer1 = new ConcreteObserver("Observer 1");
ConcreteObserver observer2 = new ConcreteObserver("Observer 2");
subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.setMessage("Hello Observers!");
}
}
ব্যাখ্যা:
এখানে ConcreteSubject ক্লাসটি একটি মেসেজ সেট করে এবং তার সমস্ত অবজারভারকে অবহিত করে। Observer ক্লাসটি তার নিজস্ব update মেথডে পরিবর্তন সনাক্ত করে এবং মেসেজটি গ্রহণ করে।
৪. Decorator Pattern
Decorator Pattern একটি স্ট্রাকচারাল ডিজাইন প্যাটার্ন যা একটি অবজেক্টের আচরণ পরিবর্তন বা প্রসারিত করার জন্য ব্যবহৃত হয়, কিন্তু অবজেক্টের মৌলিক কনফিগারেশন পরিবর্তন না করে।
// Base interface
interface Coffee {
double cost();
}
// Concrete Class
class SimpleCoffee implements Coffee {
public double cost() {
return 5.0;
}
}
// Decorator Class
class MilkDecorator implements Coffee {
private Coffee coffee;
public MilkDecorator(Coffee coffee) {
this.coffee = coffee;
}
public double cost() {
return coffee.cost() + 1.0; // Adding cost of milk
}
}
class SugarDecorator implements Coffee {
private Coffee coffee;
public SugarDecorator(Coffee coffee) {
this.coffee = coffee;
}
public double cost() {
return coffee.cost() + 0.5; // Adding cost of sugar
}
}
public class DecoratorPatternExample {
public static void main(String[] args) {
Coffee coffee = new SimpleCoffee();
System.out.println("Cost of Simple Coffee: " + coffee.cost());
Coffee milkCoffee = new MilkDecorator(coffee);
System.out.println("Cost of Coffee with Milk: " + milkCoffee.cost());
Coffee milkSugarCoffee = new SugarDecorator(milkCoffee);
System.out.println("Cost of Coffee with Milk and Sugar: " + milkSugarCoffee.cost());
}
}
ব্যাখ্যা:
এখানে SimpleCoffee ক্লাসের আচরণ প্রসারিত করার জন্য MilkDecorator এবং SugarDecorator ক্লাসগুলো ব্যবহার করা হয়েছে। এই প্যাটার্নটি আসল অবজেক্টের আচরণ পরিবর্তন না করে নতুন বৈশিষ্ট্য যোগ করে।
৫. Strategy Pattern
Strategy Pattern হল একটি আচরণগত ডিজাইন প্যাটার্ন যা একটি এলগরিদমকে একটি ক্লাসের পরিবর্তে আলাদা আলাদা ক্লাসে ইনক্লুড করে এবং এগুলোর মধ্যে পরিবর্তনযোগ্যতা আনে।
// Strategy Interface
interface PaymentStrategy {
void pay(int amount);
}
// Concrete Strategy Classes
class CreditCardPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " using Credit Card");
}
}
class PayPalPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " using PayPal");
}
}
// Context Class
class PaymentContext {
private PaymentStrategy strategy;
public PaymentContext(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void executePayment(int amount) {
strategy.pay(amount);
}
}
public class StrategyPatternExample {
public static void main(String[] args) {
PaymentContext context = new PaymentContext(new CreditCardPayment());
context.executePayment(100);
context = new PaymentContext(new PayPalPayment());
context.executePayment(200);
}
}
ব্যাখ্যা:
এখানে PaymentStrategy ইন্টারফেসের মাধ্যমে বিভিন্ন পেমেন্ট পদ্ধতি (যেমন Credit Card, PayPal) পৃথকভাবে বাস্তবায়িত হয়েছে। PaymentContext ক্লাসটি একে একে বিভিন্ন পেমেন্ট পদ্ধতি ব্যবহার করে পেমেন্ট সম্পাদন করে।
সংক্ষেপে:
- Singleton Pattern: একক ইনস্ট্যান্স তৈরি করা।
- Factory Pattern: অবজেক্ট তৈরি করার জন্য আলাদা ফ্যাক্টরি ক্লাস ব্যবহার।
- Observer Pattern: একাধিক অবজারভারকে তথ্য আপডেট পাঠানো।
- Decorator Pattern: অবজেক্টের আচরণকে প্রসারিত করা।
- Strategy Pattern: এলগরিদম পরিবর্তনযোগ্যভাবে একাধিক পদ্ধতিতে বাস্তবায়ন করা।
এই ডিজাইন প্যাটার্নগুলি আপনার কোডকে আরো উন্নত, রিইউজেবল এবং এক্সটেনসিবল করে তোলে।
Singleton Design Pattern একটি সৃজনশীল ডিজাইন প্যাটার্ন যা নিশ্চিত করে যে একটি ক্লাসের কেবলমাত্র একটি ইনস্ট্যান্স থাকবে এবং সেই ইনস্ট্যান্সটি অ্যাক্সেস করার জন্য একটি গেটার মেথড থাকবে। এটি সাধারণত ব্যবহৃত হয় যখন কোনো রিসোর্স বা ডেটা শেয়ার করতে হবে যা একাধিক থ্রেড বা অ্যাপ্লিকেশন অংশের মধ্যে শেয়ার করা হয়।
Singleton Pattern এর প্রধান বৈশিষ্ট্য:
- একটি একক ইনস্ট্যান্স: একটি ক্লাসের কেবলমাত্র এক ইনস্ট্যান্স তৈরি হবে।
- গেটার মেথড: সেই একক ইনস্ট্যান্সটি অ্যাক্সেস করার জন্য একটি গেটার মেথড থাকবে।
- Lazy Initialization: ইনস্ট্যান্সটি তখনই তৈরি হবে যখন প্রথমবার তার প্রয়োজন হবে।
Singleton Design Pattern উদাহরণ
এখানে আমরা একটি Singleton Class তৈরি করব যা একটি কেবল একক ইনস্ট্যান্স প্রদান করবে এবং সেটি ব্যবহার করবে।
Singleton Class উদাহরণ (Thread-Safe Version)
public class Singleton {
// Singleton ক্লাসের একমাত্র ইনস্ট্যান্স
private static Singleton instance;
// কনস্ট্রাক্টরকে প্রাইভেট করা হয়েছে যাতে বাইরের কোড এটি ইনস্ট্যান্স করতে না পারে
private Singleton() {
// কিছু Initialization কোড
}
// গেটার মেথড যা ক্লাসের একমাত্র ইনস্ট্যান্স প্রদান করে
public static Singleton getInstance() {
// ইনস্ট্যান্স তৈরি করা হবে যদি এটি null থাকে
if (instance == null) {
// Synchronization ব্লক ব্যবহার করা হয়েছে যাতে থ্রেড সেফ থাকে
synchronized (Singleton.class) {
// আবার চেক করা হবে যাতে একাধিক থ্রেড একই সময় ইনস্ট্যান্স তৈরি না করে
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
// Singleton ক্লাসের কিছু ফাংশন
public void showMessage() {
System.out.println("Hello from Singleton!");
}
}
public class SingletonPatternTest {
public static void main(String[] args) {
// Singleton ইনস্ট্যান্স অ্যাক্সেস করা
Singleton singleton = Singleton.getInstance();
singleton.showMessage(); // "Hello from Singleton!" মেসেজ প্রদর্শন
// একই ইনস্ট্যান্স পুনরায় ব্যবহার
Singleton anotherSingleton = Singleton.getInstance();
anotherSingleton.showMessage();
// নিশ্চিত করা যে দুটি রেফারেন্স একই ইনস্ট্যান্স পয়েন্ট করছে
System.out.println(singleton == anotherSingleton); // true
}
}
কোডের ব্যাখ্যা:
- Private Constructor:
Singletonক্লাসের কনস্ট্রাক্টরকেprivateকরা হয়েছে, যাতে বাইরের কোড এ ক্লাসটির ইনস্ট্যান্স তৈরি না করতে পারে। এটি Singleton প্যাটার্নের একটি মূল বৈশিষ্ট্য।
- Static Instance Variable:
private static Singleton instance;এই স্ট্যাটিক ভেরিয়েবলটি ক্লাসের একমাত্র ইনস্ট্যান্স হিসেবে কাজ করবে। প্রথমবার যখনgetInstance()মেথডটি কল করা হবে, তখন ইনস্ট্যান্স তৈরি হবে।
- getInstance() Method:
- এই মেথডটি Singleton ক্লাসের একমাত্র ইনস্ট্যান্স প্রদান করে। এটি প্রথমবার কল হলে ইনস্ট্যান্স তৈরি হবে।
synchronizedব্লক ব্যবহার করা হয়েছে যাতে এটি থ্রেড সেফ হয়, অর্থাৎ একাধিক থ্রেড একই সময়ে একে তৈরি করতে না পারে। তবে, এখানে দুইটিifচেক করা হয়েছে যাতে একাধিক থ্রেড প্রথমে ইনস্ট্যান্স তৈরি করতে চেষ্টা করলে তা প্রতিরোধ করা যায়।
- showMessage() Method:
showMessage()একটি সাধারণ মেথড যা এই Singleton ক্লাসের একটি ফাংশনালিটি প্রদর্শন করে।
- Testing the Singleton:
SingletonPatternTestক্লাসের মধ্যে,getInstance()মেথড ব্যবহার করে Singleton ক্লাসের ইনস্ট্যান্স এক্সেস করা হয়। আমরা দুটি আলাদা ভেরিয়েবল দিয়ে একই ইনস্ট্যান্স এক্সেস করেছি এবং তাদের সমতা যাচাই করেছি। এই চেকের ফলস্বরূপtrueহবে, যা নিশ্চিত করে যে এই দুটি ভেরিয়েবল একই ইনস্ট্যান্স পয়েন্ট করছে।
Thread Safety এবং Double-Checked Locking:
- Double-Checked Locking: একটি থ্রেড সেফ
Singletonক্লাস তৈরি করতে আমরা প্রথমেinstanceভেরিয়েবলটি চেক করি যদি এটিnullনা হয়, তারপরsynchronizedব্লক ব্যবহার করি যাতে একাধিক থ্রেড একই সময় ইনস্ট্যান্স তৈরি করতে না পারে।
Advantages of Singleton Pattern:
- Controlled Access to Single Instance: এই প্যাটার্নটি একক ইনস্ট্যান্সের মাধ্যমে রিসোর্স অ্যাক্সেস নিয়ন্ত্রণ করে।
- Global Access Point: এটি গ্লোবাল অ্যাক্সেস পয়েন্ট প্রদান করে, যাতে বিশ্বের যেকোনো জায়গা থেকে একক ইনস্ট্যান্সটি সহজে অ্যাক্সেস করা যায়।
- Lazy Initialization: ইনস্ট্যান্সটি শুধুমাত্র প্রয়োজনের সময় তৈরি হবে, এতে অ্যাপ্লিকেশনের কর্মক্ষমতা বাড়ে।
এই উদাহরণটি দেখায় কিভাবে Singleton Design Pattern বাস্তবায়িত করা যায়। Singleton প্যাটার্নের মাধ্যমে আপনি নিশ্চিত করতে পারেন যে একটি ক্লাসের শুধুমাত্র এক ইনস্ট্যান্স থাকবে, যা অন্যান্য অংশের মধ্যে শেয়ার করা যাবে।
Factory Design Pattern একটি ক্রিয়েশনাল প্যাটার্ন যা একটি ক্লাসের দায়িত্ব দেয় নির্দিষ্ট টাইপের অবজেক্ট তৈরি করার জন্য। এটি বিশেষত ব্যবহার করা হয় যখন অবজেক্ট তৈরি করার পদ্ধতি বা প্রক্রিয়া জটিল হয় বা পরিবর্তনশীল হয়, এবং আপনি একাধিক ধরণের অবজেক্ট তৈরি করতে চান একই ইন্টারফেস বা সুপারক্লাস ব্যবহার করে।
এই প্যাটার্নের প্রধান সুবিধা হল, অবজেক্ট তৈরি করার প্রক্রিয়াটি ক্লায়েন্ট কোড থেকে আড়াল করা হয়, এবং ক্লায়েন্ট কেবলমাত্র ফ্যাক্টরি ক্লাসের মাধ্যমে অবজেক্ট তৈরি করে।
উদাহরণ: গাড়ি তৈরির ফ্যাক্টরি
ধরা যাক, আমরা একটি কার ফ্যাক্টরি তৈরি করতে যাচ্ছি যেখানে বিভিন্ন ধরণের গাড়ি তৈরি হবে (যেমন, Sedan, SUV, Truck), এবং আমাদের প্রয়োজন একটি ফ্যাক্টরি প্যাটার্ন যা নির্দিষ্ট গাড়ি টাইপ অনুযায়ী অবজেক্ট তৈরি করবে।
ধাপ ১: গাড়ির ইন্টারফেস
প্রথমে আমরা একটি Car ইন্টারফেস তৈরি করি যেটি গাড়ির জন্য মৌলিক ফাংশনালিটি সংজ্ঞায়িত করবে।
// Car ইন্টারফেস
public interface Car {
void drive();
}
ধাপ ২: বিভিন্ন গাড়ি ক্লাস
এখন আমরা Car ইন্টারফেস ইমপ্লিমেন্ট করে কিছু নির্দিষ্ট গাড়ি ক্লাস তৈরি করি।
// Sedan গাড়ি
public class Sedan implements Car {
@Override
public void drive() {
System.out.println("Driving a Sedan!");
}
}
// SUV গাড়ি
public class SUV implements Car {
@Override
public void drive() {
System.out.println("Driving an SUV!");
}
}
// Truck গাড়ি
public class Truck implements Car {
@Override
public void drive() {
System.out.println("Driving a Truck!");
}
}
ধাপ ৩: Factory Class
এখন একটি ফ্যাক্টরি ক্লাস তৈরি করি যা বিভিন্ন গাড়ির অবজেক্ট তৈরি করবে।
// CarFactory ক্লাস
public class CarFactory {
// ফ্যাক্টরি মেথড যা নির্দিষ্ট গাড়ি টাইপ অনুযায়ী অবজেক্ট তৈরি করবে
public static Car getCar(String carType) {
if (carType == null) {
return null;
}
if (carType.equalsIgnoreCase("Sedan")) {
return new Sedan();
} else if (carType.equalsIgnoreCase("SUV")) {
return new SUV();
} else if (carType.equalsIgnoreCase("Truck")) {
return new Truck();
}
return null;
}
}
ধাপ ৪: ক্লায়েন্ট কোড
এখন ক্লায়েন্ট কোডে ফ্যাক্টরি ব্যবহার করে গাড়ির অবজেক্ট তৈরি করা হবে এবং তাদের drive() মেথড কল করা হবে।
// Main ক্লাস (Client Code)
public class Main {
public static void main(String[] args) {
// Sedan তৈরি
Car sedan = CarFactory.getCar("Sedan");
sedan.drive(); // Output: Driving a Sedan!
// SUV তৈরি
Car suv = CarFactory.getCar("SUV");
suv.drive(); // Output: Driving an SUV!
// Truck তৈরি
Car truck = CarFactory.getCar("Truck");
truck.drive(); // Output: Driving a Truck!
}
}
ব্যাখ্যা:
- Car ইন্টারফেস: এখানে একটি সাধারণ
Carইন্টারফেস তৈরি করা হয়েছে যা একটিdrive()মেথড ডিক্লেয়ার করেছে। এটি সমস্ত ধরনের গাড়ির জন্য কমন ফাংশনালিটি প্রদান করবে। - গাড়ি ক্লাস:
Sedan,SUV, এবংTruckক্লাসগুলিCarইন্টারফেসের মাধ্যমে বাস্তবায়িত হয়েছে। প্রতিটি ক্লাসের মধ্যে তাদের নিজস্বdrive()মেথড রয়েছে যা তাদের নিজস্ব বাস্তবায়ন করে। - CarFactory ক্লাস:
CarFactoryএকটি ফ্যাক্টরি ক্লাস যা একটি স্ট্যাটিক মেথডgetCar()প্রদান করে। এটি গাড়ির ধরন অনুযায়ী উপযুক্ত ক্লাসের অবজেক্ট তৈরি করে। - ক্লায়েন্ট কোড:
Mainক্লাসে, আমরাCarFactory.getCar()মেথডের মাধ্যমে বিভিন্ন ধরনের গাড়ির অবজেক্ট তৈরি করেছি এবং তাদেরdrive()মেথড কল করেছি।
ফ্যাক্টরি প্যাটার্নের সুবিধা:
- ক্লায়েন্ট কোডের সহজ ব্যবহার: ক্লায়েন্ট কেবল ফ্যাক্টরি মেথড ব্যবহার করে অবজেক্ট তৈরি করতে পারে, এবং কোনো বাস্তবায়ন জানার প্রয়োজন নেই।
- নতুন ধরনের গাড়ি যুক্ত করা: যদি আপনি নতুন গাড়ি টাইপ যুক্ত করতে চান, তবে আপনাকে শুধু নতুন একটি ক্লাস তৈরি করতে হবে এবং ফ্যাক্টরি ক্লাসে তা যুক্ত করতে হবে, ক্লায়েন্ট কোডে কোনো পরিবর্তন করার প্রয়োজন নেই।
- ডিপেনডেন্সি ইনজেকশন: ফ্যাক্টরি প্যাটার্ন অবজেক্ট তৈরির কাজ পৃথক করে দেয়, যা কোডে সুসংগতি বৃদ্ধি করে এবং কোডের মেইন্টেনেবিলিটি উন্নত করে।
ফ্যাক্টরি ডিজাইন প্যাটার্ন একটি খুবই কার্যকর প্যাটার্ন যা অবজেক্ট তৈরির প্রক্রিয়া সহজ করে এবং কোডের পুনঃব্যবহারযোগ্যতা ও মেইন্টেনেবিলিটি বাড়ায়। এটি বিভিন্ন পরিস্থিতিতে ব্যবহার করা যেতে পারে যেখানে অবজেক্ট তৈরি করা একটি জটিল প্রক্রিয়া হতে পারে।
Observer Design Pattern হল একটি আচরণগত ডিজাইন প্যাটার্ন যা একাধিক অবজার্ভার (subscriber) কে একটি বিষয় (subject) এর পরিবর্তন সম্পর্কে অবহিত করতে ব্যবহৃত হয়। এটি ইভেন্ট হ্যান্ডলিং বা ডাটা আপডেটের ক্ষেত্রে খুবই কার্যকর, বিশেষত যখন একাধিক অবজার্ভার থাকে যারা একত্রে একটি বিষয় (subject) এর অবস্থা পরিবর্তনের উপর নজর রাখে।
নিচে Observer Pattern ব্যবহার করে ইভেন্ট হ্যান্ডলিংয়ের একটি উদাহরণ দেওয়া হয়েছে যেখানে একটি Subject ক্লাস তার অবজার্ভারদের খবর দেয় যখন তার স্টেট পরিবর্তন হয়।
উদাহরণ:
import java.util.ArrayList;
import java.util.List;
// Observer Interface
interface Observer {
void update(String message);
}
// Concrete Observer - এটি Observer Interface ইমপ্লিমেন্ট করে
class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received message: " + message);
}
}
// Subject (Publisher) Interface
interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// Concrete Subject - এটি Subject Interface ইমপ্লিমেন্ট করে
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private String state;
public void setState(String state) {
this.state = state;
notifyObservers();
}
public String getState() {
return state;
}
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
}
// Main Class to demonstrate Observer Pattern
public class ObserverPatternExample {
public static void main(String[] args) {
// ConcreteSubject তৈরি
ConcreteSubject subject = new ConcreteSubject();
// ConcreteObservers তৈরি
Observer observer1 = new ConcreteObserver("Observer 1");
Observer observer2 = new ConcreteObserver("Observer 2");
Observer observer3 = new ConcreteObserver("Observer 3");
// Observer গুলো সাবস্ক্রাইব করে Subject এ
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.addObserver(observer3);
// Subject এর state পরিবর্তন করলে সকল Observerদের খবর দেওয়া হবে
System.out.println("State change 1: ");
subject.setState("New State: 1");
// Observer 1 কে সরিয়ে দেয়া
subject.removeObserver(observer1);
// আবার state change
System.out.println("\nState change 2: ");
subject.setState("New State: 2");
}
}
Explanation:
- Observer Interface: এটি একটি সাধারণ ইন্টারফেস যা সমস্ত অবজার্ভার ক্লাস দ্বারা ইমপ্লিমেন্ট করতে হবে। এটি
update()মেথডের মাধ্যমে অবজার্ভারদের অবহিত করবে। - ConcreteObserver Class: এই ক্লাসটি
Observerইন্টারফেস ইমপ্লিমেন্ট করে এবংupdate()মেথডের মাধ্যমে একটি বার্তা গ্রহণ করে তা প্রদর্শন করে। - Subject Interface: এটি একটি ইন্টারফেস যা
addObserver(),removeObserver(), এবংnotifyObservers()মেথড প্রদান করে। এটি অবজার্ভারদের যুক্ত করার, তাদের সরিয়ে দেওয়ার, এবং তাদের সবকে অবহিত করার কাজ করে। - ConcreteSubject Class: এটি
Subjectইন্টারফেস ইমপ্লিমেন্ট করে এবং তারstateএর পরিবর্তন ঘটানোর সাথে সাথে অবজার্ভারদের অবহিত করে।setState()মেথডের মাধ্যমে স্টেট পরিবর্তন করা হয় এবংnotifyObservers()এর মাধ্যমে সকল অবজার্ভারকে অবহিত করা হয়। - Main Class:
ObserverPatternExampleক্লাসে একটিConcreteSubjectএবং তিনটিConcreteObserverতৈরি করা হয়। আমরা প্রথমে সকল অবজার্ভারকে সাবস্ক্রাইব করে দিয়ে স্টেট পরিবর্তন ঘটাই, পরে একটি অবজার্ভার সরিয়ে দিয়ে আবার স্টেট পরিবর্তন করি।
Output Example:
State change 1:
Observer 1 received message: New State: 1
Observer 2 received message: New State: 1
Observer 3 received message: New State: 1
State change 2:
Observer 2 received message: New State: 2
Observer 3 received message: New State: 2
Key Points:
- Observer Pattern এ,
Subjectএর স্টেট পরিবর্তন হলে, সকল অবজার্ভারদের অবহিত করা হয়। - এই প্যাটার্নটি ইভেন্ট হ্যান্ডলিং এবং ডাটা আপডেটিংয়ে ব্যবহার করা হয় যেখানে একাধিক অবজার্ভার একই বিষয়টির উপর নজর রাখে।
- আপনি অবজার্ভারগুলোকে
addObserver()এবংremoveObserver()মেথডের মাধ্যমে সহজেই যুক্ত বা সরিয়ে ফেলতে পারেন। - এটি loose coupling (অল্প সম্পর্কিত) ডিজাইন তৈরি করে, যেখানে অবজার্ভার এবং সাবজেক্ট একে অপরের প্রতি খুব নির্ভরশীল নয়।
এটি মূলত GUI অ্যাপ্লিকেশন, ইভেন্ট হ্যান্ডলিং, এবং ডাটা মডেল আপডেটের জন্য ব্যবহৃত হয়।
Decorator Design Pattern একটি স্ট্রাকচারাল ডিজাইন প্যাটার্ন, যা একটি অবজেক্টের আচরণ বা ফাংশনালিটি ডাইনামিকভাবে পরিবর্তন করার জন্য ব্যবহৃত হয়। এটি নতুন ক্লাস তৈরি না করে, একটি অবজেক্টের আচরণকে ডেকোরেটর অবজেক্টের মাধ্যমে কাস্টমাইজ বা প্রসারিত করতে সাহায্য করে।
নিচে একটি উদাহরণ দেওয়া হলো, যেখানে Decorator Pattern ব্যবহার করে একটি অবজেক্টের ফাংশনালিটি ডাইনামিকভাবে পরিবর্তন করা হয়েছে:
Decorator Design Pattern Example
১. বেস ক্লাস (Component)
প্রথমে একটি বেস ক্লাস তৈরি করা হচ্ছে, যেটি একটি সাধারণ ফাংশনালিটি প্রদর্শন করবে।
// Component Interface
public interface Coffee {
double cost();
}
২. Concrete Component
এটি Coffee ইন্টারফেসের একটি কনক্রিট (Concrete) বাস্তবায়ন যা একটি সাধারণ কফি ক্লাস তৈরি করবে।
// Concrete Component
public class SimpleCoffee implements Coffee {
@Override
public double cost() {
return 5.0; // সাধারণ কফির দাম
}
}
৩. Decorator Class
এটি Coffee ইন্টারফেসকে ইমপ্লিমেন্ট করবে এবং অন্যান্য ডেকোরেটরের জন্য বেস ক্লাস হিসেবে কাজ করবে।
// Decorator Class
public abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
@Override
public double cost() {
return decoratedCoffee.cost();
}
}
৪. Concrete Decorators
এখন বিভিন্ন ডেকোরেটর ক্লাস তৈরি করা হবে, যেগুলি কফির মূল অবজেক্টের আচরণ পরিবর্তন করবে।
// Concrete Decorator 1: Milk
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double cost() {
return super.cost() + 1.5; // দুধ যোগ করলে দাম বৃদ্ধি
}
}
// Concrete Decorator 2: Sugar
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double cost() {
return super.cost() + 0.5; // চিনি যোগ করলে দাম বৃদ্ধি
}
}
৫. Client Code
এখানে কিভাবে ডেকোরেটর প্যাটার্ন ব্যবহার করে কফির ফাংশনালিটি পরিবর্তন করা হবে তা দেখানো হয়েছে।
public class DecoratorPatternExample {
public static void main(String[] args) {
// সাধারণ কফি তৈরি
Coffee simpleCoffee = new SimpleCoffee();
System.out.println("Simple Coffee cost: " + simpleCoffee.cost());
// দুধসহ কফি তৈরি
Coffee milkCoffee = new MilkDecorator(new SimpleCoffee());
System.out.println("Milk Coffee cost: " + milkCoffee.cost());
// দুধ এবং চিনি সহ কফি তৈরি
Coffee milkSugarCoffee = new SugarDecorator(new MilkDecorator(new SimpleCoffee()));
System.out.println("Milk and Sugar Coffee cost: " + milkSugarCoffee.cost());
}
}
Output:
Simple Coffee cost: 5.0
Milk Coffee cost: 6.5
Milk and Sugar Coffee cost: 7.0
ডেকোরেটর প্যাটার্নের ব্যাখ্যা:
- Component Interface (
Coffee): এটি একটি সাধারণ ইন্টারফেস যা সমস্ত কফি ক্লাসের জন্য নির্দিষ্ট আচরণ ঘোষণা করে। এখানেcost()মেথডের মাধ্যমে কফির দাম পাওয়া যায়। - Concrete Component (
SimpleCoffee): এটিCoffeeইন্টারফেসের বাস্তবায়ন এবং একটি সাধারণ কফির দাম প্রদান করে। - Decorator Class (
CoffeeDecorator): এটিCoffeeইন্টারফেসে ডেকোরেটরের বেস ক্লাস হিসেবে কাজ করে এবং অন্যান্য ডেকোরেটর ক্লাসে শেয়ার করা ফাংশনালিটি ধারণ করে।cost()মেথডটি ডেকোরেটরের ভিতরে ডেকোরেটেড অবজেক্টেরcost()মেথডে কল করা হয়। - Concrete Decorators (
MilkDecorator,SugarDecorator): এগুলিCoffeeDecoratorক্লাসের সাবক্লাস এবং কফির মূল অবজেক্টের আচরণ পরিবর্তন করতে নির্দিষ্ট ফাংশনালিটি যোগ করে (যেমন, দুধ এবং চিনি যোগ করা)। - Client Code: এটি বাস্তবে ডেকোরেটর প্যাটার্নের ব্যবহার দেখায়, যেখানে কফি অবজেক্টের উপর ডেকোরেটর যোগ করে ডাইনামিকভাবে নতুন ফাংশনালিটি যুক্ত করা হয়েছে।
Decorator Pattern এর সুবিধা:
- কাস্টমাইজেশন: অবজেক্টের আচরণ ডাইনামিকভাবে পরিবর্তন করা সম্ভব, যা সহজেই নতুন ফাংশনালিটি যোগ করার সুযোগ দেয়।
- অতিরিক্ত কোড লেখা ছাড়াই: নতুন ক্লাস তৈরি না করে অবজেক্টের আচরণ প্রসারিত করা সম্ভব।
- কমপ্লেক্সিটি কমানো: ডেকোরেটর প্যাটার্ন অনেক ছোট ক্লাসে বিভক্ত করা যায়, যা কমপ্লেক্সিটি কমাতে সহায়তা করে।
Decorator Pattern এর ব্যবহার:
- UI Components: UI কম্পোনেন্টগুলির জন্য, যেমন টেক্সট ফিল্ড, বোতাম ইত্যাদি, যেখানে একাধিক স্টাইলিং বা ফিচার যুক্ত করা যেতে পারে।
- Logging: যখন আপনি একাধিক লগিং স্টাইলের মধ্যে পরিবর্তন করতে চান, যেমন কনসোল লগিং, ফাইল লগিং ইত্যাদি।
- Stream I/O: Java I/O প্যাকেজে, যেমন
BufferedReader,FileReaderইত্যাদিতে ডেকোরেটর প্যাটার্ন ব্যবহার করা হয় যেখানে একাধিক স্ট্রিমের উপর বিভিন্ন ফিল্টার প্রয়োগ করা হয়।
এটি একটি খুব শক্তিশালী ডিজাইন প্যাটার্ন যা ফ্লেক্সিবিলিটি প্রদান করে এবং অবজেক্টের আচরণ পরিবর্তন বা প্রসারিত করার জন্য নতুন কোডের প্রয়োজনীয়তা কমায়।
Strategy Design Pattern হল একটি behavioral design pattern যা একটি এলগরিদমের পরিবারকে ডিফাইন করে এবং তাদের একটি ইন্টারফেসের মাধ্যমে সিলেক্ট করতে সক্ষম করে। এটি runtime এ এলগরিদম পরিবর্তন করতে সাহায্য করে এবং ক্লাসের মধ্যে এলগরিদমের উপযুক্ত ব্যবহার করা সম্ভব করে।
Strategy Pattern-এর মূল ধারণা হল, ক্লাসের মধ্যে আলাদা আলাদা এলগরিদম সংরক্ষণ করা এবং সেগুলি প্রয়োজনে runtime এ ব্যবহার করা। এই প্যাটার্নের মাধ্যমে, কোডের মধ্যে কোন hard-coded এলগরিদম বা লজিক থাকেনা এবং এটি আরও ফ্লেক্সিবল ও পরিবর্তনযোগ্য হয়ে ওঠে।
উদাহরণ: এলগরিদম নির্বাচন (Strategy Pattern)
ধরা যাক আমাদের একটি সফটওয়্যার সিস্টেম আছে যেখানে কিছু কনটেক্সটে ভিন্ন ভিন্ন এলগরিদম প্রয়োগ করা হয় (যেমন বিভিন্ন ক্যালকুলেশন বা ডিস্কাউন্ট ক্যালকুলেশন)। এখানে, Strategy Design Pattern ব্যবহার করা হবে।
১. Strategy Interface:
প্রথমে, আমরা একটি Strategy ইন্টারফেস তৈরি করবো যা বিভিন্ন এলগরিদমের জন্য কমন API ডিফাইন করবে।
public interface PaymentStrategy {
void pay(int amount);
}
২. Concrete Strategies:
এবার আমরা PaymentStrategy ইন্টারফেসের মাধ্যমে বিভিন্ন এলগরিদম তৈরি করবো। যেমন, CreditCardPayment এবং PayPalPayment।
// Credit Card Payment Strategy
public class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
public CreditCardPayment(String cardNumber) {
this.cardNumber = cardNumber;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using Credit Card: " + cardNumber);
}
}
// PayPal Payment Strategy
public class PayPalPayment implements PaymentStrategy {
private String email;
public PayPalPayment(String email) {
this.email = email;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using PayPal with email: " + email);
}
}
৩. Context Class:
এখন আমরা একটি PaymentContext ক্লাস তৈরি করবো যা কোন পেমেন্ট স্ট্র্যাটেজি ব্যবহার করবে তা নির্বাচন করবে। এটি run-time এ পরিবর্তনশীল স্ট্র্যাটেজি ব্যবহার করতে পারে।
public class PaymentContext {
private PaymentStrategy paymentStrategy;
// পেমেন্ট স্ট্র্যাটেজি সেট করা
public PaymentContext(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
// পেমেন্ট পদ্ধতি ব্যবহার করা
public void executePayment(int amount) {
paymentStrategy.pay(amount);
}
// স্ট্র্যাটেজি পরিবর্তন করা
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
}
৪. Client Code:
এবার আমরা ক্লায়েন্ট কোড তৈরি করবো যা বিভিন্ন পেমেন্ট স্ট্র্যাটেজি ব্যবহার করবে।
public class StrategyPatternExample {
public static void main(String[] args) {
// প্রথমে Credit Card পেমেন্ট স্ট্র্যাটেজি ব্যবহার করা হচ্ছে
PaymentStrategy creditCardPayment = new CreditCardPayment("1234-5678-9876-5432");
PaymentContext paymentContext = new PaymentContext(creditCardPayment);
paymentContext.executePayment(500);
// পেমেন্ট স্ট্র্যাটেজি পরিবর্তন করা হচ্ছে (PayPal)
PaymentStrategy payPalPayment = new PayPalPayment("user@example.com");
paymentContext.setPaymentStrategy(payPalPayment);
paymentContext.executePayment(300);
}
}
৫. আউটপুট:
Paid 500 using Credit Card: 1234-5678-9876-5432
Paid 300 using PayPal with email: user@example.com
ব্যাখ্যা:
- Strategy Interface (
PaymentStrategy):- এটি একটি সাধারণ ইন্টারফেস যা
payমেথড ডিফাইন করে, যাতে বিভিন্ন ধরনের পেমেন্ট স্ট্র্যাটেজি এটির মাধ্যমে কাজ করতে পারে।
- এটি একটি সাধারণ ইন্টারফেস যা
- Concrete Strategies (
CreditCardPaymentওPayPalPayment):- এই ক্লাসগুলি
PaymentStrategyইন্টারফেস ইমপ্লিমেন্ট করে এবং তাদের নিজস্ব পেমেন্ট এলগরিদম (ক্রেডিট কার্ড পেমেন্ট বা পেপ্যাল পেমেন্ট) বাস্তবায়ন করে।
- এই ক্লাসগুলি
- Context Class (
PaymentContext):- এই ক্লাসটি ক্লায়েন্টের কাছে স্ট্র্যাটেজি সরবরাহ করে এবং সেই স্ট্র্যাটেজি অনুযায়ী পেমেন্ট পরিচালনা করে। এছাড়া, এটি স্ট্র্যাটেজি পরিবর্তনের সুবিধাও দেয়।
- Client Code (
StrategyPatternExample):- এই কোডে আমরা
PaymentContextব্যবহার করি এবং পেমেন্ট স্ট্র্যাটেজি পরিবর্তন করে আলাদা আলাদা পেমেন্ট পদ্ধতি প্রয়োগ করি।
- এই কোডে আমরা
Strategy Design Pattern এর সুবিধা:
- ফ্লেক্সিবিলিটি: স্ট্র্যাটেজি পরিবর্তন করা সহজ এবং runtime এ ভিন্ন ভিন্ন এলগরিদম ব্যবহার করা সম্ভব।
- কোডের পুনঃব্যবহারযোগ্যতা: আলাদা আলাদা এলগরিদম তৈরি করা যাবে এবং একসাথে ব্যবহার করা যাবে।
- কোড সহজীকরণ: এলগরিদম পরিবর্তনের জন্য ক্লাস পরিবর্তন করতে হবে না, কেবল স্ট্র্যাটেজি পরিবর্তন করতে হবে।
এটি একটি সাধারণ Strategy Design Pattern এর উদাহরণ যা বিভিন্ন এলগরিদমকে encapsulate (অন্তর্ভুক্ত) করে এবং তাদের একসাথে ব্যবহার করার সুযোগ দেয়।
Read more