Decorazione di una classe che chiama i propri metodi pubblici

1

Ho scritto un sistema che calcola gli sconti per un carrello della spesa basato su un insieme di regole. Ogni regola è implementata con la seguente interfaccia (C #):

interface IRule
{
   bool IsActive();
   bool IsApplicableTo(IShoppingCart cart);
   decimal CalculateDiscount(IShoppingCart cart);
}

Un'implementazione concreta potrebbe essere simile a questa:

class Rule : IRule
{
   bool IsActive() { return true; }

   bool IsApplicableTo(IShoppingCart cart)
   {
       if (!IsActive())
          return false;

       return TestIfApplicable(cart);
   }

   decimal CalculateDiscount(IShoppingCart cart)
   {
       if (!IsApplicableTo(cart))
          return 0m;

       return CalculateStuff(cart);
   }
}

Ora sto cercando di introdurre alcune registrazioni nel sistema. Per mantenere le cose pulite, non voglio inserirle nella classe Rule. Così ho pensato di creare un decoratore per IRule, che gestisce la registrazione:

class LoggingRule : IRule
{
    IRule decoratedRule;

    bool IsActive()
    {
        LogImportantThings();

        return decoratedRule.IsActive();
    }

    //And so on.
}

Il problema che sto incontrando qui, è che la classe Rule chiama in realtà i metodi IsActive e IsApplicableTo. Quando ciò accade, il decoratore viene semplicemente passato e non viene registrato nulla.

Suppongo che potrei rendere la LoggingRule una sottoclasse di Rule. Significherebbe che dovrei fare una variante LoggingRule per ogni implementazione IRule. Ciò potrebbe potenzialmente causare molte duplicazioni.

Potrei anche inserire la registrazione in una classe base e usare il modello di oggetto Null quando non voglio registrare nulla. Ma poi di nuovo, 99 su 100 volte non voglio registrare nulla. E quando questi metodi vengono chiamati molte volte, vale la pena di un potenziale sovraccarico?

Quindi nessuna delle due soluzioni sembra particolarmente elegante, e sto lottando per trovare un'alternativa migliore ... Qualche suggerimento?

    
posta PJanssen 01.09.2014 - 15:15
fonte

1 risposta

0

Può un Rule essere attivo ma non applicare a un carrello? Sì giusto? Può un Rule applicare a un carrello della spesa e non essere attivo? Di nuovo, si. Quindi, come vedo, il metodo IsApplicableTo è (o almeno dovrebbe essere pensato come) la sua proprietà indipendente da IsActive . Se IsApplicableTo chiama IsActive , bene, anche se come regola generale, se è pubblico, è pensato per essere chiamato, e quindi verrà chiamato due volte.

In questo modo, chi gestisce IRule dovrebbe chiamare entrambi, quindi la responsabilità non è fino a IRule per restituire false se IsApplicableTo restituirebbe altrimenti true e IsActive restituisce false. Allo stesso modo, non hai bisogno di chiamare IsApplicableTo da CalculateDiscount , poiché è responsabilità del chiamante. Il chiamante probabilmente avrebbe qualcosa di simile al seguente:

for each rule:
    if rule.IsActive() and rule.IsApplicableTo(shoppingCart):
        total *= (1.0 - rule.CalculateDiscount(shoppingCart));

Se IsApplicableTo restituisce false perché IsApplicableTo chiama IsActive e IsActive restituisce false, il risultato è in definitiva lo stesso. In questo modo, LoggingRule può decorare un'altra regola, derivare solo da IRule e funzionerebbe perfettamente. Basta non commettere l'errore di aggiungere LoggingRule e la regola incapsulata stessa nella stessa lista.

Spero che ti aiuti!

    
risposta data 01.09.2014 - 15:33
fonte

Leggi altre domande sui tag