Specifikační vzor - Specification pattern

V počítačovém programování je vzor specifikace je zvláštní vzor návrhu softwaru, čímž obchodní pravidla lze rekombinovat zřetězením obchodních pravidel pomocí logické logiky. Vzor se často používá v kontextu design řízený doménou.
Vzor specifikace popisuje obchodní pravidlo, které je kombinovatelné s jinými obchodními pravidly. V tomto vzoru zdědí jednotka obchodní logiky svou funkčnost z abstraktní agregované třídy Composite Specification. Třída Composite Specification má jednu funkci nazvanou IsSatisfiedBy, která vrací logickou hodnotu. Po vytvoření instance je specifikace „zřetězena“ s dalšími specifikacemi, díky čemuž jsou nové specifikace snadno udržovatelné a přitom vysoce přizpůsobitelné obchodní logika. Dále může po vytvoření instance obchodní logika prostřednictvím vyvolání metody nebo inverze kontroly, mít jeho stav změněn, aby se stal delegátem jiných tříd, jako je úložiště perzistence.
Příklady kódu
C#
veřejnost rozhraní ISpecifikace { bool IsSatisfiedBy(objekt kandidát); ISpecifikace A(ISpecifikace jiný); ISpecifikace AndNot(ISpecifikace jiný); ISpecifikace Nebo(ISpecifikace jiný); ISpecifikace Nebo ne(ISpecifikace jiný); ISpecifikace Ne(); } veřejnost abstraktní třída Kompozitní specifikace : ISpecifikace { veřejnost abstraktní bool IsSatisfiedBy(objekt kandidát); veřejnost ISpecifikace A(ISpecifikace jiný) { vrátit se Nový Specifikace(tento, jiný); } veřejnost ISpecifikace AndNot(ISpecifikace jiný) { vrátit se Nový AndNotSpecification(tento, jiný); } veřejnost ISpecifikace Nebo(ISpecifikace jiný) { vrátit se Nový Nebo Specifikace(tento, jiný); } veřejnost ISpecifikace Nebo ne(ISpecifikace jiný) { vrátit se Nový OrNotSpecification(tento, jiný); } veřejnost ISpecifikace Ne() { vrátit se Nový Specifikace není(tento); } } veřejnost třída Specifikace : Kompozitní specifikace { soukromé ISpecifikace leftCondition; soukromé ISpecifikace pravý stav; veřejnost Specifikace(ISpecifikace vlevo, odjet, ISpecifikace že jo) { leftCondition = vlevo, odjet; pravý stav = že jo; } veřejnost přepsat bool IsSatisfiedBy(objekt kandidát) { vrátit se leftCondition.IsSatisfiedBy(kandidát) && pravý stav.IsSatisfiedBy(kandidát); } } veřejnost třída AndNotSpecification : Kompozitní specifikace { soukromé ISpecifikace leftCondition; soukromé ISpecifikace pravý stav; veřejnost AndNotSpecification(ISpecifikace vlevo, odjet, ISpecifikace že jo) { leftCondition = vlevo, odjet; pravý stav = že jo; } veřejnost přepsat bool IsSatisfiedBy(objekt kandidát) { vrátit se leftCondition.IsSatisfiedBy(kandidát) && pravý stav.IsSatisfiedBy(kandidát) != skutečný; } } veřejnost třída Nebo Specifikace : Kompozitní specifikace { soukromé ISpecifikace leftCondition; soukromé ISpecifikace pravý stav; veřejnost Nebo Specifikace(ISpecifikace vlevo, odjet, ISpecifikace že jo) { leftCondition = vlevo, odjet; pravý stav = že jo; } veřejnost přepsat bool IsSatisfiedBy(objekt kandidát) { vrátit se leftCondition.IsSatisfiedBy(kandidát) || pravý stav.IsSatisfiedBy(kandidát); } } veřejnost třída OrNotSpecification : Kompozitní specifikace { soukromé ISpecifikace leftCondition; soukromé ISpecifikace pravý stav; veřejnost OrNotSpecification(ISpecifikace vlevo, odjet, ISpecifikace že jo) { leftCondition = vlevo, odjet; pravý stav = že jo; } veřejnost přepsat bool IsSatisfiedBy(objekt kandidát) { vrátit se leftCondition.IsSatisfiedBy(kandidát) || pravý stav.IsSatisfiedBy(kandidát) != skutečný; } } veřejnost třída Specifikace není : Kompozitní specifikace { soukromé ISpecifikace Zabalené; veřejnost Specifikace není(ISpecifikace X) { Zabalené = X; } veřejnost přepsat bool IsSatisfiedBy(objekt kandidát) { vrátit se !Zabalené.IsSatisfiedBy(kandidát); } }
C # 6.0 s generiky
veřejnost rozhraní ISpecifikace<T> { bool IsSatisfiedBy(T kandidát); ISpecifikace<T> A(ISpecifikace<T> jiný); ISpecifikace<T> AndNot(ISpecifikace<T> jiný); ISpecifikace<T> Nebo(ISpecifikace<T> jiný); ISpecifikace<T> Nebo ne(ISpecifikace<T> jiný); ISpecifikace<T> Ne(); } veřejnost abstraktní třída LinqSpecifikace<T> : Kompozitní specifikace<T> { veřejnost abstraktní Výraz<Func<T, bool>> AsExpression(); veřejnost přepsat bool IsSatisfiedBy(T kandidát) => AsExpression().Kompilovat()(kandidát); } veřejnost abstraktní třída Kompozitní specifikace<T> : ISpecifikace<T> { veřejnost abstraktní bool IsSatisfiedBy(T kandidát); veřejnost ISpecifikace<T> A(ISpecifikace<T> jiný) => Nový Specifikace<T>(tento, jiný); veřejnost ISpecifikace<T> AndNot(ISpecifikace<T> jiný) => Nový AndNotSpecification<T>(tento, jiný); veřejnost ISpecifikace<T> Nebo(ISpecifikace<T> jiný) => Nový Nebo Specifikace<T>(tento, jiný); veřejnost ISpecifikace<T> Nebo ne(ISpecifikace<T> jiný) => Nový OrNotSpecification<T>(tento, jiný); veřejnost ISpecifikace<T> Ne() => Nový Specifikace není<T>(tento); } veřejnost třída Specifikace<T> : Kompozitní specifikace<T> { ISpecifikace<T> vlevo, odjet; ISpecifikace<T> že jo; veřejnost Specifikace(ISpecifikace<T> vlevo, odjet, ISpecifikace<T> že jo) { tento.vlevo, odjet = vlevo, odjet; tento.že jo = že jo; } veřejnost přepsat bool IsSatisfiedBy(T kandidát) => vlevo, odjet.IsSatisfiedBy(kandidát) && že jo.IsSatisfiedBy(kandidát); } veřejnost třída AndNotSpecification<T> : Kompozitní specifikace<T> { ISpecifikace<T> vlevo, odjet; ISpecifikace<T> že jo; veřejnost AndNotSpecification(ISpecifikace<T> vlevo, odjet, ISpecifikace<T> že jo) { tento.vlevo, odjet = vlevo, odjet; tento.že jo = že jo; } veřejnost přepsat bool IsSatisfiedBy(T kandidát) => vlevo, odjet.IsSatisfiedBy(kandidát) && !že jo.IsSatisfiedBy(kandidát); } veřejnost třída Nebo Specifikace<T> : Kompozitní specifikace<T> { ISpecifikace<T> vlevo, odjet; ISpecifikace<T> že jo; veřejnost Nebo Specifikace(ISpecifikace<T> vlevo, odjet, ISpecifikace<T> že jo) { tento.vlevo, odjet = vlevo, odjet; tento.že jo = že jo; } veřejnost přepsat bool IsSatisfiedBy(T kandidát) => vlevo, odjet.IsSatisfiedBy(kandidát) || že jo.IsSatisfiedBy(kandidát); } veřejnost třída OrNotSpecification<T> : Kompozitní specifikace<T> { ISpecifikace<T> vlevo, odjet; ISpecifikace<T> že jo; veřejnost OrNotSpecification(ISpecifikace<T> vlevo, odjet, ISpecifikace<T> že jo) { tento.vlevo, odjet = vlevo, odjet; tento.že jo = že jo; } veřejnost přepsat bool IsSatisfiedBy(T kandidát) => vlevo, odjet.IsSatisfiedBy(kandidát) || !že jo.IsSatisfiedBy(kandidát); } veřejnost třída Specifikace není<T> : Kompozitní specifikace<T> { ISpecifikace<T> jiný; veřejnost Specifikace není(ISpecifikace<T> jiný) => tento.jiný = jiný; veřejnost přepsat bool IsSatisfiedBy(T kandidát) => !jiný.IsSatisfiedBy(kandidát); }
Krajta
z abc import abstraktní metodaz datové třídy import datová třídaz psaní na stroji import Žádnýtřída Základní specifikace: @abstractmethod def is_satisfied_by(já, kandidát: Žádný) -> bool: vyzdvihnout NotImplementedError() def a_(já, jiný: „BaseSpecification“) -> „AndSpecification“: vrátit se Specifikace(já, jiný) def nebo_(já, jiný: „BaseSpecification“) -> „OrSpecification“: vrátit se Nebo Specifikace(já, jiný) def ne_(já) -> „NotSpecification“: vrátit se Specifikace není(já)@dataclass(zamrzlý=Skutečný)třída Specifikace(Základní specifikace): za prvé: Základní specifikace druhý: Základní specifikace def is_satisfied_by(já, kandidát: Žádný) -> bool: vrátit se já.za prvé.is_satisfied_by(kandidát) a já.druhý.is_satisfied_by(kandidát)@dataclass(zamrzlý=Skutečný)třída Nebo Specifikace(Základní specifikace): za prvé: Základní specifikace druhý: Základní specifikace def is_satisfied_by(já, kandidát: Žádný) -> bool: vrátit se já.za prvé.is_satisfied_by(kandidát) nebo já.druhý.is_satisfied_by(kandidát)@dataclass(zamrzlý=Skutečný)třída Specifikace není(Základní specifikace): předmět: Základní specifikace def is_satisfied_by(já, kandidát: Žádný) -> bool: vrátit se ne já.předmět.is_satisfied_by(kandidát)
Příklad použití
V následujícím příkladu načítáme faktury a odesíláme je inkasní agentuře, pokud
- jsou po splatnosti,
- oznámení byla zaslána a
- nejsou již u sběrné agentury.
Tento příklad má ukázat konečný výsledek toho, jak je logika „zřetězena“ dohromady.
Tento příklad použití předpokládá dříve definovanou třídu OverdueSpecification, která je splněna, když je datum splatnosti faktury 30 dní nebo starší, třída NoteSentSpecification, která je splněna, když byly zákazníkovi odeslány tři oznámení, a třída InCollectionSpecification, která je splněna, když má faktura již byly zaslány inkasní agentuře. Implementace těchto tříd zde není důležitá.
Pomocí těchto tří specifikací jsme vytvořili novou specifikaci nazvanou SendToCollection, která bude uspokojena, když dojde k prodlení s fakturou, když budou oznámení zaslána zákazníkovi a již nejsou u inkasní agentury.
var Zpožděný = Nový OverDueSpecification();var Oznámení odesláno = Nový NoteSentSpecification();var InCollection = Nový InCollectionSpecification();// příklad logického řetězení specifikačního vzoruvar SendToCollection = Zpožděný.A(Oznámení odesláno).A(InCollection.Ne());var InvoiceCollection = Servis.GetInvoices();pro každého (var aktuální faktura v InvoiceCollection) { -li (SendToCollection.IsSatisfiedBy(aktuální faktura)) { aktuální faktura.SendToCollection(); }}
Kritiky
Specifikační vzor lze považovat za software anti-vzor:
- Nákladné programování - Chybí přesně definovaný účel tohoto vzoru a neexistuje žádný průvodce, kdy jej implementovat nebo ne. Viz také Zákon nástroje.
- Efekt vnitřní platformy - Funkce And (), která se přímo replikuje && v C#. Také Not () a potenciálně více. Viz také Objevování čtvercového kola.
- Špagety / Lasagne kód - Oddělte třídy pro každou část specifikace fragmentů, což by mohlo být soudržným objektem. Ve výše uvedeném příkladu je OverDue další vrstva mezi logikou pro
SendToCollection
aOverDueSpecification
implementace.
Většina přirozených programovacích jazyků může pojmout design řízený doménou s hlavními objektově orientovanými koncepty.
Alternativní příklad bez specifikačního vzoru:
var InvoiceCollection = Servis.GetInvoices();pro každého (var faktura v InvoiceCollection) faktura.SendToCollectionIfNecessary();// Způsoby fakturace:veřejnost prázdnota SendToCollectionIfNecessary(){ -li (ShouldSendToCollection()) SendToCollection();}soukromé bool ShouldSendToCollection() => aktuální faktura.Zpožděný && aktuální faktura.Oznámení odesláno && !aktuální faktura.InCollection;
Tato alternativa používá základní koncepty vlastností pouze pro získání, logiky podmínek a funkcí. Klíčovou alternativou zde jsou vlastnosti jen pro získání, které jsou pojmenovány tak, aby udržovaly jazyk řízený doménou a umožňovaly další používání přirozeného &&
operátor, místo specifikačního vzoru A()
funkce. Dále vytvoření dobře pojmenované funkce SendToCollectionIfNecessary
je potenciálně užitečnější a popisnější než předchozí příklad (který by také mohl být obsažen v takové funkci, kromě toho, že není přímo na objektu zjevně).
Reference
- Evans, Eric (2004). Design založený na doméně. Addison-Wesley. str. 224.
externí odkazy
- Specifikace Eric Evans a Martin Fowler
- Specifikační vzor: Primer Matt Berther
- Specifikační vzor: Úvod do čtyř částí pomocí VB.Net Richard Dalton
- Specifikační vzor v PHP podle Moshe Brevda
- Specifikace doktríny Happyr v PHP od Happyr
- Specifikační vzor v Swift Simon Strandgaard
- Vzor specifikace v TypeScript a JavaScript Thiago Delgado Pinto
- vzor specifikace ve flash ActionScript 3 autor: Rolf Vreijdenberger