Observer Design Pattern සිංහලෙන් | Spring @EventListener | Software Engineering SC Guide

ආයුබෝවන් ඈ... කොහොමද ඉතින් ඔයාලට? Tech ලෝකේ අලුත්ම දේවල් හොයන, තමන්ගේ coding skills ටික sharpen කරගන්න ලෑස්ති වෙලා ඉන්න ඔයාලා හැමෝම අපේ අලුත් Ghost post එකට සාදරයෙන් පිළිගන්නවා! අද අපි කතා කරන්න යන්නේ software design වල අත්යවශ්යම concepts එකක් ගැන – ඒ තමයි Design Patterns. මේවා software development වලදී අපිට හමුවෙන පොදු ගැටළු වලට දීර්ඝ කාලීනව පවතින, නැවත නැවත භාවිතා කළ හැකි විසඳුම් සැපයීම සඳහා නිර්මාණය කර ඇති, ඔප්පු කර ඇති blueprints වගෙයි. මේ අතරින් විශේෂයෙන්ම අපි අද බලමු Observer Design Pattern එක මොකක්ද කියලා, ඒක අපේ applications වලට කොහොමද ප්රයෝජනවත් වෙන්නේ කියලා, ඒ වගේම Spring Framework එකේ @EventListener annotation එක පාවිච්චි කරලා මේ powerful pattern එක implement කරන්නේ කොහොමද කියලා. ඉතින්, කම්මැලිකමක් නැතුව අපි coding වලට බහිමුද?
Design Patterns කියන්නේ මොනවද?
ඉස්සෙල්ලම අපි බලමු මේ Design Patterns කියන්නේ මොනවද කියලා. හිතන්නකෝ ඔයාලා ගෙයක් හදනවා කියලා. ඉතින් Architect කෙනෙක් ඒ ගේ හදන්න කලින් drawings හදලා, plan එකක් දෙනවා නේද? ඒ plan එකේ තියෙනවා මේ ගේ කොහොමද පාවිච්චි වෙන්නේ, කාමර කොහෙද තියෙන්නේ, ඒවට යන පාරවල් කොහොමද, light කොහෙන්ද, windows කොහෙද වගේ හැමදේම. මේ standard plans පාවිච්චි කරන නිසා Architect ට නැවත නැවත මුල ඉඳන් හදන්න ඕන නෑ. ඒ වගේ තමයි software development වලදීත් අපි common, නැවත නැවත එන problems වලට solutions හොයනකොට ඒ solutions වලට standard blueprints තියෙනවා. මේවා තමයි Design Patterns කියලා කියන්නේ. මේවා "Gang of Four" (GoF) පොතෙන් (Design Patterns: Elements of Reusable Object-Oriented Software) ජනප්රිය වුණා. මේ Patterns, Creational, Structural සහ Behavioral කියන ප්රධාන කාණ්ඩ තුනකට බෙදෙනවා. අද අපි කතා කරන Observer Pattern එක අයිති වෙන්නේ Behavioral Patterns කියන කාණ්ඩයටයි. මේවා අපිට clean, maintainable, flexible, scalable, සහ reusable code ලියන්න උදව් වෙනවා. ඒ වගේම මේවා software design ගැන කතා කරන්න පොදු භාෂාවක් (common vocabulary) සපයනවා, එක නිසා team එකක වැඩ කරනකොටත් ගොඩක් පහසුවක් වෙනවා.
Observer Design Pattern එක මොකක්ද?
හරි, දැන් අපි එමු අපේ අද මාතෘකාවට – Observer Design Pattern එකට. මේක behavioral pattern එකක්. Behavioral කියන්නේ objects අතර interactions සහ responsibilities distribute කරන විදිය ගැන කතා කරන නිසා. සරලවම කිව්වොත්, මේ pattern එකෙන් වෙන්නේ object එකක state එක වෙනස් වෙනකොට, ඒකට depend වෙන අනිත් object ටිකට automatically notify කරන එක. මේක one-to-many dependency එකක් maintain කරනවා. මෙතන ප්රධාන entities දෙකක් ඉන්නවා:
- Subject (Publisher): මේ තමයි state එක handle කරන්නේ. මේකේ state එක වෙනස් වෙනකොට, ඒ වෙනස ගැන දැනුවත් වෙන්න ඕන අයට notify කරනවා. Subject එකට තමන්ගේ listenersලා කවුද කියලා හරියටම දැනගන්න අවශ්ය නෑ. ඒගොල්ලෝ event එකක් publish කරනවා විතරයි.
- Observer (Subscriber): මේවා තමයි Subject එකේ state changes ගැන දැනුවත් වෙන්න කැමති objects. Subject එක notify කරාම, මේ අය අලුත් information එක අනුව update වෙනවා, නැත්නම් තමන්ගේ අවශ්යතාවය අනුව ක්රියා කරනවා. Observerලා ඕන තරම් ඉන්න පුළුවන්.
උදාහරණයක් විදියට ගත්තොත්, පත්තර කන්තෝරුවක් (Subject/Publisher) ගැන හිතන්න. ඒගොල්ලෝ අලුත් පත්තරයක් ගහනකොට, ඒ පත්තරය ඕන කියලා කියන අයට (Observers/Subscribers) ඒක ලැබෙනවා නේද? එතකොට පත්තර කන්තෝරුවට හැම පාරම අලුත් පත්තරයක් ගහනකොට ඒකට subscribe වෙලා ඉන්න හැම කෙනෙක්ටම කතා කරලා කියන්න ඕන නෑ. ඒගොල්ලෝ publish කරපු ගමන් අනිත් අයට ඒක ලැබෙනවා. ඒ වගේම තමයි අපි YouTube channel එකකට Subscribe වුණාම, අලුත් video එකක් දැම්ම ගමන් අපිට notification එකක් එන්නේ. එතකොට YouTube channel එක තමයි Subject එක, අපි තමයි Observers. Subject එකට ඕන තරම් Observersලා ඉන්න පුළුවන්. තවත් උදාහරණයක් තමයි share market එක. Stock prices (Subject) වෙනස් වෙනකොට, ඒ changes ගැන alert වෙන්න ඕන investor apps (Observers) වලට notification යනවා. මේක නිසා Subject එකට තමන්ගේ core logic එක විතරක් කරන්න පුළුවන්, notification යවන එක වෙනම modules වලට බාර දෙන්න පුළුවන්.
ඇයි Observer Pattern එක පාවිච්චි කරන්නේ?
මේ pattern එක පාවිච්චි කරන එකෙන් අපිට ගොඩක් වාසි තියෙනවා:
- Loose Coupling: Subject එකයි Observersලයි අතර තියෙන්නේ loose coupling එකක්. මේක තමයි මේ pattern එකේ ලොකුම වාසියක්. Coupling කියන්නේ modules දෙකක් කොච්චර දුරට එකිනෙකා මත depend වෙනවද කියන එක. High coupling කියන්නේ modules දෙකක් එකට තදින් බැඳිලා ඉන්නවා කියන එක, ඒ කියන්නේ එකක වෙනසක් වුණොත් අනිත් එකටත් බලපානවා. Loose coupling කියන්නේ ඒක අඩු වීම. Observer pattern එකේදී Subject එකට තමන්ගේ Observersලව හරියටම දැනගන්න අවශ්ය නෑ. එයාලට ඕන වෙන්නේ "මාව subscribe වෙලා ඉන්න අයට notify කරන්න" කියන එක විතරයි. Observersලටත් Subject එකේ internal details ඕන නෑ. මේකෙන් code එක maintainability වැඩි වෙනවා, මොකද module එකක වෙනසක් කළාම අනිත් modules වලට බලපාන එක අඩු වෙනවා.
- Scalability and Extensibility: අපිට පහසුවෙන් අලුත් Observersලා add කරන්න පුළුවන්, නැත්නම් remove කරන්න පුළුවන්. ඒකට Subject එකේ code එක වෙනස් කරන්න ඕන වෙන්නේ නෑ. හිතන්නකෝ, අලුත් order එකක් ආවාම දැන් email එකක් සහ inventory update එකක් යවනවා. හෙට sms එකකුත් යවන්න ඕන වුණොත්, අපිට අලුත් SMSNotificationService එකක් හදලා @EventListener දාලා publish කරපු event එකට listen කරන්න විතරයි තියෙන්නේ. Subject එකේ OrderService එකේ එකම line එකක්වත් වෙනස් කරන්න ඕන නෑ.
- Reusability: Subject එකයි Observer එකයි වෙන වෙනම develop කරන්න පුළුවන් නිසා, ඒගොල්ලන්ව වෙනත් තැන්වල නැවත පාවිච්චි කරන්න (reuse) පුළුවන්. උදාහරණයක් විදියට, OrderCreatedEvent එක වෙනත් service එකක publish කරනවා නම්, අපේ EmailNotificationService එකට ඒකටත් listen කරන්න පුළුවන්.
- Testability: Modules වෙන වෙනම තියෙන නිසා, ඒවට unit tests ලියන එක පහසු වෙනවා.
Observer Pattern එක Implement කරන්නේ කොහොමද? (@EventListener සමඟ)
Traditional Observer Pattern implementation එකේදී අපි Subject interface එකක්, Observer interface එකක් හදලා registerObserver(), removeObserver(), notifyObservers() වගේ methods manual පාවිච්චි කරනවා. හැබැයි අද කාලේ modern frameworks වලදී මේක තව පහසු විදියට කරන්න පුළුවන්. Spring Framework එකේ @EventListener annotation එක මේකට හොඳම උදාහරණයක්. Spring Framework එකේ Event Publishing mechanism එක කියන්නේ Observer pattern එකේ sophisticated implementation එකක්. අපි මේක පියවරෙන් පියවර බලමු.
1. Event Class එක
මුලින්ම අපිට ඕන notify කරන්න යන "event" එකක්. මේක තමයි Observersලාට ලැබෙන information එක. මේක සාමාන්ය Java class එකක් විදියට හදන්න පුළුවන්. මේකට Event කියලම නමක් දෙන්න ඕන නෑ, මොකද Spring එකේදී method signature එකෙන් තමයි event type එක identify කරන්නේ. උදාහරණයක් විදියට, අලුත් order එකක් ආවාම notify කරන event එකක් හදමු.
// OrderCreatedEvent.java
// මේක තමයි අපේ "event payload" එක - listenersලාට ලැබෙන තොරතුරු
public class OrderCreatedEvent {
private String orderId;
private double totalAmount;
public OrderCreatedEvent(String orderId, double totalAmount) {
this.orderId = orderId;
this.totalAmount = totalAmount;
}
public String getOrderId() {
return orderId;
}
public double getTotalAmount() {
return totalAmount;
}
@Override
public String toString() {
return "OrderCreatedEvent{" +
"orderId='" + orderId + '\'' +
", totalAmount=" + totalAmount +
'}';
}
}
2. Event Publisher (Subject)
ඊළඟට අපිට ඕන Event එක publish කරන Subject එක. Spring වලදී ApplicationEventPublisher කියන interface එක පාවිච්චි කරලා event publish කරන්න පුළුවන්. මේක Spring IoC container එකෙන් auto-wired කරන්න පුළුවන්. Spring application එකක් run වෙනකොටම ApplicationContext එකක් create වෙනවා, මේ ApplicationContext එක ApplicationEventPublisher interface එක implement කරනවා. ඒ නිසා අපිට මේක direct inject කරලා පාවිච්චි කරන්න පුළුවන්.
// OrderService.java (This acts as our Subject/Publisher)
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
@Service // Spring component එකක් විදියට register කරනවා
public class OrderService {
private final ApplicationEventPublisher eventPublisher; // Event publisher එක inject කරනවා
public OrderService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void createOrder(String orderId, double amount) {
System.out.println("Processing order: " + orderId + " with amount: " + amount);
// Order එක create කරන්න අවශ්ය business logic මෙතනට එනවා...
// Database transactions, validations, etc.
// Order එක successfully created වුණාට පස්සේ event එකක් publish කරනවා
OrderCreatedEvent event = new OrderCreatedEvent(orderId, amount);
eventPublisher.publishEvent(event); // Event එක publish කරනවා
System.out.println("Order creation event published for order: " + orderId);
}
// Other order related methods go here...
}
උඩ කෝඩ් එකේ OrderService එක තමයි අපේ Subject එක. ඒක createOrder method එක ඇතුලේ OrderCreatedEvent එකක් publish කරනවා. මේකෙන් OrderService එකට තමන්ගේ core responsibility (order creation) එකට විතරක් අවධානය යොමු කරන්න පුළුවන්. notifications, inventory updates වගේ දේවල් වෙනත් modules වලට බාර දෙනවා.
3. Event Listeners (Observers)
අන්තිමට අපිට ඕන මේ publish කරන events වලට listen කරන Observersලා ටික. Spring වලදී මේක කරන්න පුළුවන් @EventListener annotation එක පාවිච්චි කරලා. මේක ඕනෑම Spring managed component එකක (e.g., @Component, @Service, @Repository, @Controller) method එකක් උඩට දාන්න පුළුවන්. Spring framework එක run වෙනකොට මේ @EventListener annotation එක තියෙන methods detect කරලා, ඒ methods වල parameter type එකට ගැලපෙන event එකක් publish වුණාම automatically ඒ method එක invoke කරනවා.
// EmailNotificationService.java (An Observer)
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component // Spring component එකක් විදියට register කරනවා
public class EmailNotificationService {
@EventListener // OrderCreatedEvent එකකට listen කරනවා
public void handleOrderCreatedEvent(OrderCreatedEvent event) {
System.out.println("EmailNotificationService: Sending email for new order: " + event.getOrderId() +
" with total: " + event.getTotalAmount());
// Email යවන්න අවශ්ය actual logic මෙතනට එනවා
// e.g., using JavaMailSender to send emails
}
}
// InventoryService.java (Another Observer)
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component // Spring component එකක් විදියට register කරනවා
public class InventoryService {
@EventListener // OrderCreatedEvent එකකට listen කරනවා
public void handleOrderCreatedEvent(OrderCreatedEvent event) {
System.out.println("InventoryService: Updating inventory for order: " + event.getOrderId() +
". Deducting items for amount: " + event.getTotalAmount());
// Inventory update කරන්න අවශ්ය actual logic මෙතනට එනවා
// e.g., calling a database service to decrement stock
}
}
මේ service classes දෙකේම තියෙන @EventListener annotation එක නිසා, OrderCreatedEvent එකක් publish වුණාම, Spring Framework එක automatically මේ methods දෙකම execute කරනවා. මේකෙන් වෙන්නේ OrderService එකට තමන්ගේ තොරතුරු වෙනත් Services වලට යවන එක ගැන දැනගන්න අවශ්ය නැති වීම. OrderService එක event එක publish කරනවා විතරයි, ඒ event එකට listen කරන හැම කෙනෙක්ම තමන්ගේ වැඩේ කරනවා. නියමයි නේද? මේක synchronous behaviour එකක් (එකම thread එකේ සිද්ධ වෙනවා), ඒත් අවශ්ය නම් @Async annotation එක පාවිච්චි කරලා asynchronous behaviour එකකුත් add කරන්න පුළුවන්, ඒක ගැන අපි තව දවසක කතා කරමු.
මෙහෙම කරලා වැඩේ බලමු:
අපි මේක Test කරන්න Main Application class එකක් හදමු. මේක Spring Boot application එකක් විදියට run කරන්න පුළුවන්.
// SpringObserverPatternApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class SpringObserverPatternApplication {
public static void main(String[] args) {
// Spring Application Context එක load කරනවා
ConfigurableApplicationContext context = SpringApplication.run(SpringObserverPatternApplication.class, args);
// OrderService bean එක context එකෙන් ගන්නවා
OrderService orderService = context.getBean(OrderService.class);
System.out.println("\n--- Simulating Order Creation (Order 1) ---");
orderService.createOrder("ORDER-001", 150.75);
System.out.println("\n--- Order Creation Simulation Complete for Order 1 ---\n");
System.out.println("\n--- Simulating Order Creation (Order 2) ---");
orderService.createOrder("ORDER-002", 200.00);
System.out.println("\n--- Second Order Creation Simulation Complete for Order 2 ---\n");
// Application context එක close කරනවා
context.close();
}
}
මේ application එක run කරලා බලන්න. Output එකේ මෙහෙම දෙයක් දැකගන්න පුළුවන් වෙයි:
--- Simulating Order Creation (Order 1) ---
Processing order: ORDER-001 with amount: 150.75
Order creation event published for order: ORDER-001
EmailNotificationService: Sending email for new order: ORDER-001 with total: 150.75
InventoryService: Updating inventory for order: ORDER-001. Deducting items for amount: 150.75
--- Order Creation Simulation Complete for Order 1 ---
--- Simulating Order Creation (Order 2) ---
Processing order: ORDER-002 with amount: 200.0
Order creation event published for order: ORDER-002
EmailNotificationService: Sending email for new order: ORDER-002 with total: 200.0
InventoryService: Updating inventory for order: ORDER-002. Deducting items for amount: 200.0
--- Second Order Creation Simulation Complete for Order 2 ---
බලන්න, OrderService එකට අවශ්ය වුණේ OrderCreatedEvent එක publish කරන්න විතරයි. ඒ publish කරපු event එකට EmailNotificationService එකයි InventoryService එකයි listen කරලා තමන්ගේ වැඩේ කරගෙන ගියා. මේක තමයි Observer Pattern එකේ power එක! Loose coupling, extensibility නියමෙටම පේනවා නේද? OrderService එක කිසිම වෙලාවක EmailNotificationService එක ගැනවත් InventoryService එක ගැනවත් දැනගත්තේ නෑ. ඒගොල්ලෝ අතර තියෙන්නේ event එකක් හරහා communicate කරන agreement එකක් විතරයි. මේකෙන් System එකේ maintainability සහ future changes වලට adapt වීමේ හැකියාව විශාල ලෙස වැඩි වෙනවා.
ප්රායෝගික භාවිතයන් (Real-world Use Cases)
මේ Observer Pattern එක software development වල ගොඩක් තැන් වල පාවිච්චි වෙනවා. මේවායින් කීපයක් තමයි:
- UI Events (Event Handling): web applications (JavaScript), desktop applications (Java Swing, AWT) වල Button click, text change, mouse events වගේ UI events handle කරන්න බහුලවම මේ pattern එක පාවිච්චි කරනවා. Usersලා කරන actions වලට React කරන එක මේකෙන් පහසු වෙනවා.
- Notifications and Alerts: Social media platforms වල අලුත් post එකක් දැම්මම followersලාට notification යවන එක, email alerts, SMS alerts වගේ පද්ධති වල. කාලගුණ වාර්තා, stock price alerts වගේ දේවලුත් මේකට උදාහරණ.
- Asynchronous Processing: Heavy tasks, background tasks වලදී. උදාහරණයක් විදියට, order එකක් place කළාම user ට confirm කරලා, inventory update, invoicing, shipping label generation වගේ දේවල් background එකේ separate process විදියට run කරන්න මේ pattern එක පාවිච්චි කරන්න පුළුවන්.
- MVC Architecture: Model-View-Controller (MVC) architecture එකේ Model (data) එකේ වෙනස්කම් View (UI) එකට notify කරන එක. Model එක වෙනස් වුණාම ඒක View එකට කියනවා update වෙන්න කියලා.
- Microservices Communication: Event-driven microservices architecture වලදී service එකකින් වෙනත් service එකකට information යවන්න. service එකක් event එකක් publish කරනවා, ඒ event එකට කැමති අනිත් servicesලා listen කරනවා.
ඉතින්, මොකද හිතන්නේ?
අද අපි Observer Design Pattern එක ගැන හොඳට කතා කරා. ඒ වගේම Spring Framework එකේ @EventListener annotation එක පාවිච්චි කරලා ඒක කොහොමද පහසුවෙන් implement කරන්නේ කියලත් බැලුවා. මේ pattern එකෙන් අපේ applications වල maintainability, extensibility, scalability ගොඩක් වැඩි වෙනවා. මේ වගේ Design Patterns තේරුම් ගන්න එකෙන් ඔයාලගේ coding skills වගේම problem-solving ability එකත් ගොඩක් දියුණු වෙනවා. මේවා practical projects වලට apply කරන්න පටන් ගත්තම ඔයාලට දැනෙයි code quality එක කොච්චර වැඩි වෙනවද කියලා.
මේ concept එක ගැන ඔයාලගේ අදහස් මොනවද? ඔයාලා Observer Pattern එක පාවිච්චි කරලා තියෙනවද? තියෙනවා නම් මොන වගේ scenario වලදීද? පහළ comment section එකේ ඔයාලගේ අදහස් බෙදා හදා ගන්න අමතක කරන්න එපා. ඔයාලට මේ post එකෙන් මොනවහරි ප්රශ්නයක් තියෙනවා නම් ඒකත් අහන්න පුළුවන්. ඒ වගේම මේ post එකෙන් ඔයාලට අලුත් දෙයක් ඉගෙන ගන්න ලැබුණා නම් යාළුවොත් එක්ක share කරන්නත් අමතක කරන්න එපා!
ඊළඟ post එකෙන් හමුවෙමු, හැමෝටම ජය!