Τι είναι τα Design Patterns και γιατί υπάρχουν
Τα design patterns δεν είναι “μαγικές συνταγές”. Είναι δοκιμασμένες λύσεις σε επαναλαμβανόμενα προβλήματα σχεδιασμού λογισμικού. Προέκυψαν γιατί οι προγραμματιστές αντιμετωπίζουν ξανά και ξανά τα ίδια αρχιτεκτονικά διλήμματα: ευελιξία, επεκτασιμότητα, καθαρός διαχωρισμός ευθυνών.
Το βασικό τους όφελος:
- Μειώνουν την πολυπλοκότητα
- Αυξάνουν τη συντηρησιμότητα
- Δημιουργούν κοινή “γλώσσα” μεταξύ developers
Το Strategy Pattern ανήκει στα behavioral patterns και ασχολείται με το πώς αλλάζει η συμπεριφορά ενός αντικειμένου δυναμικά.
Τι είναι το Strategy Pattern
Το Strategy Pattern επιτρέπει να ορίζεις μια οικογένεια αλγορίθμων, να τους ενθυλακώνεις (encapsulate) και να τους κάνεις εναλλάξιμους.
Με απλά λόγια:
Αν έχεις πολλές διαφορετικές “στρατηγικές” για να κάνεις το ίδιο πράγμα, δεν γράφεις if/else παντού αλλά τις απομονώνεις.
Το πρόβλημα που λύνει
Φαντάσου αυτό:
if(type == "credit")
PayWithCredit();
else if(type == "paypal")
PayWithPaypal();
else if(type == "crypto")
PayWithCrypto();
Προβλήματα:
- Παραβίαση του Open/Closed Principle
- Δύσκολη συντήρηση
- Δύσκολο testing
- Κακή επεκτασιμότητα
Η ιδέα του Strategy Pattern
Αντί να έχεις conditional logic, κάνεις:
“Δίνω” στο αντικείμενο μια στρατηγική και αυτό τη χρησιμοποιεί.
Δομή
Το pattern έχει 3 βασικά μέρη:
- Strategy Interface
- Concrete Strategies
- Context
Παράδειγμα σε C#
- Strategy Interface
public interface IPaymentStrategy
{
void Pay(decimal amount);
}
- Concrete Strategies
public class CreditCardPayment : IPaymentStrategy
{
public void Pay(decimal amount)
{
Console.WriteLine($"Paid {amount} with Credit Card");
}
}
public class PaypalPayment : IPaymentStrategy
{
public void Pay(decimal amount)
{
Console.WriteLine($"Paid {amount} with PayPal");
}
}
- Context
public class PaymentContext
{
private IPaymentStrategy _strategy;
public PaymentContext(IPaymentStrategy strategy)
{
_strategy = strategy;
}
public void SetStrategy(IPaymentStrategy strategy)
{
_strategy = strategy;
}
public void ExecutePayment(decimal amount)
{
_strategy.Pay(amount);
}
}
- Χρήση
var context = new PaymentContext(new CreditCardPayment());
context.ExecutePayment(100);
context.SetStrategy(new PaypalPayment());
context.ExecutePayment(200);
Γιατί να το χρησιμοποιήσεις
- Καθαρός Κώδικας
Δεν έχεις if/else παντού.
- Open/Closed Principle
Προσθέτεις νέα στρατηγική χωρίς να αλλάξεις υπάρχοντα κώδικα.
- Testability
Κάθε στρατηγική μπορεί να γίνει unit test ανεξάρτητα.
- Επαναχρησιμοποίηση
Οι στρατηγικές είναι plug-and-play.
- Runtime ευελιξία
Αλλάζεις συμπεριφορά δυναμικά.
Πότε να το χρησιμοποιήσεις
✔ Όταν έχεις πολλές παραλλαγές μιας λειτουργίας
✔ Όταν βλέπεις πολλά if/else ή switch
✔ Όταν θέλεις να αλλάζεις behavior στο runtime
✔ Όταν θέλεις καθαρή αρχιτεκτονική
Πότε ΔΕΝ χρειάζεται
❌ Όταν έχεις 1-2 απλές επιλογές
❌ Όταν προσθέτει άσκοπη πολυπλοκότητα
❌ Όταν δεν υπάρχει πιθανότητα επέκτασης
Real-world παραδείγματα
- Payment systems (PayPal, Stripe, Crypto)
- Compression algorithms (zip, rar)
- Sorting strategies
- Authentication methods
Strategy Pattern σε συνδυασμό με Dependency Injection
Γιατί να το συνδυάσεις με DI
Το Strategy Pattern από μόνο του λύνει το πρόβλημα των if/else.
Αλλά αν κάνεις αυτό:
var context = new PaymentContext(new CreditCardPayment());
έχεις ακόμα ένα πρόβλημα:
Ο κώδικας σου γνωρίζει τις υλοποιήσεις (concrete classes)
Αυτό σημαίνει:
- Tight coupling
- Δύσκολο testing (χωρίς mocking εύκολα)
- Παραβίαση του Dependency Inversion Principle
Τι σου δίνει το Dependency Injection
Με DI:
- Δεν δημιουργείς αντικείμενα στα δίνει το framework
- Δουλεύεις με interfaces
- Μπορείς να αλλάξεις behavior χωρίς να πειράξεις business logic
Με απλά λόγια:
- Το Strategy Pattern βρίσκει πώς να αλλάξει behavior
- Το DI αποφασίζει ποιο behavior θα χρησιμοποιηθεί
Παράδειγμα σε ASP.NET Core
var builder = WebApplication.CreateBuilder(args);
// Register strategies
builder.Services.AddScoped<IPaymentStrategy, CreditCardPayment>();
builder.Services.AddScoped<IPaymentStrategy, PaypalPayment>();
// Register context & service
builder.Services.AddScoped<PaymentContext>();
builder.Services.AddScoped<PaymentService>();
var app = builder.Build();
Service που χρησιμοποιεί το Strategy Pattern
public class PaymentService
{
private readonly PaymentContext _context;
public PaymentService(PaymentContext context)
{
_context = context;
}
public void Pay(string type, decimal amount)
{
_context.ExecutePayment(type, amount);
}
}
Παράδειγμα χρήσης (π.χ. σε Controller ή minimal API)
app.MapPost("/pay", (PaymentService service, string type, decimal amount) =>
{
service.Pay(type, amount);
return Results.Ok("Payment completed");
});
Request παραδείγματα
POST /pay?type=credit&amount=100
POST /pay?type=paypal&amount=200
Το DI δίνει όλες τις στρατηγικές
Το PaymentContext επιλέγει σωστή με βάση το type
Το PaymentService δεν ξέρει τίποτα για implementations
Μηδέν if/else, full extensibility
Αν τώρα προσθέσεις:
public class CryptoPayment : IPaymentStrategy
{
public string Key => "crypto";
public void Pay(decimal amount)
{
Console.WriteLine($"Paid {amount} with Crypto");
}
}
και κάνεις:
builder.Services.AddScoped<IPaymentStrategy, CryptoPayment>();
Να θυμάσαι..
Το Strategy Pattern είναι ένα από τα πιο χρήσιμα patterns γιατί:
- Διαχωρίζει το τι κάνεις από το πώς το κάνεις
- Σου δίνει ελευθερία να αλλάζεις behavior χωρίς να “σπας” τον κώδικα
- Σε φέρνει πιο κοντά σε καθαρή αρχιτεκτονική (clean architecture)
Το Strategy Pattern μόνο του είναι καλό.
Με Dependency Injection γίνεται enterprise-grade εργαλείο.
Σου δίνει:
- ευελιξία
- επεκτασιμότητα
- καθαρότητα
- testability

Top comments (0)