Java Factory Method Pattern: කර්මාන්තශාලා ක්රමය - SC Guide

හලෝ යාළුවනේ! කොහොමද ඉතින්? හොඳින් ඉන්නවා කියලා හිතනවා.
අද අපි කතා කරන්න යන්නේ Java Software Engineering වල ගොඩක්ම වැදගත් තැනක් ගන්න, “Design Patterns” කියන මාතෘකාව ගැන. විශේෂයෙන්ම, “Creational Design Patterns” ගණයට අයිති වෙන “Factory Method Design Pattern” එක ගැන තමයි අපි ගැඹුරින් බලන්නේ.
ඕනම software project එකක් ගද්දී, අපි objects හදන විදිය හරිම වැදගත්. පොඩි project එකක නම්, `new` keyword එක පාවිච්චි කරලා කෙලින්ම objects හදන එක ප්රශ්නයක් නෙවෙයි. හැබැයි project එක ලොකු වෙන්න, සංකීර්ණ වෙන්න වෙන්න, මේ object creation logic එක manage කරන එක හිතුවට වඩා අමාරු වෙන්න පුළුවන්. ඒ වගේ වෙලාවට තමයි මේ Design Patterns අපිට ජීවිතේට වටින්නේ!
හරි, එහෙනම් අපි බලමු මොකක්ද මේ Factory Method Pattern එක කියන්නේ කියලා, আর ඒක අපේ software development ජීවිතේ කොච්චර ලේසි කරනවද කියලා.
මොකක්ද මේ Factory Method Design Pattern එක?
සරලවම කිව්වොත්, Factory Method Pattern එක කියන්නේ object එකක් හදන වගකීම, ඒ object එක හදන්න ඕන class එකටම බාර දෙන ක්රමවේදයක්. ඒ කියන්නේ, object එකක් හදද්දී, අපි කෙලින්ම `new ConcreteProduct()` කියලා ලියන්නේ නෑ. ඒ වෙනුවට, අපි වෙනත් method එකක් හරහා (මේ method එකට තමයි "factory method" කියන්නේ) object එක හදන්න කියනවා. මේ method එක concrete class එකක් මගින් implement කරනවා.
මේක හරියට, අපි කර්මාන්ත ශාලාවකට ගිහින් "මට මේ වාහනය ඕන" කියලා කිව්වම, ඒ කර්මාන්ත ශාලාවෙන් ඒක හදලා දෙනවා වගේ වැඩක්. අපිට "මේක මෙහෙම හදන්න, මේ material දාලා හදන්න" කියලා දැනගන්න ඕන නෑ. අපිට ඕන දේ කිව්වම, එයාලා ඒක හදලා දෙනවා. Software වලදිත් එහෙමයි, අපිට අවශ්ය object එකේ Type එක කිව්වම, factory method එක ඒක හදලා දෙනවා. ඒක හදන විදිය ගැන (Instantiation Logic) අපිට හිතන්න ඕන නෑ.
මේක "Creational Design Pattern" ගණයට අයිති වෙනවා. ඒ කියන්නේ, systems වල flexibility එක සහ reusability එක වැඩි කරන්න, object creation process එක manage කරන්න මේ patterns පාවිච්චි කරනවා.
Factory Method Pattern එක ඇයි අපිට ඕන වෙන්නේ?
හරි, දැන් ඔයාලා හිතයි, "ඇයි මට කෙලින්ම `new Student()`, `new Teacher()` කියලා ලියන්න බැරි?" කියලා. ඒකට හේතු කීපයක් තියෙනවා. මේ Pattern එක අපිට උදව් කරනවා code එකේ Design Principles හරියටම follow කරන්න.
1. Loose Coupling
ඔබේ code එකේ හැම තැනම `new ConcreteProduct()` කියලා ලියනවා කියන්නේ, ඔබේ client code එක `ConcreteProduct` කියන class එකත් එක්ක ගොඩක් දුරට බද්ධ (tightly coupled) වෙලා කියන එක. හිතන්න, අනාගතයේදී `ConcreteProduct` කියන class එකේ constructor එක වෙනස් වුනොත් (උදා: අලුත් parameter එකක් එකතු වුනොත්), එතකොට ඔබේ code එකේ හැම තැනම ඒ වෙනස්කම් කරන්න වෙනවා. මේක project එකක් ලොකු වෙනකොට පට්ට headache එකක්! මේක Dependency Inversion Principle එකටත් පටහලයි.
Factory Method එකක් පාවිච්චි කරද්දී, client code එක දන්නේ `Product` interface එක විතරයි. ඒ කියන්නේ, `ConcreteProduct` එකක් හදන විදිය ගැන client code එකට කිසිම දැනුමක් නෑ. මේක "loose coupling" කියන concept එකට හොඳ උදාහරණයක්. එතකොට, අනාගතයේදී අලුත් Product Type එකක් ආවොත්, නැත්නම් තියෙන Product එකක හැදිල්ල වෙනස් වුනොත්, client code එකට ඒක බලපාන්නේ නෑ.
2. Open/Closed Principle
මේක Design Patterns වල හරිම වැදගත් Principle එකක්. "Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification." ඒ කියන්නේ, අපිට අලුත් functionality එකක් එකතු කරන්න පුළුවන් වෙන්න ඕන, හැබැයි දැනට තියෙන stable code එක වෙනස් නොකර. දැනට තියෙන code එක වෙනස් කරනවා කියන්නේ, regression bugs ඇති වෙන්න තියෙන අවධානම වැඩි වෙනවා කියන එක.
Factory Method එක මේ Principle එකට හොඳටම ගැලපෙනවා. ඔබට අලුත් Product වර්ගයක් හදන්න අවශ්ය වුනොත්, ඔබට කරන්න තියෙන්නේ අලුත් `ConcreteProduct` class එකක් හදලා, ඒකට අලුත් `ConcreteCreator` class එකක් හදන එක විතරයි. දැනට තියෙන code එක (Creator interface/abstract class එක) වෙනස් කරන්න ඕන වෙන්නේ නෑ. වැඩේ ගොඩක් ලේසියි!
3. Single Responsibility Principle
මේක කියන්නේ, "Each class or module should have only one reason to change." ඒ කියන්නේ, class එකක් හදලා තියෙන්නේ එකම වගකීමක් ඉටු කරන්න විතරයි. Factory Method Pattern එකේදී, object හදන වගකීම වෙනම class එකකට (`Creator`) දෙනවා. එතකොට, `Product` class එකේ වගකීම product එකේ functionality එක ගැන විතරයි. `Client` class එකේ වගකීම `Product` එක පාවිච්චි කරන එක විතරයි. මේක maintainability එක වැඩි කරන්න උදව් වෙනවා.
4. Runtime Flexibility
Factory Method එක පාවිච්චි කරද්දී, අපිට Product එකක් හදන්න ඕන වෙන්නේ runtime එකේදී. ඒ කියන්නේ, application එක run වෙන වෙලාවේදී අපිට අවශ්ය Product Type එක තෝරගන්න පුළුවන්. උදාහරණයක් විදියට, user input එකක් අනුව, නැත්නම් configuration එකක් අනුව වෙනස් Product objects හදන්න පුළුවන්. මේ flexibility එක complex systems වලදී ගොඩක් වටිනවා.
කෝඩ් එකෙන් බලමු (Let's look at the Code)
හරි, අපි දැන් පොඩි Java example එකකින් බලමු Factory Method Pattern එක implement කරන්නේ කොහොමද කියලා. අපි හිතමු, අපි Vehicle එකක් හදන Factory එකක් හදනවා කියලා. අපිට `Bike` සහ `Car` වගේ vehicles හදන්න පුළුවන් වෙන්න ඕන.
1. Product Interface
මුලින්ම, අපිට Product එකක් ඕන. මේ Product එක තමයි අපේ vehicles. ඒකට අපි interface එකක් හදමු.
// Product Interface
interface Vehicle {
void drive();
}
2. Concrete Products
දැන් අපිට මේ Product Interface එක implement කරන Concrete Products ඕන. `Bike` සහ `Car` කියන්නේ Concrete Products.
// Concrete Products
class Bike implements Vehicle {
@Override
public void drive() {
System.out.println("Riding a Bike. Vroom vroom!");
}
}
class Car implements Vehicle {
@Override
public void drive() {
System.out.println("Driving a Car. Honk honk!");
}
}
3. Creator Abstract Class
ඊළඟට, අපිට Creator එකක් ඕන. මේක abstract class එකක් වෙන්න පුළුවන්. මේකේ තමයි factory method එක තියෙන්නේ. මේ abstract class එකේ business logic methods තියෙන්නත් පුළුවන්, ඒවා concrete products එක්ක වැඩ කරනවා.
// Creator Abstract Class
abstract class VehicleFactory {
// The factory method (abstract)
public abstract Vehicle createVehicle();
// Other business logic methods can go here, which use the created product
public void deliverVehicle() {
Vehicle vehicle = createVehicle(); // Calling the factory method
System.out.println("Vehicle created and ready for delivery!");
vehicle.drive(); // Using the created vehicle
}
}
මෙහිදී, `createVehicle()` කියන්නේ අපේ Factory Method එක. මේක abstract නිසා, මේක implement කරන concrete classes වලට තමන්ට අවශ්ය Product එක හදන්න පුළුවන්. `deliverVehicle()` method එක factory method එක පාවිච්චි කරලා, concrete Product එකක් හදලා, ඒක පාවිච්චි කරන ආකාරය පෙන්නනවා. මේකෙන් Creator class එකේ business logic එක Product creation logic එකෙන් වෙන් වෙන හැටි පැහැදිලියි.
4. Concrete Creators
දැන් අපි මේ Creator එක implement කරන Concrete Creators හදමු. මේවා තමයි අපේ specific factories.
// Concrete Creators
class BikeFactory extends VehicleFactory {
@Override
public Vehicle createVehicle() {
return new Bike();
}
}
class CarFactory extends VehicleFactory {
@Override
public Vehicle createVehicle() {
return new Car();
}
}
5. Client Code
අන්තිමට, අපි මේවා පාවිච්චි කරන Client Code එක බලමු. මෙතනදී අපි කෙලින්ම `new Bike()` හෝ `new Car()` කියලා ලියන්නේ නෑ. අපි Factory එකෙන් Product එක ඉල්ලනවා.
// Client Code
public class FactoryMethodDemo {
public static void main(String[] args) {
System.out.println("--- Creating a Bike ---");
VehicleFactory bikeFactory = new BikeFactory();
// Using the factory method directly
Vehicle bike = bikeFactory.createVehicle();
bike.drive();
// Or using the template method in the creator
System.out.println("--- Delivering a Bike ---");
bikeFactory.deliverVehicle();
System.out.println("\n--- Creating a Car ---");
VehicleFactory carFactory = new CarFactory();
// Using the factory method directly
Vehicle car = carFactory.createVehicle();
car.drive();
// Or using the template method in the creator
System.out.println("--- Delivering a Car ---");
carFactory.deliverVehicle();
// Imagine a scenario where vehicle type is determined at runtime
System.out.println("\n--- Runtime Vehicle Selection ---");
String vehicleType = "car"; // This could come from user input, config, etc.
VehicleFactory dynamicFactory;
if ("bike".equalsIgnoreCase(vehicleType)) {
dynamicFactory = new BikeFactory();
} else if ("car".equalsIgnoreCase(vehicleType)) {
dynamicFactory = new CarFactory();
} else {
// Default or error handling
System.out.println("Unknown vehicle type: " + vehicleType);
return;
}
dynamicFactory.deliverVehicle();
}
}
දැන් බලන්න, `FactoryMethodDemo` class එක දන්නේ `VehicleFactory` සහ `Vehicle` interfaces ගැන විතරයි. ඒක දන්නේ නෑ `Bike` එකක් හරි `Car` එකක් හරි හදන්නේ කොහොමද කියලා. ඒ වගකීම `BikeFactory` සහ `CarFactory` වලට දීලා තියෙන්නේ. අන්න ඒක තමයි Factory Method Pattern එකේ Magic එක! අන්තිම උදාහරණයෙන් පේනවා runtime එකේදී අපිට අවශ්ය factory එක තෝරගෙන, object එක හදන්න පුළුවන් හැටි.
අනාගතයේදී අපිට අලුත් vehicle වර්ගයක් (උදා: `Bus`) එකතු කරන්න ඕන වුනොත්, අපිට කරන්න තියෙන්නේ `Bus` class එකක් හදලා, `BusFactory` එකක් හදන එක විතරයි. `FactoryMethodDemo` class එකේ කිසිම වෙනසක් කරන්න ඕන වෙන්නේ නෑ. නියමයි නේද?
වාසි සහ අවාසි (Pros and Cons)
ඕනම Design Pattern එකක වගේ, Factory Method එකෙත් වාසි සහ අවාසි දෙකම තියෙනවා.
වාසි (Pros):
- Decoupling: Client code එක concrete classes එක්ක tight coupling එකක් ඇති කරගන්නේ නෑ. ඒක Product interface එකත් එක්ක විතරයි කතා කරන්නේ. මේකෙන් systems වල maintainability සහ flexibility වැඩි වෙනවා.
- Extensibility: අලුත් Product වර්ග පහසුවෙන් එකතු කරන්න පුළුවන්, දැනට තියෙන code එක වෙනස් නොකර. (Open/Closed Principle). මේක විශාල applications වලදී අලුත් features එකතු කිරීම පහසු කරනවා.
- Maintainability: Object creation logic එක එක තැනකට වෙන කරන නිසා, ඒක වෙනස් කරන්න ඕන වුනොත්, එක තැනක වෙනස්කම් කරලා ඇති. මේකෙන් code base එක clean සහ organized විදියට තියාගන්න පුළුවන්.
- Testability: Object creation logic එක decouple කරන නිසා, unit testing කරන්න පහසු වෙනවා. Mock objects පාවිච්චි කරලා, creation process එක simulate කරන්න පුළුවන්.
- Runtime Flexibility: Application එක run වෙන වෙලාවේදී අවශ්ය Product එක මොකක්ද කියලා තීරණය කරලා, ඒ අනුව object එක හදන්න පුළුවන්.
අවාසි (Cons):
- Increased Complexity: පොඩි project එකකට මේක ටිකක් overkill වෙන්න පුළුවන්. Object create කරන්න classes වැඩි වෙනවා (Creator interface/abstract class, Concrete Creator classes, Product interface, Concrete Product classes). මේක Indirection level එක වැඩි කරනවා.
- Initial Overhead: පටන් ගන්නකොට classes වැඩිපුර හදන්න වෙනවා. Simple cases වලදී `new` keyword එක පාවිච්චි කරන එක වඩා සරලයි.
හැබැයි, project එකේ scalability, flexibility සහ maintainability එක ගැන හිතද්දී, මේ pattern එකෙන් ලැබෙන වාසි අවාසි වලට වඩා ගොඩක් වැඩියි.
අවසාන වශයෙන් (In Conclusion)
Factory Method Design Pattern එක කියන්නේ Java Software Engineering වලදී Object Creation එක manage කරන්න තියෙන හරිම බලගතු tool එකක්. මේක අපිට code එක clean, extensible, සහ maintainable විදියට තියාගන්න උදව් වෙනවා. මුලින් ටිකක් විතර සංකීර්ණයි වගේ දැනුනට, තේරුම් ගත්තම මේකෙන් ලැබෙන වාසි හරිම ලොකුයි.
ඔබේ project එක ලොකු වෙනකොට, අලුත් Product Types නිතරම එකතු කරන්න වෙනවා නම්, නැත්නම් runtime එකේදී Product type එක තීරණය කරන්න ඕන නම්, Factory Method Pattern එක ඔබට කදිම විසඳුමක් වෙයි. මේක `Abstract Factory` වගේ patterns වලටත් මුලික පදනම සපයනවා.
අනිවාර්යයෙන්ම මේ concept එක ඔයාලගේ project වලට apply කරලා බලන්න. තවදුරටත් මේ ගැන ප්රශ්න තියෙනවා නම්, පහලින් comment එකක් දාන්න. අපි ඒ ගැන කතා කරමු. නැත්නම්, මේ වගේ වෙන Design Pattern එකක් ගැන දැනගන්න ඕන නම්, ඒකත් comment කරන්න.
එහෙනම්, තවත් අලුත් Design Pattern එකක් ගැන කතා කරන්න හම්බවෙමු! සුභ දවසක්!