Skip to main content
Innovation|Innovation

జావాలో Design Patterns: LEGO తో నిర్మించినట్లు మంచి సాఫ్ట్‌వేర్ రాయండి

జావాలో ఆరు అవసరమైన design patterns — Strategy, Factory, Builder, Observer, Decorator, మరియు Template Method — real-world analogies, working code examples, మరియు ప్రతి ఒక్కటి ఎప్పుడు ఉపయోగించాలో స్పష్టమైన మార్గదర్శకత్వంతో beginner-friendly guide.

8 ఏప్రిల్, 202614 min read

Design Patterns ఎందుకు ముఖ్యం

మీరు LEGO బ్రిక్స్ తో ఒక ఇల్లు కడుతున్నారని ఊహించుకోండి. మీరు బ్రిక్స్ ను యాదృచ్ఛికంగా కలిపి అది పనిచేస్తుందని ఆశించవచ్చు. లేదా వేలాది మంది బిల్డర్లు ఇప్పటికే పరీక్షించిన నిరూపితమైన బ్లూప్రింట్‌లను అనుసరించవచ్చు. Design patterns అనేవి సాఫ్ట్‌వేర్ కోసం ఆ బ్లూప్రింట్‌లు.

Design pattern అంటే ఒక సాధారణ సమస్యకు పునర్వినియోగ పరిష్కారం. మీరు pattern నుండి కోడ్ ను copy-paste చేయరు. బదులుగా, మీరు ఆలోచనను నేర్చుకుని మీ పరిస్థితికి అనుగుణంగా మార్చుకుంటారు. దీన్ని ఒక recipe లాగా ఆలోచించండి: recipe మీకు steps చెప్తుంది, కానీ మీరు మీ స్వంత ingredients ఎంచుకుంటారు.

ఈ guide లో, ప్రతి Java developer తెలుసుకోవాల్సిన ఆరు అవసరమైన patterns గురించి తెలుసుకుందాం. ప్రతిదానికి: ఏ సమస్యను పరిష్కరిస్తుంది, సాధారణ real-world analogy, పనిచేసే Java code, మరియు దాన్ని ఎప్పుడు ఉపయోగించాలో నేర్చుకుందాం.

1. Strategy Pattern — Runtime లో మీ పద్ధతిని ఎంచుకోండి

సమస్య

మీకు ఒక పని ఉంది, దాన్ని అనేక విధాలుగా చేయవచ్చు. ఉదాహరణకు, ఒక customer credit card, PayPal, లేదా bank transfer ద్వారా చెల్లించవచ్చు. ప్రతి method ను handle చేయడానికి if-else chains ఉపయోగిస్తే, మీ code messy అవుతుంది. ప్రతిసారి కొత్త payment method జోడించినప్పుడు, అదే class తెరిచి మరో branch జోడించాలి. ఇది Open/Closed Principle ను ఉల్లంఘిస్తుంది — classes extension కోసం open గా ఉండాలి కానీ modification కోసం closed గా ఉండాలి.

Real-World Analogy

ఇంటి నుండి school కి ప్రయాణించడం గురించి ఆలోచించండి. మీరు car, bus, లేదా train తీసుకోవచ్చు. గమ్యస్థానం ఒకటే, కానీ అక్కడికి చేరే పద్ధతి వేరు. మీరు పరిస్థితి ఆధారంగా strategy ఎంచుకుంటారు: వర్షం పడుతుంటే car తీసుకుంటారు; డబ్బు ఆదా చేయాలంటే bus తీసుకుంటారు. Strategy pattern ఇలాగే పనిచేస్తుంది — ఒకే లక్ష్యం, వేరే విధానాలు, runtime లో ఎంపిక.

Java Code

Step 1: Strategy interface ను define చేయండి.

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);
    }
}

// ఉపయోగం
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 ఎప్పుడు ఉపయోగించాలి

ఉపయోగించండి: ఒకే task కోసం అనేక algorithms ఉన్నప్పుడు మరియు వాటిని ఉపయోగించే class మార్చకుండా వాటి మధ్య switch చేయాలనుకున్నప్పుడు. సాధారణ ఉదాహరణలు: payment methods, sorting algorithms, compression formats, validation rules.

ఉపయోగించవద్దు: మీకు ఒకటి లేదా రెండు సాధారణ variations మాత్రమే ఉన్నప్పుడు. ఎప్పటికీ పెరగని రెండు options ఉన్నప్పుడు సాధారణ if-else సరిపోతుంది.

2. Factory Pattern — ఖచ్చితమైన Class తెలియకుండా Objects సృష్టించండి

సమస్య

మీ code కు ఏదైనా input ఆధారంగా వివిధ రకాల objects సృష్టించాలి. ఉదాహరణకు, మీరు notification పంపాలనుకుంటున్నారు, కానీ కొన్నిసార్లు అది email, కొన్నిసార్లు SMS, కొన్నిసార్లు push notification. Calling code new EmailNotification() ను నేరుగా ఉపయోగిస్తే, అది ఆ specific class కు tightly coupled అవుతుంది. కొత్త notification type జోడించడమంటే notifications సృష్టించే ప్రతి చోటా మార్చడం.

Real-World Analogy

Restaurant లో food order చేయడం గురించి ఆలోచించండి. మీరు waiter కి "నాకు pizza కావాలి" అని చెప్తారు. మీరు kitchen లోకి వెళ్ళి స్వయంగా చేయరు. Kitchen (factory) దాన్ని ఎలా తయారు చేయాలో నిర్ణయిస్తుంది — ఏ chef, ఏ oven, ఏ ingredients. మీకు మీ pizza వస్తుంది. Factory pattern ఇలాగే పనిచేస్తుంది: మీకు కావలసింది అడగండి, factory దాన్ని ఎలా సృష్టించాలో తెలుసుకుంటుంది.

Java Code

Step 1: Product interface ను define చేయండి.

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);
        };
    }
}

// ఉపయోగం
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 సృష్టించాలనుకున్నప్పుడు మరియు ఖచ్చితమైన type input లేదా configuration పై ఆధారపడినప్పుడు. సాధారణ ఉదాహరణలు: notification channels, database drivers, document parsers, UI components.

ఉపయోగించవద్దు: మీరు ఎల్లప్పుడూ ఒకే type సృష్టించినప్పుడు. ఒక product తో factory అనేది over-engineering.

3. Builder Pattern — సంక్లిష్ట Objects ను దశలవారీగా నిర్మించండి

సమస్య

కొన్ని objects కు చాలా fields ఉంటాయి. ఒక User కు name, email, phone, address, age, మరియు role ఉండవచ్చు. 10 parameters తో constructor ఉపయోగిస్తే, order గుర్తుంచుకోవడం అసాధ్యం. ఏది phone number? ఏది email? దీన్ని "telescoping constructor" సమస్య అంటారు.

Real-World Analogy

Custom burger order చేయడం గురించి ఆలోచించండి. మీరు 8 toppings ను ఒకేసారి అరవరు. బదులుగా, దశలవారీగా build చేస్తారు: "నాకు beef patty కావాలి, lettuce జోడించు, tomato జోడించు, cheese జోడించు, onions వద్దు, extra pickles." Builder pattern ఇలాగే పనిచేస్తుంది — ప్రతి field ను ఒకదాని తర్వాత ఒకటి, ఏ order లోనైనా set చేసి, చివరగా 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);
        }
    }
}

// ఉపయోగం — చదవగలిగే, flexible, ఏ 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 defaults ఉపయోగిస్తాయి

Shortcut: Lombok @Builder

Builder ను manually రాయడం చాలా tedious. 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;
}

// ఉపయోగం — identical fluent API, zero boilerplate
Order order = Order.builder()
    .orderId("ORD-001")
    .customerName("Charlie")
    .totalAmount(149.99)
    .shippingAddress("123 Main St")
    .build();
// status default గా "PENDING" ఉంటుంది

Java Records + Compact Constructors

సాధారణ data carriers కోసం, Java 16+ records మీకు immutability ఉచితంగా ఇస్తాయి:

public record Product(String name, double price, String category) {
    // Validation కోసం Compact constructor
    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 కు notify చేయాలి. Order code నేరుగా వీటన్నిటినీ call చేస్తే, ప్రతి listener కు tightly coupled అవుతుంది. కొత్త reaction జోడించడమంటే order code మార్చడం.

Real-World Analogy

YouTube channel గురించి ఆలోచించండి. Creator కొత్త video upload చేసినప్పుడు, ప్రతి subscriber కు notification వస్తుంది. Creator ప్రతి subscriber కు వ్యక్తిగతంగా call చేయరు. బదులుగా, YouTube system handle చేస్తుంది: "ఏదో జరిగింది (కొత్త video) — sign up చేసిన అందరికీ చెప్పు." Observer pattern ఇలాగే పనిచేస్తుంది: event publish చేయండి, registered listeners అందరూ స్వతంత్రంగా react అవుతారు.

Java Code — Spring Events

Step 1: Event ను define చేయండి.

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) {
        // Database లో order save చేయడం...
        System.out.println("Order " + orderId + " saved to database.");

        // Event publish చేయడం — OrderService కు ఎవరు listen చేస్తారో తెలియదు
        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());
    }
}

// placeOrder() call చేసినప్పుడు, మూడు listeners అన్నీ automatically fire అవుతాయి:
// 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 ను separate threads లో run చేయడానికి @Async జోడించండి — order service emails లేదా analytics కోసం wait చేయదు:

@Component
public class EmailListener {
    @Async
    @EventListener
    public void onOrderPlaced(OrderPlacedEvent event) {
        // Separate thread లో run అవుతుంది — order flow ను block చేయదు
        sendEmail(event.getCustomerEmail(), "Order Confirmed: " + event.getOrderId());
    }
}

Observer/Event ఎప్పుడు ఉపయోగించాలి

ఉపయోగించండి: ఒక action decouple చేయవలసిన అనేక side effects trigger చేసినప్పుడు. సాధారణ ఉదాహరణలు: order placed (email + inventory + analytics), user registered (welcome email + default settings), payment received (receipt + ledger update).

ఉపయోగించవద్దు: ఒక event కు ఒక reaction మాత్రమే ఉన్నప్పుడు. Event infrastructure కంటే direct method call సరళం.

5. Decorator Pattern — Original మార్చకుండా Features జోడించండి

సమస్య

మీకు పనిచేసే class ఉంది, దానికి extra behavior జోడించాలనుకుంటున్నారు — logging, caching, లేదా retry logic వంటివి — original code మార్చకుండా. Original class edit చేస్తే, దాన్ని break చేసే risk ఉంది. ప్రతి combination కోసం subclasses సృష్టిస్తే (LoggingService, CachingService, LoggingCachingService), class explosion వస్తుంది.

Real-World Analogy

Plain donut decorate చేయడం గురించి ఆలోచించండి. Donut అనేది core object. మీరు chocolate glaze జోడించవచ్చు (ఒక decorator), తర్వాత sprinkles (మరొక decorator), తర్వాత whipped cream (మూడవ decorator). ప్రతి topping మునుపటిదాన్ని wrap చేస్తుంది. Donut recipe మార్చకుండా toppings mix and match చేయవచ్చు. Decorator pattern ఇలాగే పనిచేస్తుంది: object ను layer by layer extra behavior తో wrap చేయండి.

Java Code

Step 1: Base interface ను define చేయండి.

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

Step 2: Core implementation సృష్టించండి.

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

Step 3: Decorators సృష్టించండి — ప్రతి ఒక్కటి original ను wrap చేసి behavior జోడిస్తుంది.

// Logging Decorator — ముందు మరియు తర్వాత log చేస్తుంది
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 — available అయితే cached value return చేస్తుంది
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();

// Caching తో wrap చేయండి
service = new CachingDecorator(service);

// పైన logging తో wrap చేయండి
service = new LoggingDecorator(service);

// మొదటి call — cache miss, database hit అవుతుంది
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

// రెండవ call — cache hit, database skip అవుతుంది
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 ఎప్పుడు ఉపయోగించాలి

ఉపయోగించండి: Original 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 ను Define చేయండి, Subclasses Details పూర్తి చేయనివ్వండి

సమస్య

మీకు అనేక classes ఉన్నాయి, అవి ఒకే overall process ను follow చేస్తాయి కానీ specific steps లో భిన్నంగా ఉంటాయి. ఉదాహరణకు, data export ఎల్లప్పుడూ ఇలా ఉంటుంది: (1) data fetch చేయడం, (2) format చేయడం, (3) file కు write చేయడం. కానీ formatting CSV, PDF, మరియు Excel కు వేరుగా ఉంటుంది. ప్రతి class మొత్తం process ను scratch నుండి implement చేస్తే, shared logic duplicate అవుతుంది.

Real-World Analogy

Sandwiches తయారు చేయడానికి recipe template గురించి ఆలోచించండి. ప్రతి sandwich ఒకే steps follow చేస్తుంది: (1) bread ఎంచుకోండి, (2) filling జోడించండి, (3) toppings జోడించండి, (4) wrap చేయండి. Turkey sandwich మరియు veggie sandwich ఒకే template ను follow చేస్తాయి, కానీ filling step వేరుగా ఉంటుంది. Template Method pattern shared skeleton ను define చేసి, ప్రతి variant specific steps fill in చేయనిస్తుంది.

Java Code

Step 1: Abstract template class ను define చేయండి.

public abstract class DataExporter {

    // Template method — algorithm skeleton ను define చేస్తుంది
    // "final" కాబట్టి subclasses steps order మార్చలేవు
    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 — అన్ని exporters కు ఒకే విధంగా
    private List<Map<String, Object>> fetchData(String reportName) {
        System.out.println("Fetching data for report: " + reportName);
        return List.of(
            Map.of("name", "Alice", "score", 95),
            Map.of("name", "Bob", "score", 87),
            Map.of("name", "Charlie", "score", 92)
        );
    }

    // Abstract step — ప్రతి subclass వేరుగా implement చేస్తుంది
    protected abstract String formatData(List<Map<String, Object>> data);

    // Shared step — అన్ని exporters కు ఒకే విధంగా
    private void writeToFile(String reportName, String content) {
        System.out.println("Writing " + content.length() + " characters to file: "
            + reportName);
    }
}

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\n");
        for (var row : data) {
            sb.append(row.get("name")).append(",").append(row.get("score")).append("\n");
        }
        return sb.toString();
    }
}

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

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

Step 3: ఉపయోగించండి — calling code కు 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.

ఉపయోగించవద్దు: Algorithm steps classes మధ్య పూర్తిగా భిన్నంగా ఉన్నప్పుడు. అటువంటి సందర్భంలో, Strategy pattern మంచి ఎంపిక.

త్వరిత పోలిక: ఏ Pattern ఏ సమస్యను పరిష్కరిస్తుంది?

Patternపరిష్కరించే సమస్యముఖ్య ఆలోచన
Strategyఒక task కోసం అనేక algorithmsInterface ద్వారా runtime లో behavior swap చేయడం
FactoryObject creation input పై ఆధారపడినప్పుడుCreation logic ను ఒకే చోట centralize చేయడం
Builderచాలా fields ఉన్న complex objectsదశలవారీగా build చేసి, finalize చేయడం
Observerఒక event అనేక reactions trigger చేసినప్పుడుEvent publish చేయండి, listeners స్వతంత్రంగా react అవుతారు
DecoratorOriginal modify చేయకుండా behavior జోడించడంObjects ను functionality layers లో wrap చేయడం
Template Methodఒకే structure, వేరే detailsAbstract class skeleton define చేస్తుంది, subclasses steps fill in చేస్తాయి

ముఖ్య విషయాలు

సాధారణంగా ప్రారంభించండి. మీ code లో patterns ను force చేయవద్దు. ముందు straightforward solution రాయండి. Duplication, rigidity, లేదా messy conditionals గమనించినప్పుడు, pattern వైపు చూడండి.

Patterns structure గురించి, code గురించి కాదు. పై code examples templates. Real projects లో, మీరు వాటిని adapt చేస్తారు. Strategy classes బదులు lambdas ఉపయోగించవచ్చు. Factory switch statement బదులు registry map ఉపయోగించవచ్చు.

సమస్యను గుర్తించడం నేర్చుకోండి, solution మాత్రమే కాదు. Algorithm ఎంచుకునే if-else chain చూసినప్పుడు, Strategy ఆలోచించండి. ఎక్కడైనా new SomeClass() చూసినప్పుడు, Factory ఆలోచించండి. 10 parameters ఉన్న constructor చూసినప్పుడు, Builder ఆలోచించండి.

Patterns కలపండి. Real applications అనేక patterns కలిపి ఉపయోగిస్తాయి. Factory Strategy objects సృష్టించవచ్చు. Builder Observer events fire చేసే objects నిర్మించవచ్చు. Patterns building blocks — అవసరమైనట్లు mix చేయండి.

తరచుగా అడిగే ప్రశ్నలు (FAQ)

Strategy pattern మరియు Factory pattern మధ్య తేడా ఏమిటి?

Strategy అనేది behavior ఎంచుకోవడం గురించి — ఏదైనా ఎలా చేయాలో ఎంచుకోవడం (credit card vs. PayPal తో pay చేయడం). Factory అనేది creation ఎంచుకోవడం గురించి — ఏమి create చేయాలో ఎంచుకోవడం (email notification vs. SMS notification). Strategy algorithms swap చేస్తుంది; Factory object types swap చేస్తుంది. ఇలా ఆలోచించండి: Strategy HOW నిర్ణయిస్తుంది, Factory WHAT నిర్ణయిస్తుంది.

Constructor బదులు Builder pattern ఎప్పుడు ఉపయోగించాలి?

మీ object కు 4 లేదా 5 కంటే ఎక్కువ parameters ఉన్నప్పుడు Builder ఉపయోగించండి, ముఖ్యంగా చాలా optional అయినప్పుడు. new User(null, null, "Alice", null, 25, null, "ADMIN") లాగా చాలా nulls తో రాస్తున్నట్లయితే, మీకు Builder అవసరమని స్పష్టమైన సంకేతం. Construction సమయంలో flexibility కావాలి కానీ immutable objects (అన్ని fields final) కావాలనుకున్నప్పుడు కూడా Builder ఉపయోగించండి.

Spring Boot తో design patterns ఉపయోగించవచ్చా?

అవును, Spring Boot internally చాలా patterns ఉపయోగిస్తుంది. @EventListener అనేది Observer pattern. Spring యొక్క BeanFactory అనేది Factory pattern. Dependency injection తో @Qualifier Strategy pattern achieve చేస్తుంది. HandlerInterceptor chain అనేది Decorator pattern. ఈ patterns నేర్చుకోవడం Spring Boot ను deeper level లో అర్థం చేసుకోవడానికి సహాయపడుతుంది.

Lambdas మరియు records వంటి modern Java features తో design patterns ఇంకా relevant గా ఉన్నాయా?

ఖచ్చితంగా, కానీ implementation మారుతుంది. Strategy pattern, ఇది ఒకప్పుడు full interface plus multiple classes అవసరం, ఇప్పుడు simple lambda గా ఉండవచ్చు: processor.setStrategy(amount -> System.out.println("Paid " + amount)). Records simple data holders కోసం చాలా Builder use cases replace చేస్తాయి. Patterns concepts గా ఇప్పటికీ relevant — code modern Java తో మరింత concise అవుతుంది.

నా సమస్య కోసం ఏ design pattern ఉపయోగించాలో ఎలా తెలుసుకోవాలి?

Pain point ను identify చేయడంతో ప్రారంభించండి. Algorithm ఎంచుకునే అనేక if-else branches ఉంటే, Strategy ఉపయోగించండి. Input ఆధారంగా వివిధ object types create చేస్తుంటే, Factory ఉపయోగించండి. Constructor కు చాలా parameters ఉంటే, Builder ఉపయోగించండి. ఒక action అనేక side effects trigger చేయాల్సి ఉంటే, Observer ఉపయోగించండి. Existing code touch చేయకుండా logging లేదా caching వంటి behavior జోడించాలంటే, Decorator ఉపయోగించండి. Classes ఒకే process share చేస్తూ specific steps లో భిన్నంగా ఉంటే, Template Method ఉపయోగించండి. సమస్యను match చేయడం ముఖ్యం, pattern ను force చేయడం కాదు.

More from Innovation

Innovation

ఈ ఏడాది తెలివితేటలు చౌకయ్యాయి — విలువ మీరు దానితో నిర్మించేదానికి మారింది

ఒక మిలియన్ AI టోకెన్ల ఖర్చు ఒకే ఏడాదిలో దాదాపు నాలుగు రెట్లు తగ్గింది. ముడిసరుకు ఇంత వేగంగా చౌకైనప్పుడు, ప్రయోజనం మోడల్ ఎవరిది అనేది కాకుండా ఉపయోగకరమైనది ఎవరు నిర్మిస్తారు అనేదిగా మారుతుంది.

14, జూన్ 20263 min read
Innovation

రోబోట్లు ప్రదర్శన ఆపి పని మొదలుపెట్టిన సంవత్సరం

ఒక దశాబ్దం పాటు హ్యూమనాయిడ్ రోబోట్లు కేవలం డెమో వీడియోలే — ఎప్పుడూ ఆకట్టుకునేవి, ఎప్పుడూ ఐదేళ్ళ దూరంలో ఉండేవి. 2026లో కథ నిశ్శబ్దంగా వీడియోల నుండి ఉత్పత్తి సంఖ్యలకు మారింది.

14, జూన్ 20263 min read
Innovation

AI నెలనెలా చౌకవుతోంది. మరి అందరి AI బిల్లులు ఎందుకు పేలిపోతున్నాయి?

AI ధర ఏడాదిలో సుమారు 4 రెట్లు తగ్గింది — కానీ కంపెనీలు వార్షిక AI బడ్జెట్లను నెలల్లో ఖర్చు చేస్తున్నాయి. రెండూ నిజమే — కారణం అర్థం చేసుకోవడం ముఖ్యం.

12, జూన్ 20263 min read