DEV Community

nikosst
nikosst

Posted on

Strategy & Factory Pattern στην C# Μια ορθολογιστική και SOLID προσέγγιση

Θεωρητική Θεμελίωση

1. Γιατί υπάρχουν τα Design Patterns;

Τα Design Patterns δεν είναι "μαγικές συνταγές".
Είναι τυποποιημένες λύσεις σε επαναλαμβανόμενα προβλήματα σχεδίασης.

Αριστοτελικά θα λέγαμε:

  • Υλική αιτία: Ο κώδικας και οι κλάσεις.
  • Μορφική αιτία: Η αρχιτεκτονική δομή (pattern).
  • Ποιητική αιτία: Ο προγραμματιστής.
  • Τελική αιτία: Η διατηρησιμότητα, επεκτασιμότητα, καθαρότητα.
  • Τα patterns υπηρετούν την τελική αιτία: την ποιότητα του λογισμικού.

Strategy Pattern

Τι είναι το Strategy Pattern;

Το Strategy είναι ένα behavioral pattern που:

Ορίζει μια οικογένεια αλγορίθμων, τους ενθυλακώνει, και τους καθιστά εναλλάξιμους κατά τον χρόνο εκτέλεσης.

Με απλά λόγια:
Αν έχεις πολλούς τρόπους να κάνεις κάτι, αντί για if/switch, βάζεις διαφορετικές στρατηγικές.


Πότε το χρησιμοποιώ;

Χρησιμοποιείται όταν:

  • Έχεις πολλαπλούς αλγορίθμους για το ίδιο πρόβλημα
  • Θες runtime επιλογή συμπεριφοράς
  • Θες να αποφύγεις μεγάλα if/switch
  • Θες να τηρείς το Open/Closed Principle

SOLID Θεμελίωση

Το Strategy στηρίζεται κυρίως:

SRP Single Responsibility

Κάθε στρατηγική έχει μία ευθύνη.

OCP Open/Closed

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

DIP Dependency Inversion

Το context εξαρτάται από abstraction (interface), όχι από concrete class.


Παράδειγμα Strategy Pattern (C#)

Σενάριο: Υπολογισμός Έκπτωσης

Θέλουμε διαφορετικούς τρόπους υπολογισμού έκπτωσης:

  • Κανονικός πελάτης
  • Premium πελάτης
  • Black Friday

Ορισμός Strategy Interface

public interface IDiscountStrategy
{
    decimal CalculateDiscount(decimal amount);
}

Enter fullscreen mode Exit fullscreen mode

Concrete Strategies

public class RegularDiscountStrategy : IDiscountStrategy
{
    public decimal CalculateDiscount(decimal amount)
    {
        return amount * 0.05m;
    }
}

public class PremiumDiscountStrategy : IDiscountStrategy
{
    public decimal CalculateDiscount(decimal amount)
    {
        return amount * 0.15m;
    }
}

public class BlackFridayDiscountStrategy : IDiscountStrategy
{
    public decimal CalculateDiscount(decimal amount)
    {
        return amount * 0.30m;
    }
}

Enter fullscreen mode Exit fullscreen mode

Context Class

public class Order
{
    private readonly IDiscountStrategy _discountStrategy;

    public Order(IDiscountStrategy discountStrategy)
    {
        _discountStrategy = discountStrategy;
    }

    public decimal CalculateFinalPrice(decimal amount)
    {
        var discount = _discountStrategy.CalculateDiscount(amount);
        return amount - discount;
    }
}

Enter fullscreen mode Exit fullscreen mode

Χρήση

var strategy = new PremiumDiscountStrategy();
var order = new Order(strategy);

var finalPrice = order.CalculateFinalPrice(1000);
Console.WriteLine(finalPrice);

Enter fullscreen mode Exit fullscreen mode

Ορθολογική Ανάλυση

Χωρίς Strategy:

if(customerType == "Premium")
...
else if(...)

Enter fullscreen mode Exit fullscreen mode

Αυτό παραβιάζει:

  • OCP
  • SRP
  • οδηγεί σε conditional complexity

Με Strategy:

  • Συμπεριφορά αποσυνδέεται
  • Ο κώδικας γίνεται επεκτάσιμος
  • Μπορεί να ενσωματωθεί σε DI container

Factory Pattern

Τι είναι το Factory Pattern;

Factory είναι creational pattern.

Αντί να δημιουργείς αντικείμενα με new,
μεταφέρεις τη δημιουργία σε ειδική κλάση.

Αποσυνδέει τον client από τη διαδικασία κατασκευής.


Πότε το χρησιμοποιώ;

  • Όταν η δημιουργία αντικειμένων είναι σύνθετη
  • Όταν θέλω να αποκρύψω implementation details
  • Όταν η επιλογή concrete class γίνεται runtime
  • Όταν τηρώ το DIP

SOLID Θεμελίωση
SRP

Η Factory έχει μία ευθύνη: δημιουργία αντικειμένων.

OCP

Νέα προϊόντα → νέα implementations, όχι αλλαγή client.

DIP

Ο client εξαρτάται από abstraction.


Παράδειγμα Factory Pattern

Σενάριο: Δημιουργία Payment Processor

Abstraction

public interface IPaymentProcessor
{
    void ProcessPayment(decimal amount);
}

Enter fullscreen mode Exit fullscreen mode

Concrete Implementations

public class CreditCardPayment : IPaymentProcessor
{
    public void ProcessPayment(decimal amount)
    {
        Console.WriteLine($"Processing credit card payment: {amount}");
    }
}

public class PayPalPayment : IPaymentProcessor
{
    public void ProcessPayment(decimal amount)
    {
        Console.WriteLine($"Processing PayPal payment: {amount}");
    }
}

Enter fullscreen mode Exit fullscreen mode

Factory

public class PaymentProcessorFactory
{
    public static IPaymentProcessor Create(string paymentType)
    {
        return paymentType switch
        {
            "CreditCard" => new CreditCardPayment(),
            "PayPal" => new PayPalPayment(),
            _ => throw new ArgumentException("Invalid payment type")
        };
    }
}

Enter fullscreen mode Exit fullscreen mode

Χρήση

var processor = PaymentProcessorFactory.Create("CreditCard");
processor.ProcessPayment(500);

Enter fullscreen mode Exit fullscreen mode

Αριστοτελική Ανάλυση

Το Strategy απαντά στο ερώτημα:

Ποια πράξη θα εκτελέσω;

Το Factory απαντά στο ερώτημα:

Ποιο ον θα δημιουργήσω;

Το πρώτο αφορά ενέργεια (πρᾶξις)
Το δεύτερο αφορά γένεσιν (δημιουργία)


Strategy vs Factory Συγκριτικός Πίνακας

| Strategy            | Factory               |
| ------------------- | --------------------- |
| Behavioral          | Creational            |
| Αλλάζει συμπεριφορά | Αλλάζει δημιουργία    |
| Runtime εναλλαγή    | Runtime επιλογή τύπου |
| Αντικαθιστά if      | Αντικαθιστά new       |

Enter fullscreen mode Exit fullscreen mode

Συνδυασμός Strategy + Factory (Advanced)

Συχνά:

  • Factory δημιουργεί Strategy
  • Strategy εκτελεί συμπεριφορά
public class DiscountStrategyFactory
{
    public static IDiscountStrategy Create(string type)
    {
        return type switch
        {
            "Regular" => new RegularDiscountStrategy(),
            "Premium" => new PremiumDiscountStrategy(),
            "BlackFriday" => new BlackFridayDiscountStrategy(),
            _ => throw new ArgumentException()
        };
    }
}

Enter fullscreen mode Exit fullscreen mode

Πότε ΔΕΝ πρέπει να τα χρησιμοποιώ;

  • Όταν έχω μόνο μία υλοποίηση
  • Όταν η πολυπλοκότητα υπερβαίνει το όφελος
  • Όταν οδηγούμαι σε over-engineering

Η αρετή βρίσκεται στο μέσον (Αριστοτέλης).


Επίλογος

Τα patterns δεν είναι αυτοσκοπός.
Είναι εργαλεία λογικής καθαρότητας.

Ο καλός σχεδιασμός:

  • Ελαχιστοποιεί σύζευξη
  • Μεγιστοποιεί συνοχή
  • Επιτρέπει επέκταση χωρίς τροποποίηση

Strategy → ελευθερία συμπεριφοράς
Factory → ελευθερία δημιουργίας

Αν θέλετε, μπορούμε να προχωρήσουμε σε:

  • Abstract Factory
  • Factory Method
  • Real-world Clean Architecture παράδειγμα
  • Ενσωμάτωση με DI / ASP.NET Core
  • Ακαδημαϊκή μορφοποίηση έτοιμη για δημοσίευση

nikosstit@gmail.com

Top comments (0)