Skip to main content
Innovation|Innovation

Java में Design Patterns: LEGO से बनाने की तरह बेहतर Software बनाएँ

छह आवश्यक Java design patterns — Strategy, Factory, Builder, Observer, Decorator, और Template Method — के लिए beginner-friendly guide, वास्तविक जीवन के उदाहरणों, working कोड examples, और हर एक का कब इस्तेमाल करें के स्पष्ट मार्गदर्शन के साथ।

11 अप्रैल 202617 min read

Design Patterns क्यों मायने रखते हैं

कल्पना करें कि आप LEGO ईंटों से एक घर बना रहे हैं। आप ईंटों को यूँ ही फेंक सकते हैं और उम्मीद कर सकते हैं कि काम हो जाएगा। या आप उन सिद्ध blueprints का पालन कर सकते हैं जिन्हें हज़ारों builders पहले ही आज़मा चुके हैं। Design patterns software के लिए वे blueprints हैं।

एक design pattern एक सामान्य समस्या का पुनः उपयोग करने योग्य समाधान है। आप pattern से कोड copy-paste नहीं करते। इसके बजाय, आप विचार सीखते हैं और अपनी स्थिति के अनुसार ढालते हैं। इसे एक recipe की तरह सोचें: recipe आपको steps बताती है, लेकिन आप अपनी सामग्री चुनते हैं।

इस guide में, हम छह आवश्यक patterns को cover करेंगे जो हर Java developer को पता होने चाहिए। हर एक के लिए, आप सीखेंगे: यह किस समस्या को हल करता है, एक सरल वास्तविक जीवन का उदाहरण, working Java कोड, और इसे कब इस्तेमाल करें।

1. Strategy Pattern — Runtime पर अपना Method चुनें

समस्या

आपके पास एक काम है जो कई तरह से हो सकता है। उदाहरण के लिए, एक customer credit card, PayPal, या bank transfer से भुगतान कर सकता है। अगर आप हर method को संभालने के लिए if-else chains का इस्तेमाल करते हैं, तो आपका कोड गंदा हो जाता है। हर बार जब आप एक नया payment method जोड़ते हैं, आपको वही class खोलनी पड़ती है और एक और branch जोड़नी पड़ती है। यह Open/Closed Principle को तोड़ता है — classes extension के लिए open होनी चाहिए लेकिन modification के लिए closed।

वास्तविक जीवन का उदाहरण

घर से school तक जाने के बारे में सोचें। आप car, bus, या train ले सकते हैं। Destination एक ही है, लेकिन वहाँ पहुँचने का तरीका अलग है। आप स्थिति के आधार पर strategy चुनते हैं: अगर बारिश हो रही है, तो car लें; अगर पैसे बचाना है, तो bus लें। Strategy pattern इसी तरह काम करता है — एक ही लक्ष्य, अलग approaches, runtime पर चुना गया।

Java Code

Step 1: Strategy interface परिभाषित करें।

public interface PaymentStrategy {
    void pay(double amount);
}

Step 2: Concrete strategies बनाएँ — हर payment method के लिए एक class।

public class CreditCardPayment implements PaymentStrategy {
    private final String cardNumber;

    public CreditCardPayment(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    @Override
    public void pay(double amount) {
        System.out.println("Paid $" + amount + " using Credit Card ending in "
            + cardNumber.substring(cardNumber.length() - 4));
    }
}

public class PayPalPayment implements PaymentStrategy {
    private final String email;

    public PayPalPayment(String email) {
        this.email = email;
    }

    @Override
    public void pay(double amount) {
        System.out.println("Paid $" + amount + " using PayPal account " + email);
    }
}

public class BankTransferPayment implements PaymentStrategy {
    private final String bankAccount;

    public BankTransferPayment(String bankAccount) {
        this.bankAccount = bankAccount;
    }

    @Override
    public void pay(double amount) {
        System.out.println("Paid $" + amount + " via Bank Transfer to account " + bankAccount);
    }
}

Step 3: एक context class में strategy का इस्तेमाल करें।

public class PaymentProcessor {
    private PaymentStrategy strategy;

    public void setStrategy(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    public void processPayment(double amount) {
        if (strategy == null) {
            throw new IllegalStateException("Payment strategy not set!");
        }
        strategy.pay(amount);
    }
}

// Usage
PaymentProcessor processor = new PaymentProcessor();

processor.setStrategy(new CreditCardPayment("4111111111111234"));
processor.processPayment(99.99);
// Output: Paid $99.99 using Credit Card ending in 1234

processor.setStrategy(new PayPalPayment("user@example.com"));
processor.processPayment(49.50);
// Output: Paid $49.50 using PayPal account user@example.com

processor.setStrategy(new BankTransferPayment("ACC-78901"));
processor.processPayment(250.00);
// Output: Paid $250.0 via Bank Transfer to account ACC-78901

Strategy का इस्तेमाल कब करें

इसका इस्तेमाल करें जब: आपके पास एक ही काम के लिए कई algorithms हैं और आप class को बदले बिना उनके बीच बदलना चाहते हैं जो उनका इस्तेमाल करती है। सामान्य उदाहरण: payment methods, sorting algorithms, compression formats, validation rules।

इससे बचें जब: आपके पास केवल एक या दो सरल variations हैं। एक सरल if-else ठीक है जब केवल दो विकल्प हैं जो कभी नहीं बढ़ेंगे।

2. Factory Pattern — सटीक Class को जाने बिना Objects बनाएँ

समस्या

आपके कोड को कुछ input के आधार पर अलग-अलग प्रकार के objects बनाने की ज़रूरत है। उदाहरण के लिए, आप एक notification भेजना चाहते हैं, लेकिन कभी-कभी यह एक email है, कभी-कभी SMS, और कभी-कभी एक push notification। अगर calling कोड सीधे new EmailNotification() का इस्तेमाल करता है, तो यह उस विशिष्ट class से कसकर जुड़ जाता है। एक नया notification प्रकार जोड़ने का मतलब है हर उस जगह को बदलना जहाँ notifications बनाए जाते हैं।

वास्तविक जीवन का उदाहरण

एक restaurant में food order करने के बारे में सोचें। आप waiter को कहते हैं "मुझे एक pizza चाहिए।" आप kitchen में जाकर खुद नहीं बनाते। Kitchen (factory) तय करती है कि इसे कैसे बनाना है — कौन-सा chef, कौन-सा oven, कौन-सी सामग्री। आप बस अपना pizza पाते हैं। Factory pattern इसी तरह काम करता है: आप जो चाहिए वह माँगते हैं, और factory यह पता लगाती है कि इसे कैसे बनाया जाए।

Java Code

Step 1: Product interface परिभाषित करें।

public interface Notification {
    void send(String recipient, String message);
}

Step 2: Concrete products बनाएँ।

public class EmailNotification implements Notification {
    @Override
    public void send(String recipient, String message) {
        System.out.println("EMAIL to " + recipient + ": " + message);
    }
}

public class SmsNotification implements Notification {
    @Override
    public void send(String recipient, String message) {
        System.out.println("SMS to " + recipient + ": " + message);
    }
}

public class PushNotification implements Notification {
    @Override
    public void send(String recipient, String message) {
        System.out.println("PUSH to " + recipient + ": " + message);
    }
}

Step 3: Factory बनाएँ।

public class NotificationFactory {

    public static Notification create(String type) {
        return switch (type.toLowerCase()) {
            case "email" -> new EmailNotification();
            case "sms"   -> new SmsNotification();
            case "push"  -> new PushNotification();
            default      -> throw new IllegalArgumentException(
                                "Unknown notification type: " + type);
        };
    }
}

// Usage
Notification alert = NotificationFactory.create("email");
alert.send("user@example.com", "Your order has shipped!");
// Output: EMAIL to user@example.com: Your order has shipped!

Notification reminder = NotificationFactory.create("sms");
reminder.send("+1234567890", "Your appointment is tomorrow.");
// Output: SMS to +1234567890: Your appointment is tomorrow.

Notification promo = NotificationFactory.create("push");
promo.send("device-token-abc", "New features available!");
// Output: PUSH to device-token-abc: New features available!

Factory का इस्तेमाल कब करें

इसका इस्तेमाल करें जब: आपको संबंधित classes के परिवार से objects बनाने की ज़रूरत है और सटीक प्रकार input या configuration पर निर्भर करता है। सामान्य उदाहरण: notification channels, database drivers, document parsers, UI components।

इससे बचें जब: आप हमेशा एक ही प्रकार बनाते हैं। एक product वाला factory over-engineering है।

3. Builder Pattern — जटिल Objects Step by Step बनाएँ

समस्या

कुछ objects में कई fields होती हैं। एक User में name, email, phone, address, age, और role हो सकता है। अगर आप 10 parameters वाले constructor का इस्तेमाल करते हैं, तो order याद रखना असंभव हो जाता है। कौन-सा phone number है? कौन-सा email? इसे "telescoping constructor" समस्या कहा जाता है।

वास्तविक जीवन का उदाहरण

एक custom burger order करने के बारे में सोचें। आप सभी 8 toppings एक साथ नहीं चिल्लाते। इसके बजाय, आप इसे step by step बनाते हैं: "मुझे एक beef patty चाहिए, lettuce डालें, tomato डालें, cheese डालें, onions नहीं, extra pickles।" Builder pattern इसी तरह काम करता है — आप हर field को एक बार में set करते हैं, किसी भी order में, और फिर build() call करके final object प्राप्त करते हैं।

Java Code — Manual Builder

public class User {
    private final String name;
    private final String email;
    private final String phone;
    private final int age;
    private final String role;

    private User(Builder builder) {
        this.name = builder.name;
        this.email = builder.email;
        this.phone = builder.phone;
        this.age = builder.age;
        this.role = builder.role;
    }

    // Getters...

    public static class Builder {
        private final String name;  // required
        private String email;
        private String phone;
        private int age;
        private String role = "USER"; // default value

        public Builder(String name) {
            this.name = name;
        }

        public Builder email(String email) {
            this.email = email;
            return this;
        }

        public Builder phone(String phone) {
            this.phone = phone;
            return this;
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public Builder role(String role) {
            this.role = role;
            return this;
        }

        public User build() {
            return new User(this);
        }
    }
}

// Usage — readable, flexible, any order
User admin = new User.Builder("Alice")
    .email("alice@example.com")
    .role("ADMIN")
    .age(30)
    .build();

User guest = new User.Builder("Bob")
    .email("bob@example.com")
    .build();  // phone, age use defaults

Shortcut: Lombok @Builder

हाथ से एक builder लिखना थकाऊ है। Lombok इसे एक annotation के साथ generate करता है:

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class Order {
    private final String orderId;
    private final String customerName;
    private final double totalAmount;
    @Builder.Default
    private final String status = "PENDING";
    private final String shippingAddress;
}

// Usage — identical fluent API, zero boilerplate
Order order = Order.builder()
    .orderId("ORD-001")
    .customerName("Charlie")
    .totalAmount(149.99)
    .shippingAddress("123 Main St")
    .build();
// status defaults to "PENDING"

Java Records + Compact Constructors

सरल data carriers के लिए, Java 16+ records आपको free में immutability देते हैं:

public record Product(String name, double price, String category) {
    // Compact constructor for validation
    public Product {
        if (price < 0) throw new IllegalArgumentException("Price cannot be negative");
        name = name.trim();
    }
}

Product item = new Product("Laptop", 999.99, "Electronics");
System.out.println(item.name());  // "Laptop" — auto-generated accessor

Builder का इस्तेमाल कब करें

इसका इस्तेमाल करें जब: Objects में 4 या 5 से ज़्यादा fields हों, या कई optional fields हों। यह तब भी उपयोगी है जब आप immutable objects चाहते हैं। सामान्य उदाहरण: configuration objects, HTTP requests, database queries, report parameters।

इससे बचें जब: object में केवल 2-3 fields हों। एक सरल constructor या एक record साफ़ है।

4. Observer / Event Pattern — जब कुछ होता है तो सूचित करें

समस्या

जब आपके system में कुछ महत्वपूर्ण होता है (जैसे एक order place होना), कई हिस्सों को react करना होता है: एक confirmation email भेजना, inventory update करना, event log करना, warehouse को सूचित करना। अगर order कोड सीधे इन सबको call करता है, तो यह हर listener से कसकर जुड़ जाता है। एक नई reaction जोड़ने का मतलब है order कोड को बदलना।

वास्तविक जीवन का उदाहरण

एक YouTube channel के बारे में सोचें। जब एक creator एक नया video upload करता है, हर subscriber को notification मिलती है। Creator personally हर subscriber को call नहीं करता। इसके बजाय, YouTube का system इसे संभालता है: "कुछ हुआ (नया video) — सभी को बताओ जिन्होंने sign up किया है।" Observer pattern इसी तरह काम करता है: एक event publish करें, और सभी registered listeners स्वतंत्र रूप से react करते हैं।

Java Code — Spring Events

Step 1: Event परिभाषित करें।

public class OrderPlacedEvent {
    private final String orderId;
    private final String customerEmail;
    private final double totalAmount;

    public OrderPlacedEvent(String orderId, String customerEmail, double totalAmount) {
        this.orderId = orderId;
        this.customerEmail = customerEmail;
        this.totalAmount = totalAmount;
    }

    // Getters
    public String getOrderId() { return orderId; }
    public String getCustomerEmail() { return customerEmail; }
    public double getTotalAmount() { return totalAmount; }
}

Step 2: Service से event publish करें।

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
public class OrderService {
    private final ApplicationEventPublisher eventPublisher;

    public OrderService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public void placeOrder(String orderId, String email, double total) {
        // Save order to database...
        System.out.println("Order " + orderId + " saved to database.");

        // Publish event — OrderService does NOT know who listens
        eventPublisher.publishEvent(new OrderPlacedEvent(orderId, email, total));
    }
}

Step 3: Listeners बनाएँ — हर एक स्वतंत्र रूप से react करता है।

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class EmailListener {
    @EventListener
    public void onOrderPlaced(OrderPlacedEvent event) {
        System.out.println("Sending confirmation email to " + event.getCustomerEmail()
            + " for order " + event.getOrderId());
    }
}

@Component
public class InventoryListener {
    @EventListener
    public void onOrderPlaced(OrderPlacedEvent event) {
        System.out.println("Updating inventory for order " + event.getOrderId());
    }
}

@Component
public class AnalyticsListener {
    @EventListener
    public void onOrderPlaced(OrderPlacedEvent event) {
        System.out.println("Logging analytics: order " + event.getOrderId()
            + " total $" + event.getTotalAmount());
    }
}

// When placeOrder() is called, ALL three listeners fire automatically:
// Output:
// Order ORD-42 saved to database.
// Sending confirmation email to user@example.com for order ORD-42
// Updating inventory for order ORD-42
// Logging analytics: order ORD-42 total $149.99

Bonus: Async Events

Listeners को अलग threads में चलाने के लिए @Async जोड़ें — order service emails या analytics के लिए इंतज़ार नहीं करती:

@Component
public class EmailListener {
    @Async
    @EventListener
    public void onOrderPlaced(OrderPlacedEvent event) {
        // Runs in a separate thread — does not block the order flow
        sendEmail(event.getCustomerEmail(), "Order Confirmed: " + event.getOrderId());
    }
}

Observer/Event का इस्तेमाल कब करें

इसका इस्तेमाल करें जब: एक action कई side effects trigger करता है जिन्हें decouple किया जाना चाहिए। सामान्य उदाहरण: order placed (email + inventory + analytics), user registered (welcome email + default settings), payment received (receipt + ledger update)।

इससे बचें जब: एक event पर केवल एक reaction है। एक सीधा method call event infrastructure से सरल है।

5. Decorator Pattern — Original को बदले बिना Features जोड़ें

समस्या

आपके पास एक working class है, और आप अतिरिक्त behavior जोड़ना चाहते हैं — जैसे logging, caching, या retry logic — मूल कोड को modify किए बिना। अगर आप मूल class को edit करते हैं, तो इसे तोड़ने का risk है। अगर आप हर combination के लिए subclasses बनाते हैं (LoggingService, CachingService, LoggingCachingService), तो आपको class explosion मिलती है।

वास्तविक जीवन का उदाहरण

एक सादे donut को decorate करने के बारे में सोचें। Donut खुद core object है। आप chocolate glaze जोड़ सकते हैं (एक decorator), फिर sprinkles (एक और decorator), फिर whipped cream (एक तीसरा)। हर topping पिछले को wrap करती है। आप donut recipe को बदले बिना toppings को mix and match कर सकते हैं। Decorator pattern इसी तरह काम करता है: एक object को अतिरिक्त behavior के साथ wrap करें, layer by layer।

Java Code

Step 1: Base interface परिभाषित करें।

public interface DataService {
    String fetchData(String key);
}

Step 2: Core implementation बनाएँ।

public class DatabaseService implements DataService {
    @Override
    public String fetchData(String key) {
        // Simulate database lookup
        System.out.println("  [DB] Querying database for key: " + key);
        return "data-for-" + key;
    }
}

Step 3: Decorators बनाएँ — हर एक original को wrap करता है और behavior जोड़ता है।

// Logging Decorator — logs before and after
public class LoggingDecorator implements DataService {
    private final DataService wrapped;

    public LoggingDecorator(DataService wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public String fetchData(String key) {
        System.out.println("[LOG] Fetching data for key: " + key);
        String result = wrapped.fetchData(key);
        System.out.println("[LOG] Result: " + result);
        return result;
    }
}

// Caching Decorator — returns cached value if available
public class CachingDecorator implements DataService {
    private final DataService wrapped;
    private final Map<String, String> cache = new HashMap<>();

    public CachingDecorator(DataService wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public String fetchData(String key) {
        if (cache.containsKey(key)) {
            System.out.println("[CACHE] Hit for key: " + key);
            return cache.get(key);
        }
        System.out.println("[CACHE] Miss for key: " + key);
        String result = wrapped.fetchData(key);
        cache.put(key, result);
        return result;
    }
}

Step 4: Decorators को layers की तरह stack करें।

// Base service
DataService service = new DatabaseService();

// Wrap with caching
service = new CachingDecorator(service);

// Wrap with logging on top
service = new LoggingDecorator(service);

// First call — cache miss, hits database
service.fetchData("user-42");
// Output:
// [LOG] Fetching data for key: user-42
// [CACHE] Miss for key: user-42
//   [DB] Querying database for key: user-42
// [LOG] Result: data-for-user-42

// Second call — cache hit, skips database
service.fetchData("user-42");
// Output:
// [LOG] Fetching data for key: user-42
// [CACHE] Hit for key: user-42
// [LOG] Result: data-for-user-42

Decorator का इस्तेमाल कब करें

इसका इस्तेमाल करें जब: आप मूल class को modify किए बिना behavior (logging, caching, retries, metrics, auth checks) जोड़ना चाहते हैं। Middleware stacks, I/O streams (Java का अपना BufferedReader जो FileReader को wrap करता है), और Spring AOP proxies में सामान्य।

इससे बचें जब: आपको केवल एक combination की ज़रूरत होगी। Behavior built-in के साथ एक single class सरल है।

6. Template Method Pattern — Steps परिभाषित करें, Subclasses को details भरने दें

समस्या

आपके पास कई classes हैं जो समान समग्र process का पालन करती हैं लेकिन विशिष्ट steps में भिन्न हैं। उदाहरण के लिए, data export में हमेशा शामिल है: (1) data प्राप्त करें, (2) उसे format करें, (3) file में लिखें। लेकिन CSV, PDF, और Excel के लिए formatting अलग है। अगर हर class पूरी process को scratch से implement करती है, तो आप shared logic को duplicate करते हैं।

वास्तविक जीवन का उदाहरण

Sandwiches बनाने के लिए एक recipe template के बारे में सोचें। हर sandwich समान steps का पालन करता है: (1) bread चुनें, (2) filling डालें, (3) toppings डालें, (4) इसे wrap करें। एक turkey sandwich और एक veggie sandwich समान template का पालन करते हैं, लेकिन filling step अलग है। Template Method pattern shared skeleton परिभाषित करता है और हर variant को विशिष्ट steps भरने देता है।

Java Code

Step 1: Abstract template class परिभाषित करें।

public abstract class DataExporter {

    // Template method — defines the algorithm skeleton
    // "final" so subclasses cannot change the order of steps
    public final void export(String reportName) {
        List<Map<String, Object>> data = fetchData(reportName);
        String formatted = formatData(data);
        writeToFile(reportName, formatted);
        System.out.println("Export complete: " + reportName);
    }

    // Shared step — same for all exporters
    private List<Map<String, Object>> fetchData(String reportName) {
        System.out.println("Fetching data for report: " + reportName);
        // Simulate fetching from database
        return List.of(
            Map.of("name", "Alice", "score", 95),
            Map.of("name", "Bob", "score", 87),
            Map.of("name", "Charlie", "score", 92)
        );
    }

    // Abstract step — each subclass implements differently
    protected abstract String formatData(List<Map<String, Object>> data);

    // Shared step — same for all exporters
    private void writeToFile(String reportName, String content) {
        System.out.println("Writing " + content.length() + " characters to file: "
            + reportName);
        // Simulate file writing
    }
}

Step 2: हर format के लिए concrete implementations बनाएँ।

public class CsvExporter extends DataExporter {
    @Override
    protected String formatData(List<Map<String, Object>> data) {
        StringBuilder sb = new StringBuilder("name,score
");
        for (var row : data) {
            sb.append(row.get("name")).append(",").append(row.get("score")).append("
");
        }
        return sb.toString();
    }
}

public class PdfExporter extends DataExporter {
    @Override
    protected String formatData(List<Map<String, Object>> data) {
        StringBuilder sb = new StringBuilder("[PDF Document]
");
        sb.append("=== Report ===
");
        for (var row : data) {
            sb.append("  ").append(row.get("name"))
              .append(": ").append(row.get("score")).append("
");
        }
        return sb.toString();
    }
}

public class ExcelExporter extends DataExporter {
    @Override
    protected String formatData(List<Map<String, Object>> data) {
        StringBuilder sb = new StringBuilder("[Excel Workbook]
");
        sb.append("| Name    | Score |
");
        for (var row : data) {
            sb.append("| ").append(String.format("%-7s", row.get("name")))
              .append(" | ").append(String.format("%-5s", row.get("score")))
              .append(" |
");
        }
        return sb.toString();
    }
}

Step 3: इसका इस्तेमाल करें — calling कोड format के बारे में नहीं जानता या परवाह करता है।

DataExporter csvExporter = new CsvExporter();
csvExporter.export("monthly-scores.csv");
// Output:
// Fetching data for report: monthly-scores.csv
// Writing 39 characters to file: monthly-scores.csv
// Export complete: monthly-scores.csv

DataExporter pdfExporter = new PdfExporter();
pdfExporter.export("monthly-scores.pdf");

DataExporter excelExporter = new ExcelExporter();
excelExporter.export("monthly-scores.xlsx");

Template Method का इस्तेमाल कब करें

इसका इस्तेमाल करें जब: कई classes समान algorithm structure share करती हैं लेकिन कुछ steps में भिन्न हैं। सामान्य उदाहरण: data export (CSV/PDF/Excel), data import (अलग file formats parse करना), authentication flows (OAuth/SAML/LDAP), report generation।

इससे बचें जब: Classes के बीच algorithm steps पूरी तरह अलग हैं। उस मामले में, Strategy pattern बेहतर fit है।

त्वरित तुलना: कौन-सा Pattern क्या हल करता है?

Patternयह किस समस्या को हल करता हैमुख्य विचार
Strategyएक काम के लिए कई algorithmsInterface के ज़रिए runtime पर behavior swap करें
FactoryObject creation input पर निर्भर हैएक जगह creation logic को केंद्रीकृत करें
Builderकई fields वाले जटिल objectsStep by step बनाएँ, फिर finalize करें
Observerएक event कई reactions trigger करता हैEvent publish करें, listeners स्वतंत्र रूप से react करते हैं
DecoratorOriginal को modify किए बिना behavior जोड़ेंObjects को functionality की layers में wrap करें
Template Methodसमान structure, अलग detailsAbstract class skeleton परिभाषित करती है, subclasses steps भरती हैं

मुख्य सीख

सरल शुरुआत करें। Patterns को अपने कोड में ज़बरदस्ती न ठूँसें। पहले सीधा समाधान लिखें। जब आप duplication, rigidity, या messy conditionals देखें, तब pattern की ओर जाएँ।

Patterns structure के बारे में हैं, कोड के बारे में नहीं। ऊपर दिए गए कोड examples templates हैं। असली projects में, आप उन्हें अनुकूलित करेंगे। एक Strategy classes के बजाय lambdas का इस्तेमाल कर सकती है। एक Factory switch statement के बजाय registry map का इस्तेमाल कर सकता है।

समाधान नहीं, समस्या को पहचानना सीखें। जब आप if-else की एक chain देखते हैं जो एक algorithm चुन रही है, तो Strategy के बारे में सोचें। जब आप हर जगह new SomeClass() बिखरा देखते हैं, तो Factory के बारे में सोचें। जब आप 10 parameters वाला constructor देखते हैं, तो Builder के बारे में सोचें।

Patterns को combine करें। असली applications कई patterns का एक साथ इस्तेमाल करती हैं। एक Factory Strategy objects बना सकता है। एक Builder ऐसे objects बना सकता है जो Observer events fire करते हैं। Patterns building blocks हैं — जैसी ज़रूरत हो वैसे mix करें।

अक्सर पूछे जाने वाले प्रश्न

Strategy pattern और Factory pattern में क्या फ़र्क़ है?

Strategy behavior चुनने के बारे में है — आप चुनते हैं कि कुछ कैसे करना है (credit card बनाम PayPal से भुगतान)। Factory creation चुनने के बारे में है — आप चुनते हैं कि क्या बनाना है (email notification बनाम SMS notification)। Strategy algorithms swap करती है; Factory object types swap करती है। इसे इस तरह सोचें: Strategy तय करती है कैसे, Factory तय करती है क्या।

Constructor के बजाय Builder pattern का इस्तेमाल कब करना चाहिए?

Builder का इस्तेमाल करें जब आपके object में 4 या 5 से ज़्यादा parameters हों, खासकर अगर कई optional हों। अगर आप खुद को new User(null, null, "Alice", null, 25, null, "ADMIN") बहुत सारे nulls के साथ लिखते पाते हैं, तो यह स्पष्ट संकेत है कि आपको Builder की ज़रूरत है। Builder का इस्तेमाल तब भी करें जब आप immutable objects चाहते हैं (सभी fields final) लेकिन construction के दौरान flexibility चाहते हैं।

क्या मैं Spring Boot के साथ design patterns का इस्तेमाल कर सकता हूँ?

हाँ, और Spring Boot actually कई patterns का internally इस्तेमाल करता है। @EventListener Observer pattern है। Spring का BeanFactory Factory pattern है। dependency injection के साथ @Qualifier वही हासिल करता है जो Strategy pattern करता है। HandlerInterceptor chain Decorator pattern है। इन patterns को सीखने से आपको Spring Boot को गहरे स्तर पर समझने में मदद मिलती है।

क्या lambdas और records जैसी modern Java features के साथ design patterns अभी भी प्रासंगिक हैं?

बिल्कुल, लेकिन implementation बदलती है। Strategy pattern, जिसे पहले एक पूरी interface और कई classes की ज़रूरत होती थी, अब एक सरल lambda हो सकती है: processor.setStrategy(amount -> System.out.println("Paid " + amount))। Records सरल data holders के लिए कई Builder use cases को बदलते हैं। Patterns concepts के रूप में अभी भी प्रासंगिक हैं — modern Java के साथ कोड बस ज़्यादा concise हो जाता है।

मुझे कैसे पता चले कि अपनी समस्या के लिए कौन-सा design pattern इस्तेमाल करना है?

Pain point की पहचान करके शुरू करें। अगर आपके पास एक algorithm चुनने वाली कई if-else branches हैं, तो Strategy इस्तेमाल करें। अगर आप input के आधार पर अलग object types बना रहे हैं, तो Factory इस्तेमाल करें। अगर एक constructor में बहुत सारे parameters हैं, तो Builder इस्तेमाल करें। अगर एक action को कई side effects trigger करने की ज़रूरत है, तो Observer इस्तेमाल करें। अगर आप मौजूदा कोड को छुए बिना logging या caching जैसा behavior जोड़ना चाहते हैं, तो Decorator इस्तेमाल करें। अगर classes समान process share करती हैं लेकिन विशिष्ट steps में भिन्न हैं, तो Template Method इस्तेमाल करें। कुंजी समस्या को match करना है, pattern को ज़बरदस्ती नहीं ठूँसना।

More from Innovation

Innovation

इस साल बुद्धिमत्ता सस्ती हो गई — और मूल्य उस ओर खिसक गया जो आप उससे बनाते हैं

एक करोड़ AI टोकन की लागत एक ही साल में लगभग चौगुनी गिर गई। जब कच्चा माल इतनी तेज़ी से इतना सस्ता हो जाए, तो बढ़त मॉडल के मालिक होने की नहीं, बल्कि उपयोगी चीज़ बनाने वाले की होती है।

Jun 14, 20265 min read
Innovation

वह साल जब रोबोट ने प्रदर्शन करना बंद किया और काम करना शुरू किया

एक दशक तक ह्यूमनॉइड रोबोट सिर्फ़ डेमो वीडियो थे — हमेशा प्रभावशाली, हमेशा पाँच साल दूर। 2026 में कहानी चुपचाप वीडियो से उत्पादन संख्या में बदल गई। यहाँ बताया गया है कि वास्तव में क्या बदला।

Jun 14, 20265 min read
Innovation

AI हर महीने सस्ता हो रहा है। फिर सबके AI बिल क्यों फट रहे हैं?

AI की कीमत एक साल में करीब 4 गुना गिरी — जो काम $20 में होता था वह अब सेंट्स में। फिर भी कंपनियां सालभर का AI बजट महीनों में फूंक रही हैं। दोनों बातें सच हैं, और वजह समझना ज़रूरी है।

Jun 12, 20264 min read