Javada Design Patterns

30.06.2026 | Muallif: Jaxongir a.k.a | Kategoriya: Patternlar & SOLID | 26 daqiqa o'qish

Design pattern - dasturlashdagi ko‘p uchraydigan muammolar uchun sinovdan o‘tgan yechim shakli.

Javada Design Patterns quyidagicha berilgan: Creational: Singleton, Factory, Builder, Prototype; Structural: Adapter, Decorator, Proxy, Facade; Behavioral: Strategy, Observer, Command, Template Method; Anti-patterns to avoid.

Muhim narsa: patternni yodlash emas, qachon kerakligini tushunish.


1. Design Pattern nima uchun kerak?

Tasavvur qil, loyihada payment qilish kerak:

Payme
Click
Uzum
Cash
Card

Agar hammasini if/else bilan yozaversak:

if (type.equals("PAYME")) {
    // payme logic
} else if (type.equals("CLICK")) {
    // click logic
} else if (type.equals("UZUM")) {
    // uzum logic
}

Boshida ishlaydi. Lekin loyiha kattalashsa:

  • kod chalkashadi;

  • yangi payment qo‘shish qiyinlashadi;

  • test yozish qiyinlashadi;

  • bitta class juda katta bo‘lib ketadi;

  • SOLID buziladi.

Design pattern shu muammolarni tartibli hal qiladi.


2. Pattern turlari

Design patternlar odatda 3 guruhga bo‘linadi:

Tur

Vazifasi

Creational

Object yaratish usullarini tartiblaydi

Structural

Class/objectlarni bir-biriga ulashni tartiblaydi

Behavioral

Objectlar orasidagi xatti-harakatni tartiblaydi


3. Creational Patterns

Creational patternlar object yaratish bilan bog‘liq.


3.1 Singleton Pattern

Singleton - classdan faqat bitta instance bo‘lishini ta’minlaydi.

Masalan:

  • application config;

  • logger;

  • cache manager;

  • connection manager.

Oddiy Java’da:

public class AppConfig {

    private static final AppConfig INSTANCE = new AppConfig();

    private AppConfig() {
    }

    public static AppConfig getInstance() {
        return INSTANCE;
    }
}

Ishlatish:

AppConfig config = AppConfig.getInstance();

Eng yaxshi Java Singleton varianti

public enum AppConfig {
    INSTANCE;

    public String getAppName() {
        return "Order Service";
    }
}

Ishlatish:

String appName = AppConfig.INSTANCE.getAppName();

enum singleton reflection va serialization muammolariga chidamliroq.


Spring’da Singleton

Spring’da ko‘p beanlar default holatda singleton bo‘ladi.

@Service
public class UserService {
}

Bu classdan Spring context ichida odatda bitta object yaratiladi.

Lekin e’tibor ber:

@Service
public class UserService {
    private String currentUser;
}

Bu xavfli. Chunki singleton bean umumiy bo‘ladi. Ko‘p request kelganda currentUser aralashib ketishi mumkin.

Yaxshi:

@Service
public class UserService {

    public UserDto getUser(Long userId) {
        String currentUser = getCurrentUser();
        return findUser(userId);
    }
}

State’ni local variable’da saqla.


Singleton qachon yomon?

Agar noto‘g‘ri ishlatilsa:

  • global state ko‘payadi;

  • test qilish qiyinlashadi;

  • dependency yashirin bo‘ladi;

  • thread-safety muammosi chiqadi.

Spring loyihada qo‘lda Singleton yozish kamdan-kam kerak bo‘ladi.


4. Factory Pattern

Factory - object yaratish logic’ini alohida joyga chiqaradi.

Masalan, payment service tanlash.

Avval interface yozamiz:

public interface PaymentService {
    void pay(BigDecimal amount);
}

Implementationlar:

@Service
public class PaymePaymentService implements PaymentService {

    @Override
    public void pay(BigDecimal amount) {
        System.out.println("Payme orqali to‘lov: " + amount);
    }
}
@Service
public class ClickPaymentService implements PaymentService {

    @Override
    public void pay(BigDecimal amount) {
        System.out.println("Click orqali to‘lov: " + amount);
    }
}

Enum:

public enum PaymentType {
    PAYME,
    CLICK
}

Factory:

@Component
public class PaymentServiceFactory {

    private final PaymePaymentService paymePaymentService;
    private final ClickPaymentService clickPaymentService;

    public PaymentServiceFactory(
            PaymePaymentService paymePaymentService,
            ClickPaymentService clickPaymentService
    ) {
        this.paymePaymentService = paymePaymentService;
        this.clickPaymentService = clickPaymentService;
    }

    public PaymentService getService(PaymentType type) {
        return switch (type) {
            case PAYME -> paymePaymentService;
            case CLICK -> clickPaymentService;
        };
    }
}

Ishlatish:

@Service
public class OrderService {

    private final PaymentServiceFactory paymentServiceFactory;

    public OrderService(PaymentServiceFactory paymentServiceFactory) {
        this.paymentServiceFactory = paymentServiceFactory;
    }

    public void createOrder(PaymentType paymentType, BigDecimal amount) {
        PaymentService paymentService = paymentServiceFactory.getService(paymentType);
        paymentService.pay(amount);
    }
}

Factory qachon kerak?

Agar object yoki service tanlash shartga bog‘liq bo‘lsa:

payment type bo‘yicha
notification type bo‘yicha
delivery type bo‘yicha
report format bo‘yicha
file parser type bo‘yicha

Masalan:

PDF report
Excel report
CSV report

Factory shu yerda juda qulay.


5. Builder Pattern

Builder - ko‘p fieldli objectni chiroyli yaratish uchun ishlatiladi.

Masalan:

User user = new User(
        "Ali",
        "ali@gmail.com",
        25,
        "Tashkent",
        true,
        LocalDate.now()
);

Bu o‘qishga noqulay. Qaysi qiymat qaysi fieldga tegishli ekanini tushunish qiyin.

Builder bilan:

User user = User.builder()
        .name("Ali")
        .email("ali@gmail.com")
        .age(25)
        .city("Tashkent")
        .active(true)
        .createdAt(LocalDate.now())
        .build();

Bu ancha tushunarli.


Lombok bilan Builder

@Builder
@Getter
public class User {
    private String name;
    private String email;
    private int age;
    private String city;
    private boolean active;
}

Ishlatish:

User user = User.builder()
        .name("Ali")
        .email("ali@gmail.com")
        .age(25)
        .build();

Builder qachon kerak?

Quyidagi holatlarda:

  • fieldlar ko‘p bo‘lsa;

  • optional fieldlar ko‘p bo‘lsa;

  • constructor juda uzun bo‘lib ketsa;

  • DTO yoki response object yaratishda;

  • immutable object yaratishda.


Builder qachon kerak emas?

Agar classda 2-3 field bo‘lsa, oddiy constructor yetadi.

public record UserDto(Long id, String name) {
}

Bu holatda builder shart emas.


6. Prototype Pattern

Prototype - mavjud objectdan nusxa olish.

Java’da bunga clone() orqali misol berishadi, lekin real loyihada ko‘p ishlatilmaydi.

Misol:

public class Document implements Cloneable {

    private String title;
    private String content;

    @Override
    public Document clone() {
        try {
            return (Document) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

Lekin clone() ko‘p muammo beradi:

  • shallow copy;

  • deep copy;

  • mutable fieldlar;

  • collectionlar;

  • noto‘g‘ri nusxa olish.

Real loyihada ko‘proq copy constructor yoki static factory ishlatiladi:

public class User {

    private String name;
    private String email;

    public User(User source) {
        this.name = source.name;
        this.email = source.email;
    }
}

Yoki record bilan:

public record UserDto(String name, String email) {
}

7. Structural Patterns

Structural patternlar class va objectlarni qanday ulashni tartiblaydi.


8. Adapter Pattern

Adapter - mos kelmaydigan interface’ni loyihamizga moslab beradi.

Masalan, bizning sistemada shunday interface bor:

public interface SmsSender {
    void send(String phone, String message);
}

Lekin tashqi SMS provider boshqacha ishlaydi:

public class EskizSmsClient {
    public void sendSms(String token, String phoneNumber, String text) {
        System.out.println("Eskiz SMS yuborildi");
    }
}

Adapter yozamiz:

@Component
public class EskizSmsAdapter implements SmsSender {

    private final EskizSmsClient eskizSmsClient;

    public EskizSmsAdapter(EskizSmsClient eskizSmsClient) {
        this.eskizSmsClient = eskizSmsClient;
    }

    @Override
    public void send(String phone, String message) {
        String token = "TOKEN";
        eskizSmsClient.sendSms(token, phone, message);
    }
}

Endi service tashqi clientni bilmaydi:

@Service
public class NotificationService {

    private final SmsSender smsSender;

    public NotificationService(SmsSender smsSender) {
        this.smsSender = smsSender;
    }

    public void sendCode(String phone) {
        smsSender.send(phone, "Tasdiqlash kodi: 1234");
    }
}

Adapter qachon kerak?

  • tashqi API interface’i noqulay bo‘lsa;

  • eski kodni yangi sistemaga ulash kerak bo‘lsa;

  • third-party library’ni o‘z interface’ing orqali ishlatmoqchi bo‘lsang;

  • vendor lock-in kamaytirmoqchi bo‘lsang.


9. Decorator Pattern

Decorator - mavjud objectga qo‘shimcha behavior qo‘shadi.

Masalan, notification yuborish bor.

public interface Notifier {
    void send(String message);
}

Asosiy implementation:

public class EmailNotifier implements Notifier {

    @Override
    public void send(String message) {
        System.out.println("Email: " + message);
    }
}

Decorator:

public class SmsNotifierDecorator implements Notifier {

    private final Notifier notifier;

    public SmsNotifierDecorator(Notifier notifier) {
        this.notifier = notifier;
    }

    @Override
    public void send(String message) {
        notifier.send(message);
        System.out.println("SMS: " + message);
    }
}

Ishlatish:

Notifier notifier = new SmsNotifierDecorator(new EmailNotifier());
notifier.send("Order yaratildi");

Natija:

Email: Order yaratildi
SMS: Order yaratildi

Decorator real Java’da qayerda bor?

Java I/O’da juda ko‘p:

BufferedReader reader = new BufferedReader(
        new InputStreamReader(
                new FileInputStream("data.txt")
        )
);

Bu yerda har bir class avvalgisiga qo‘shimcha imkoniyat qo‘shadi.


Decorator qachon kerak?

  • objectga dinamik qo‘shimcha behavior qo‘shish kerak bo‘lsa;

  • inheritance ko‘payib ketmasligi uchun;

  • optional featurelar ko‘p bo‘lsa.


10. Proxy Pattern

Proxy - asl object oldida turib, unga kirishni nazorat qiladi.

Masalan:

Client -> Proxy -> Real Service

Proxy qo‘shimcha ishlarni qilishi mumkin:

  • logging;

  • security;

  • transaction;

  • caching;

  • lazy loading;

  • access control.


Oddiy Proxy misol

public interface UserService {
    UserDto getUser(Long id);
}

Real service:

public class RealUserService implements UserService {

    @Override
    public UserDto getUser(Long id) {
        System.out.println("DBdan user olindi");
        return new UserDto(id, "Ali");
    }
}

Proxy:

public class UserServiceProxy implements UserService {

    private final UserService target;

    public UserServiceProxy(UserService target) {
        this.target = target;
    }

    @Override
    public UserDto getUser(Long id) {
        System.out.println("Before getUser");
        UserDto result = target.getUser(id);
        System.out.println("After getUser");
        return result;
    }
}

Spring’da Proxy

Spring’da quyidagilar ko‘pincha proxy orqali ishlaydi:

@Transactional
@Cacheable
@Async
@PreAuthorize

Masalan:

@Transactional
public void createOrder() {
    // transaction ichida ishlaydi
}

Spring bu method atrofida proxy yaratadi:

start transaction
methodni chaqir
commit yoki rollback

Proxy bilan muhim muammo: self-invocation

@Service
public class OrderService {

    public void outer() {
        inner();
    }

    @Transactional
    public void inner() {
        // transaction kutilyapti
    }
}

Bu yerda outer() ichidan inner() chaqirilsa, proxy aylanib o‘tiladi. Natijada @Transactional ishlamasligi mumkin.

Yaxshi:

@Service
public class OrderFacade {

    private final OrderService orderService;

    public OrderFacade(OrderService orderService) {
        this.orderService = orderService;
    }

    public void outer() {
        orderService.inner();
    }
}

11. Facade Pattern

Facade - murakkab subsystem ustidan sodda interface beradi.

Masalan, order yaratish jarayoni:

1. user tekshiriladi
2. product tekshiriladi
3. order yaratiladi
4. payment boshlanadi
5. notification yuboriladi

Agar controller hammasini bilsa, yomon:

@PostMapping("/orders")
public OrderDto create(@RequestBody CreateOrderRequest request) {
    userService.validate(request.userId());
    productService.validate(request.productId());
    Order order = orderService.create(request);
    paymentService.pay(order);
    notificationService.send(order);
    return mapper.toDto(order);
}

Controller juda ko‘p narsani bilib qoldi.

Facade bilan:

@Service
public class OrderFacade {

    private final UserService userService;
    private final ProductService productService;
    private final OrderService orderService;
    private final PaymentService paymentService;
    private final NotificationService notificationService;

    public OrderFacade(
            UserService userService,
            ProductService productService,
            OrderService orderService,
            PaymentService paymentService,
            NotificationService notificationService
    ) {
        this.userService = userService;
        this.productService = productService;
        this.orderService = orderService;
        this.paymentService = paymentService;
        this.notificationService = notificationService;
    }

    public OrderDto createOrder(CreateOrderRequest request) {
        userService.validate(request.userId());
        productService.validate(request.productId());

        Order order = orderService.create(request);

        paymentService.pay(order);
        notificationService.send(order);

        return OrderDto.from(order);
    }
}

Controller:

@PostMapping("/orders")
public OrderDto create(@RequestBody CreateOrderRequest request) {
    return orderFacade.createOrder(request);
}

Facade qachon kerak?

  • controller juda semirib ketsa;

  • bitta use case bir nechta service’ni ishlatsa;

  • subsystem murakkab bo‘lsa;

  • tashqi qatlamga sodda API bermoqchi bo‘lsang.


12. Behavioral Patterns

Behavioral patternlar objectlar orasidagi xatti-harakatni tartiblaydi.


13. Strategy Pattern

Strategy - bir nechta algoritm yoki behaviorni almashtirib ishlatish.

Bu Java/Spring backend’da eng ko‘p kerak bo‘ladigan patternlardan biri.

Misol: payment.

public interface PaymentStrategy {
    PaymentType getType();

    void pay(BigDecimal amount);
}

Payme:

@Service
public class PaymePaymentStrategy implements PaymentStrategy {

    @Override
    public PaymentType getType() {
        return PaymentType.PAYME;
    }

    @Override
    public void pay(BigDecimal amount) {
        System.out.println("Payme payment: " + amount);
    }
}

Click:

@Service
public class ClickPaymentStrategy implements PaymentStrategy {

    @Override
    public PaymentType getType() {
        return PaymentType.CLICK;
    }

    @Override
    public void pay(BigDecimal amount) {
        System.out.println("Click payment: " + amount);
    }
}

Resolver:

@Component
public class PaymentStrategyResolver {

    private final Map<PaymentType, PaymentStrategy> strategies;

    public PaymentStrategyResolver(List<PaymentStrategy> strategyList) {
        this.strategies = strategyList.stream()
                .collect(Collectors.toMap(
                        PaymentStrategy::getType,
                        strategy -> strategy
                ));
    }

    public PaymentStrategy resolve(PaymentType type) {
        PaymentStrategy strategy = strategies.get(type);

        if (strategy == null) {
            throw new IllegalArgumentException("Unsupported payment type: " + type);
        }

        return strategy;
    }
}

Service:

@Service
public class PaymentService {

    private final PaymentStrategyResolver resolver;

    public PaymentService(PaymentStrategyResolver resolver) {
        this.resolver = resolver;
    }

    public void pay(PaymentType type, BigDecimal amount) {
        PaymentStrategy strategy = resolver.resolve(type);
        strategy.pay(amount);
    }
}

Strategy nima beradi?

Yangi payment qo‘shish uchun eski kodni kamroq o‘zgartiramiz.

Masalan, Uzum qo‘shish:

@Service
public class UzumPaymentStrategy implements PaymentStrategy {

    @Override
    public PaymentType getType() {
        return PaymentType.UZUM;
    }

    @Override
    public void pay(BigDecimal amount) {
        System.out.println("Uzum payment: " + amount);
    }
}

Tamom. Resolver avtomatik listdan olib map qiladi.


14. Observer Pattern

Observer - bir hodisa bo‘lganda boshqa objectlarga xabar berish.

Masalan:

Order created
   ↓
Send SMS
Send Email
Send Telegram
Update analytics

OrderService hammasini o‘zi qilsa, coupling oshadi.

Spring’da buni event orqali qilish mumkin.

Event:

public record OrderCreatedEvent(Long orderId) {
}

Publisher:

@Service
public class OrderService {

    private final ApplicationEventPublisher eventPublisher;

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

    public void createOrder() {
        Order order = saveOrder();

        eventPublisher.publishEvent(new OrderCreatedEvent(order.getId()));
    }

    private Order saveOrder() {
        return new Order(1L);
    }
}

Listener:

@Component
public class OrderNotificationListener {

    @EventListener
    public void handle(OrderCreatedEvent event) {
        System.out.println("Order notification: " + event.orderId());
    }
}

Transaction bilan ehtiyot bo‘lish

Agar event transaction ichida publish bo‘lsa, listener transaction commit bo‘lmasdan ishlashi mumkin.

Yaxshiroq:

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handle(OrderCreatedEvent event) {
    System.out.println("Transaction commitdan keyin ishlaydi");
}

Bu juda muhim production holat.


Observer qachon kerak?

  • bitta hodisaga bir nechta reaction bo‘lsa;

  • asosiy business logicni notificationdan ajratmoqchi bo‘lsang;

  • coupling kamaytirish kerak bo‘lsa;

  • event-driven yondashuv kerak bo‘lsa.


15. Command Pattern

Command - bajariladigan amalni object sifatida ifodalaydi.

Masalan:

CreateOrderCommand
CancelOrderCommand
RefundPaymentCommand
SendNotificationCommand

Command object:

public interface Command {
    void execute();
}

Implementation:

public class SendEmailCommand implements Command {

    private final EmailService emailService;
    private final String email;
    private final String message;

    public SendEmailCommand(
            EmailService emailService,
            String email,
            String message
    ) {
        this.emailService = emailService;
        this.email = email;
        this.message = message;
    }

    @Override
    public void execute() {
        emailService.send(email, message);
    }
}

Executor:

public class CommandExecutor {

    public void execute(Command command) {
        command.execute();
    }
}

Command qachon kerak?

  • tasklarni queue’ga qo‘yish kerak bo‘lsa;

  • undo/redo kerak bo‘lsa;

  • har xil actionlarni bir xil usulda bajarish kerak bo‘lsa;

  • job processing;

  • audit qilish;

  • retry mechanism.

Real backend’da command pattern ko‘pincha quyidagilarda ko‘rinadi:

background jobs
message handlers
CQRS command handlers
use case classes

16. Template Method Pattern

Template Method - umumiy algoritm skeletini parent classda yozib, ayrim qadamlarni child classga qoldiradi.

Masalan, report generate qilish:

1. data olish
2. formatlash
3. file yaratish
4. saqlash

Abstract class:

public abstract class ReportGenerator {

    public final void generate() {
        Object data = loadData();
        String formatted = format(data);
        save(formatted);
    }

    protected abstract Object loadData();

    protected abstract String format(Object data);

    protected void save(String content) {
        System.out.println("Report saved: " + content);
    }
}

PDF report:

public class PdfReportGenerator extends ReportGenerator {

    @Override
    protected Object loadData() {
        return "PDF data";
    }

    @Override
    protected String format(Object data) {
        return "PDF formatted: " + data;
    }
}

Excel report:

public class ExcelReportGenerator extends ReportGenerator {

    @Override
    protected Object loadData() {
        return "Excel data";
    }

    @Override
    protected String format(Object data) {
        return "Excel formatted: " + data;
    }
}

Template Method qachon kerak?

  • jarayon qadamlari bir xil;

  • lekin ayrim qadamlar har xil;

  • umumiy flow buzilmasligi kerak;

  • inheritance mos bo‘lsa.

Lekin Spring loyihalarda ko‘p hollarda Strategy pattern Template Method’dan qulayroq bo‘ladi. Chunki composition inheritance’dan moslashuvchanroq.


17. Patternlar va SOLID bog‘liqligi

Design patternlar ko‘pincha SOLID prinsiplarini qo‘llashga yordam beradi.

Pattern

Qaysi prinsipga yordam beradi

Strategy

Open/Closed, Dependency Inversion

Factory

Single Responsibility, Open/Closed

Adapter

Dependency Inversion

Decorator

Open/Closed

Facade

Single Responsibility

Observer

Loose Coupling

Command

Single Responsibility


18. Real Spring Boot loyihada eng kerakli patternlar

Java backend uchun eng keraklilari:

Pattern

Real ishlatiladigan joy

Strategy

payment, delivery, notification, pricing

Factory

service tanlash, parser tanlash

Builder

DTO, request/response, test data

Adapter

external API client

Proxy

Spring AOP, transaction, cache, security

Facade

use case orchestration

Observer

Spring events

Decorator

filter, middleware, notification chain

Command

job, queue, CQRS command


19. Anti-patterns

Anti-pattern - yaxshi ko‘rinadigan, lekin amalda zarar beradigan yomon yechim.


19.1 God Object

Bitta class hamma narsani qiladi.

Yomon:

@Service
public class OrderService {

    public void createOrder() {
        validateUser();
        validateProduct();
        calculateDiscount();
        createPayment();
        sendSms();
        sendEmail();
        updateAnalytics();
        generateInvoice();
    }
}

Muammo:

  • class juda katta;

  • test qiyin;

  • o‘zgartirish xavfli;

  • responsibility ko‘p.

Yaxshi:

OrderService
PaymentService
NotificationService
InvoiceService
DiscountService

19.2 Spaghetti Code

Kod oqimi chalkash, tushunish qiyin.

Ko‘pincha sabablar:

  • juda ko‘p nested if;

  • uzun methodlar;

  • nomlar noaniq;

  • business logic har joyga tarqalgan;

  • copy-paste ko‘p.

Yechim:

  • methodlarga ajratish;

  • strategy/factory ishlatish;

  • validationni alohida qilish;

  • nomlarni aniq qo‘yish.


19.3 Golden Hammer

Bitta patternni hamma joyda ishlatish.

Masalan, har joyga Strategy tiqish:

UserCreateStrategy
UserUpdateStrategy
UserDeleteStrategy

Agar oddiy if yetarli bo‘lsa, pattern shart emas.

Pattern kodni soddalashtirishi kerak. Murakkablashtirsa - noto‘g‘ri ishlatilgan.


19.4 Anemic Domain Model

Entity faqat fieldlardan iborat, hamma business logic service’da.

@Entity
public class Order {
    private OrderStatus status;
}

Service:

public void cancel(Order order) {
    if (order.getStatus() == OrderStatus.PAID) {
        throw new IllegalStateException();
    }

    order.setStatus(OrderStatus.CANCELLED);
}

Yaxshiroq:

@Entity
public class Order {

    private OrderStatus status;

    public void cancel() {
        if (status == OrderStatus.PAID) {
            throw new IllegalStateException("Paid order cannot be cancelled");
        }

        this.status = OrderStatus.CANCELLED;
    }
}

Bu domain logicni entity ichida saqlaydi.

Lekin har doim ham entity ichiga hamma logicni tiqish shart emas. Oddiy CRUD loyihalarda service-based yondashuv ham normal.


20. Pattern tanlash bo‘yicha qoida

Pattern tanlashda quyidagi savollarni so‘rang:

1. Menda variantlar ko‘pmi?

Masalan:

Click, Payme, Uzum, Cash

Ha bo‘lsa - Strategy yoki Factory.


2. Tashqi API’ni ichki interface’imga moslash kerakmi?

Ha bo‘lsa - Adapter.


3. Bir objectga qo‘shimcha behavior qo‘shyapmanmi?

Ha bo‘lsa - Decorator.


4. Bir nechta service’ni bitta use case sifatida boshqarayapmanmi?

Ha bo‘lsa - Facade.


5. Hodisa bo‘lganda bir nechta joy reaction qilishi kerakmi?

Ha bo‘lsa - Observer/Event.


6. Spring annotation orqali sehrli ish bo‘lyaptimi?

Masalan:

@Transactional
@Cacheable
@Async

Bu joyda Proxy konsepti bor.


21. Katta real misol: Payment tizim

Talab

Order yaratilganda user quyidagi to‘lov usullaridan birini tanlaydi:

PAYME
CLICK
UZUM
CASH

Yomon variant:

public void pay(String type, BigDecimal amount) {
    if (type.equals("PAYME")) {
        // payme
    } else if (type.equals("CLICK")) {
        // click
    } else if (type.equals("UZUM")) {
        // uzum
    } else if (type.equals("CASH")) {
        // cash
    }
}

Yaxshi variant: Strategy.


Enum

public enum PaymentType {
    PAYME,
    CLICK,
    UZUM,
    CASH
}

Interface

public interface PaymentStrategy {
    PaymentType type();

    void pay(BigDecimal amount);
}

Implementations

@Service
public class PaymeStrategy implements PaymentStrategy {

    @Override
    public PaymentType type() {
        return PaymentType.PAYME;
    }

    @Override
    public void pay(BigDecimal amount) {
        System.out.println("Payme orqali to‘lov");
    }
}
@Service
public class ClickStrategy implements PaymentStrategy {

    @Override
    public PaymentType type() {
        return PaymentType.CLICK;
    }

    @Override
    public void pay(BigDecimal amount) {
        System.out.println("Click orqali to‘lov");
    }
}

Resolver

@Component
public class PaymentStrategyResolver {

    private final Map<PaymentType, PaymentStrategy> strategies;

    public PaymentStrategyResolver(List<PaymentStrategy> strategyList) {
        this.strategies = strategyList.stream()
                .collect(Collectors.toMap(
                        PaymentStrategy::type,
                        Function.identity()
                ));
    }

    public PaymentStrategy resolve(PaymentType type) {
        PaymentStrategy strategy = strategies.get(type);

        if (strategy == null) {
            throw new IllegalArgumentException("Payment type supported emas: " + type);
        }

        return strategy;
    }
}

Service

@Service
public class PaymentService {

    private final PaymentStrategyResolver resolver;

    public PaymentService(PaymentStrategyResolver resolver) {
        this.resolver = resolver;
    }

    public void pay(PaymentType type, BigDecimal amount) {
        PaymentStrategy strategy = resolver.resolve(type);
        strategy.pay(amount);
    }
}

Mana shu real Middle-level yondashuv.


22. Design Patterns bo‘yicha interview savollar

Design pattern nima?

Ko‘p uchraydigan software design muammolariga tayyor, sinovdan o‘tgan yechim shakli.


Singleton nima?

Classdan bitta instance bo‘lishini ta’minlaydigan pattern.


Spring bean singletonmi?

Default holatda ha. Spring container ichida bitta bean instance yaratadi.


Factory va Strategy farqi?

Factory object/service yaratadi yoki tanlaydi.
Strategy esa tanlangan algoritm/behaviorni bajaradi.

Ko‘pincha ikkalasi birga ishlatiladi.


Adapter va Facade farqi?

Adapter mos kelmaydigan interface’ni moslashtiradi.
Facade murakkab subsystem uchun sodda kirish nuqtasi beradi.


Decorator va Proxy farqi?

Decorator yangi behavior qo‘shadi.
Proxy accessni nazorat qiladi yoki method chaqiruvini o‘rab turadi.


Strategy qachon ishlatiladi?

Bir nechta algoritm/variant bo‘lsa va runtime’da ulardan birini tanlash kerak bo‘lsa.


Observer qachon ishlatiladi?

Bir hodisa bo‘lganda bir nechta listener reaction qilishi kerak bo‘lsa.


Template Method va Strategy farqi?

Template Method inheritance asosida ishlaydi.
Strategy composition asosida ishlaydi.

Spring loyihalarda Strategy ko‘pincha moslashuvchanroq.


23. Java developer uchun minimal checklist

Design Patterns bo‘yicha quyidagilarni bilishing kerak:

  • Patternni yodlash emas, muammoni tanish;

  • Singleton va Spring singleton farqini bilish;

  • Factory bilan service tanlash;

  • Builder bilan object yaratish;

  • Adapter bilan tashqi API’ni o‘rash;

  • Decorator bilan behavior qo‘shish;

  • Proxy orqali Spring AOP ishlashini tushunish;

  • Facade bilan use case’ni tartiblash;

  • Strategy bilan if/elselarni kamaytirish;

  • Observer/Event bilan coupling kamaytirish;

  • Anti-patternlardan qochish.


Qisqa xulosa

Design Patterns - kodni "chiroyli" qilish uchun emas. Ular kodni:

o‘qiladigan
kengaytiriladigan
test qilinadigan
kam bog‘langan
productionga mos

qilish uchun kerak.

Java developer uchun eng ko‘p kerak bo‘ladigan patternlar:

Strategy
Factory
Builder
Adapter
Proxy
Facade
Observer
Decorator
Command