A számítástudományban a specifikáció programtervezési minta egy olyan minta, amelynek üzleti szabályai kombinálhatók egymással a logika szabályai szerint. A mintát gyakran használják a tartományvezérelt fejlesztésben. Nem tévesztendő össze a program specifikációjával, ami azt írja le, hogy a programnak mit kell tudni.
A specifikáció tervminta üzleti szabályt vázol, ami kombinálható más üzleti szabályokkal. Ebben a mintában az üzleti logika egy egysége funkcionalitását az összetett specifikáció (Composite Specification) absztrakt aggregátumból örökli. Az összetett specifikációnak van egy IsSatisfiedBy logikai függvénye. Példányosítás után A specifikációt összeláncolják más specifikációkkal, így az új specifikációk könnyen karbantarthatók, de jól konfigurálhatók. Továbbá példányosítva metódushívással vagy a kontroll megfordításával megváltoztatja az állapotát, így egy másik osztály delegáltjává válik. Kódpéldák
C# Egy C# kódpélda: public interface ISpecification
bool IsSatisfiedBy(object candidate); ISpecification And(ISpecification other); ISpecification AndNot(ISpecification other); ISpecification Or(ISpecification other); ISpecification OrNot(ISpecification other); ISpecification Not();
public abstract class CompositeSpecification : ISpecification
public abstract bool IsSatisfiedBy(object candidate);
public ISpecification And(ISpecification other)
return new AndSpecification(this, other);
public ISpecification AndNot(ISpecification other)
return new AndNotSpecification(this, other);
public ISpecification Or(ISpecification other)
return new OrSpecification(this, other);
public ISpecification OrNot(ISpecification other)
return new OrNotSpecification(this, other);
public ISpecification Not()
return new NotSpecification(this);
public class AndSpecification : CompositeSpecification
private ISpecification leftCondition; private ISpecification rightCondition;
public AndSpecification(ISpecification left, ISpecification right)
leftCondition = left; rightCondition = right;
public override bool IsSatisfiedBy(object candidate)
return leftCondition.IsSatisfiedBy(candidate) && rightCondition.IsSatisfiedBy(candidate);
public class AndNotSpecification : CompositeSpecification
private ISpecification leftCondition; private ISpecification rightCondition;
public AndNotSpecification(ISpecification left, ISpecification right)
leftCondition = left; rightCondition = right;
public override bool IsSatisfiedBy(object candidate)
return leftCondition.IsSatisfiedBy(candidate) && rightCondition.IsSatisfiedBy(candidate) != true;
public class OrSpecification : CompositeSpecification
private ISpecification leftCondition; private ISpecification rightCondition;
public OrSpecification(ISpecification left, ISpecification right)
leftCondition = left; rightCondition = right;
public override bool IsSatisfiedBy(object candidate)
return leftCondition.IsSatisfiedBy(candidate) || rightCondition.IsSatisfiedBy(candidate);
public class OrNotSpecification : CompositeSpecification
private ISpecification leftCondition; private ISpecification rightCondition;
public OrNotSpecification(ISpecification left, ISpecification right)
leftCondition = left; rightCondition = right;
public override bool IsSatisfiedBy(object candidate)
return leftCondition.IsSatisfiedBy(candidate) || rightCondition.IsSatisfiedBy(candidate) != true;
public class NotSpecification : CompositeSpecification
private ISpecification Wrapped;
public NotSpecification(ISpecification x)
Wrapped = x;
public override bool IsSatisfiedBy(object candidate)
return !Wrapped.IsSatisfiedBy(candidate);
C# 6.0 generikusokkal
public interface ISpecification<T>
bool IsSatisfiedBy(T candidate); ISpecification<T> And(ISpecification<T> other); ISpecification<T> AndNot(ISpecification<T> other); ISpecification<T> Or(ISpecification<T> other); ISpecification<T> OrNot(ISpecification<T> other); ISpecification<T> Not();
public abstract class LinqSpecification<T> : CompositeSpecification<T>
public abstract Expression<Func<T, bool>> AsExpression(); public override bool IsSatisfiedBy(T candidate) => AsExpression().Compile()(candidate);
public abstract class CompositeSpecification<T> : ISpecification<T>
public abstract bool IsSatisfiedBy(T candidate); public ISpecification<T> And(ISpecification<T> other) => new AndSpecification<T>(this, other); public ISpecification<T> AndNot(ISpecification<T> other) => new AndNotSpecification<T>(this, other); public ISpecification<T> Or(ISpecification<T> other) => new OrSpecification<T>(this, other); public ISpecification<T> OrNot(ISpecification<T> other) => new OrNotSpecification<T>(this, other); public ISpecification<T> Not() => new NotSpecification<T>(this);
public class AndSpecification<T> : CompositeSpecification<T>
ISpecification<T> left; ISpecification<T> right;
public AndSpecification(ISpecification<T> left, ISpecification<T> right)
this.left = left; this.right = right;
public override bool IsSatisfiedBy(T candidate) => left.IsSatisfiedBy(candidate) && right.IsSatisfiedBy(candidate);
public class AndNotSpecification<T> : CompositeSpecification<T>
ISpecification<T> left; ISpecification<T> right;
public AndNotSpecification(ISpecification<T> left, ISpecification<T> right)
this.left = left; this.right = right;
public override bool IsSatisfiedBy(T candidate) => left.IsSatisfiedBy(candidate) && right.IsSatisfiedBy(candidate) != true;
public class OrSpecification<T> : CompositeSpecification<T>
ISpecification<T> left; ISpecification<T> right;
public OrSpecification(ISpecification<T> left, ISpecification<T> right)
this.left = left; this.right = right;
public override bool IsSatisfiedBy(T candidate) => left.IsSatisfiedBy(candidate) || right.IsSatisfiedBy(candidate);
public class OrNotSpecification<T> : CompositeSpecification<T>
ISpecification<T> left; ISpecification<T> right;
public OrNotSpecification(ISpecification<T> left, ISpecification<T> right)
this.left = left; this.right = right;
public override bool IsSatisfiedBy(T candidate) => left.IsSatisfiedBy(candidate) || right.IsSatisfiedBy(candidate) != true;
public class NotSpecification<T> : CompositeSpecification<T>
ISpecification<T> other; public NotSpecification(ISpecification<T> other) => this.other = other; public override bool IsSatisfiedBy(T candidate) => !other.IsSatisfiedBy(candidate);
Példa a használatra A következő példában hívásokat küldünk a kollekció ügynökségnek, ha: a hívás esedékes az értesítések el lettek küldve nincsenek meg a kollekció ügynökségnél.
A példa a logika láncolásának végeredményét mutatja be.
A felhasználó feltételez egy már korábban használt OverdueSpecification osztályt, ami igazat mutat, ha a hívási dátum legalább 30 napos, egy NoticeSentSpecification osztályt, ami igazat mutat, ha három értesítést elküldtek, és egy InCollectionSpecification osztályt, ami igazat mutat, ha már küldték a hívást. Ezeknek az implementációja itt nem érdekes.
Ezekkel létrehozunk egy újabb specifikáció osztályt, aminek neve SendToCollection, ami akkor igaz, ha a fenti három feltétel teljesül.
var OverDue = new OverDueSpecification(); var NoticeSent = new NoticeSentSpecification(); var InCollection = new InCollectionSpecification();
// example of specification pattern logic chaining var SendToCollection = OverDue.And(NoticeSent).And(InCollection.Not());
var InvoiceCollection = Service.GetInvoices();
foreach (var currentInvoice in InvoiceCollection) { if (SendToCollection.IsSatisfiedBy(currentInvoice)) { currentInvoice.SendToCollection();
Kritika A specifikáció minta tekinthető programtervezési antimintának is: Cargo kultusz antiminta: A mintának nincsenek jó definiált céljai, hiányoznak az útmutatások is, hogy mikor használjuk és mikor ne. Lásd még: Az eszköz törvénye (arany kalapács). Belső platform hatás: A logikai függvények C#-ban duplikálják a már meglevő alsóbb szintű logikai műveleteket. Lásd még: A kerék újrafeltalálása. Spagetti kód antiminta: Mivel a minta a specifikáció minden részéhez más osztályt definiál, a kód töredezettségét okozza, mivel azt is szétbontja, ami összetartozik. A fenti példában az OverDue egy extra réteg a SendToCollection és az OverDueSpecification között.
A legtöbb programozási nyelv az alapvető objektumorientált fogalmakkal természetesen alkalmazkodik a tartományvezérelt fejlesztéshez.
A példa a specifikáció minta nélkül: var InvoiceCollection = Service.GetInvoices(); foreach (var invoice in InvoiceCollection) invoice.SendToCollectionIfNecessary();
// Invoice methods: public void SendToCollectionIfNecessary() { if (ShouldSendToCollection()) SendToCollection(); }
private bool ShouldSendToCollection() => currentInvoice.OverDue && currentInvoice.NoticeSent && !currentInvoice.InCollection;
Ez az alternatíva csak a következőket használja: csak olvasható tulajdonságok, feltétellogika és függvények. Itt akulcs alternatíva a csak olvasható tulajdonságok, amelyekl jól elnevezve támogatják a tartományvezérelt fejlesztést, és a specifikáció által definiált logikai műveletek helyett a nyelvben már meglevő logikai műveleteket használja. Továbbá ergy jól elnevezett SendToCollectionIfNecessary függvény potenciálisan hasznosabb és még leíró is, jobban, mint a specifikációt használó példa, ami tartalmazott ugyan egy hasonló függvényt, de az nem kapcsolódott közvetlenül az objektumhoz.
Források
Fordítás
Kategória:Programtervezési minták