Specification Pattern

Seitdem ich die praktische Anwendung des von Eric Evans in Domain-Driven Design beschriebenen Spezifikationsmusters in Jimmy Nilsons Buch gesehen habe, bin ich ein großer Fan geworden.

SPECIFICATION provides a concise way of expressing certain kinds of rules, extricating them from conditional logic and making them explicit in the model.

Genau dasselbe Prinzip habe ich in diesem Artikel angewendet und ich nutze dieser Vorgehensweise laufend.

  • Wann braucht man eine Spezifikation?
    Immer, wenn ein Domain-Objekt bestimmte Erwartungen erfüllen muss, zum Beispiel beim Validieren (wie in o.g. Artikel) oder bei der Suche anhand von bestimmten Kriterien.
  • namespace Specification
    {
        public interface IValidable{}
    
        public class Invoice : IValidable{}
    
        public interface ISpecification
        {
            bool IsSatisfiedBy(IValidable obj);
        }
    
        public class BlackListClientsSpecification : ISpecification
        {
            private readonly DateTime m_currentDate;
    
            public DelinquentClientsSpecification(DateTime currentDate)
            {
                m_currentDate = currentDate;
            }
    
            public bool IsSatisfiedBy(IValidable obj)
            {
                Client candidate = obj as Client;
                //do something with Candidate and CurrentDate
                return true;
            }
        }
    }
    //Selektion:
    ...
        public class ClientRepository
        {
            IList<Client> SelectSatisfying(ISpecification specification)
            {
                return SelectAllClients().Where(a => specification.IsSatisfiedBy(a));
            }
        }
    ...
        var delinquentClients = clientRepository.SelectSatisfying(new DelinquentClientSpecification(currentDate));
    ...
    
  • Wozu braucht man eine Spezifikation?
    Ich kenne das aus eigener Erfahrung, wie sich Codeschnippsel (zum Beispiel obj.State==valid oder obj.Activ==true && obj.Created usw.), die verschiedenen Kriterien darstellen, wie Unkraut überall in Code verbreiten. Das kann man mit dieser Lösung wunderbar unterbinden, alle Bedingungen müssen in die jeweils passende Spezifikation gesammelt werden.

Man kann als Basis ein Interface, ( ISpecification ) oder eine abstrakte, an das beschriebene Domain Objekt angepasste Klasse InvoiceSpecification nutzen, abhängig davon, ob man nur einen Kontrakt oder auch Code wiederverwenden will.
Aus der Sicht des Testens, sind natürlich diese Klassen ideal, sie haben keine Abhängigkeiten, sind pure logische Ausdrücke der Entscheidungen z.B. der Geschäftsleitung. Für diese Klassen wurde TDD erfunden ;).