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
CardAgar 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‘yichaMasalan:
PDF report
Excel report
CSV reportFactory 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 yaratildiDecorator 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 ServiceProxy 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
@PreAuthorizeMasalan:
@Transactional
public void createOrder() {
// transaction ichida ishlaydi
}Spring bu method atrofida proxy yaratadi:
start transaction
methodni chaqir
commit yoki rollbackProxy 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 yuboriladiAgar 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 analyticsOrderService 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
SendNotificationCommandCommand 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 classes16. 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. saqlashAbstract 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
DiscountService19.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
UserDeleteStrategyAgar 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, CashHa 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
@AsyncBu joyda Proxy konsepti bor.
21. Katta real misol: Payment tizim
Talab
Order yaratilganda user quyidagi to‘lov usullaridan birini tanlaydi:
PAYME
CLICK
UZUM
CASHYomon 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 mosqilish uchun kerak.
Java developer uchun eng ko‘p kerak bo‘ladigan patternlar:
Strategy
Factory
Builder
Adapter
Proxy
Facade
Observer
Decorator
Command