DEV Community

nikosst
nikosst

Posted on

Strategy Pattern Θεωρία, Χρήση και Πρακτική Εφαρμογή

Τι είναι τα 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();
Enter fullscreen mode Exit fullscreen mode

Προβλήματα:

  • Παραβίαση του Open/Closed Principle
  • Δύσκολη συντήρηση
  • Δύσκολο testing
  • Κακή επεκτασιμότητα

Η ιδέα του Strategy Pattern

Αντί να έχεις conditional logic, κάνεις:

“Δίνω” στο αντικείμενο μια στρατηγική και αυτό τη χρησιμοποιεί.


Δομή

Το pattern έχει 3 βασικά μέρη:

  • Strategy Interface
  • Concrete Strategies
  • Context

Παράδειγμα σε C#

  1. Strategy Interface
public interface IPaymentStrategy
{
    void Pay(decimal amount);
}
Enter fullscreen mode Exit fullscreen mode
  1. 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");
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. 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);
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Χρήση
var context = new PaymentContext(new CreditCardPayment());
context.ExecutePayment(100);

context.SetStrategy(new PaypalPayment());
context.ExecutePayment(200);
Enter fullscreen mode Exit fullscreen mode

Γιατί να το χρησιμοποιήσεις

  1. Καθαρός Κώδικας

Δεν έχεις if/else παντού.

  1. Open/Closed Principle

Προσθέτεις νέα στρατηγική χωρίς να αλλάξεις υπάρχοντα κώδικα.

  1. Testability

Κάθε στρατηγική μπορεί να γίνει unit test ανεξάρτητα.

  1. Επαναχρησιμοποίηση

Οι στρατηγικές είναι plug-and-play.

  1. 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();
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

Παράδειγμα χρήσης (π.χ. σε Controller ή minimal API)

app.MapPost("/pay", (PaymentService service, string type, decimal amount) =>
{
    service.Pay(type, amount);
    return Results.Ok("Payment completed");
});
Enter fullscreen mode Exit fullscreen mode

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");
    }
}
Enter fullscreen mode Exit fullscreen mode

και κάνεις:

builder.Services.AddScoped<IPaymentStrategy, CryptoPayment>();
Enter fullscreen mode Exit fullscreen mode

Να θυμάσαι..

Το Strategy Pattern είναι ένα από τα πιο χρήσιμα patterns γιατί:

  • Διαχωρίζει το τι κάνεις από το πώς το κάνεις
  • Σου δίνει ελευθερία να αλλάζεις behavior χωρίς να “σπας” τον κώδικα
  • Σε φέρνει πιο κοντά σε καθαρή αρχιτεκτονική (clean architecture)

Το Strategy Pattern μόνο του είναι καλό.

Με Dependency Injection γίνεται enterprise-grade εργαλείο.

Σου δίνει:

  • ευελιξία
  • επεκτασιμότητα
  • καθαρότητα
  • testability

nikosstit@gmail.com

Top comments (0)