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