Специфікація — це шаблон проєктування, який представляє бізнес логіку у вигляді ланцюжка об'єктів зв'язних операцій булевої логіки.
Переваги та недоліки
Переваги
- логіка фільтрації об'єктів винесена в окремі класи-специфікацій, які можна, без втрат в гнучкості системи, об'єднювати між собою
Недоліки
- важкий в реалізації
Опис мовою C#
Додамо деякі класи, які будуть симулювати реальні об'єкти.
public class User { public string Name { get; set; } public bool IsAdmin { get; set; } public override string ToString() { return $"{Name}. Admin = {IsAdmin}"; } }
Запишемо стандартну реалізацію, яку згодом покращимо для конкретної мови програмування.
public interface ISpecification<TEntity> { bool IsSatisfiedBy(TEntity entity); // об'єднання ISpecification<TEntity> And(ISpecification<TEntity> other); ISpecification<TEntity> Or(ISpecification<TEntity> other); ISpecification<TEntity> Not(); }
Додамо абстрактний клас, який дозволить нам об'єднювати специфікації в ланцюжки за допомогою операторів булевої логіки. У C# цей клас можна замінити на перевантаження операцій чи методами розширень до ISpecification.
public abstract class CompositeSpecification<TEntity> : ISpecification<TEntity> { public abstract bool IsSatisfiedBy(TEntity entity); public ISpecification<TEntity> And(ISpecification<TEntity> other) { return new AndSpecification<TEntity>(this, other); } public ISpecification<TEntity> Or(ISpecification<TEntity> other) { return new OrSpecification<TEntity>(this, other); } public ISpecification<TEntity> Not() { return new NotSpecification<TEntity>(this); } }
Реалізацій конкретних декораторів
public class AndSpecification<TEntity> : CompositeSpecification<TEntity> { private readonly ISpecification<TEntity> spec1; private readonly ISpecification<TEntity> spec2; public AndSpecification(ISpecification<TEntity> spec1, ISpecification<TEntity> spec2) { this.spec1 = spec1; this.spec2 = spec2; } public override bool IsSatisfiedBy(TEntity candidate) { return spec1.IsSatisfiedBy(candidate) && spec2.IsSatisfiedBy(candidate); } } public class OrSpecification<TEntity> : CompositeSpecification<TEntity> { private readonly ISpecification<TEntity> spec1; private readonly ISpecification<TEntity> spec2; public OrSpecification(ISpecification<TEntity> spec1, ISpecification<TEntity> spec2) { this.spec1 = spec1; this.spec2 = spec2; } public override bool IsSatisfiedBy(TEntity candidate) { return spec1.IsSatisfiedBy(candidate) || spec2.IsSatisfiedBy(candidate); } } public class NotSpecification<TEntity> : CompositeSpecification<TEntity> { private readonly ISpecification<TEntity> wrapped; public NotSpecification(ISpecification<TEntity> spec) { wrapped = spec; } public override bool IsSatisfiedBy(TEntity candidate) { return !wrapped.IsSatisfiedBy(candidate); } }
Припустимо, що виникли наступні задачі:
- знайти користувачів, за їх статусом
- знайти користувачів по імені, за введеним значенням
Тоді конкретні специфікації матимуть наступний вигляд
public class RoleSpecification : CompositeSpecification<User> { private readonly bool isUserAdmin; public RoleSpecification(bool isUserAdmin) { this.isUserAdmin = isUserAdmin; } public override bool IsSatisfiedBy(User entity) { return entity.IsAdmin == isUserAdmin; } } public class SearchByNameSpecification : CompositeSpecification<User> { private readonly string searchSubstring; public SearchByNameSpecification(string searchSubstring) { this.searchSubstring = searchSubstring; } public override bool IsSatisfiedBy(User entity) { return entity.Name.Contains(searchSubstring); } }
Використання матиме наступний вигляд:
// задана предметна область User[] users = new User[] { new User { IsAdmin = false, Name = "User 1" }, new User { IsAdmin = false, Name = "User 2" }, new User { IsAdmin = true, Name = "User 3" }, }; // конкретні специфікації ISpecification<User> roleSpecification = new RoleSpecification(isUserAdmin: false); ISpecification<User> nameSpecification = new SearchByNameSpecification(searchSubstring: "User"); // композиції специфікації ISpecification<User> andSpecification = nameSpecification.And(roleSpecification); ISpecification<User> orSpecification = nameSpecification.Or(roleSpecification); // результати вибірки Console.WriteLine("AND Specification"); foreach (User user in users) { if (andSpecification.IsSatisfiedBy(user)) { Console.WriteLine(user); } } Console.WriteLine("OR Specification"); foreach (User user in users) { if (orSpecification.IsSatisfiedBy(user)) { Console.WriteLine(user); } }
Покращена версія
При використанні із LINQ специфікації можна обгортати у функції, або ж забезпечити специфікації такою функціональністю:
// інтерфейс public interface ISpecification<TEntity> { bool IsSatisfiedBy(TEntity entity); Func<TEntity, bool> AsExpression(); . . . } // абстрактний клас public abstract class CompositeSpecification<TEntity> : ISpecification<TEntity> { public abstract Func<TEntity, bool> AsExpression(); public bool IsSatisfiedBy(TEntity entity) => AsExpression().Invoke(entity); . . . } // оператори булевої логіки public class AndSpecification<TEntity> : CompositeSpecification<TEntity> { . . . public override Func<TEntity, bool> AsExpression() { return (entity) => spec1.IsSatisfiedBy(entity) && spec2.IsSatisfiedBy(entity); } } // конкретні специфікації public class RoleSpecification : CompositeSpecification<User> { private readonly Func<User, bool> isUserAdminPredicate; public RoleSpecification(bool isUserAdmin) { this.isUserAdminPredicate = (user) => user.IsAdmin == isUserAdmin; } public override Func<User, bool> AsExpression() { return isUserAdminPredicate; } } // використання foreach (User user in users.Where(specification.AsExpression())) { Console.WriteLine(user); }
Див. також
Джерела
- Патерн проєктування Специфікація [Архівовано 19 вересня 2013 у Wayback Machine.]
- Специфікація, як заміна репозиторію [Архівовано 2 вересня 2019 у Wayback Machine.]
Вікіпедія, Українська, Україна, книга, книги, бібліотека, стаття, читати, завантажити, безкоштовно, безкоштовно завантажити, mp3, відео, mp4, 3gp, jpg, jpeg, gif, png, малюнок, музика, пісня, фільм, книга, гра, ігри, мобільний, телефон, android, ios, apple, мобільний телефон, samsung, iphone, xiomi, xiaomi, redmi, honor, oppo, nokia, sonya, mi, ПК, web, Інтернет
Specifikaciya ce shablon proyektuvannya yakij predstavlyaye biznes logiku u viglyadi lancyuzhka ob yektiv zv yaznih operacij bulevoyi logiki Shablon proyektuvannya u viglyadi UML diagramiPerevagi ta nedolikiPerevagi logika filtraciyi ob yektiv vinesena v okremi klasi specifikacij yaki mozhna bez vtrat v gnuchkosti sistemi ob yednyuvati mizh soboyu Nedoliki vazhkij v realizaciyiOpis movoyu C Dodamo deyaki klasi yaki budut simulyuvati realni ob yekti public class User public string Name get set public bool IsAdmin get set public override string ToString return Name Admin IsAdmin Zapishemo standartnu realizaciyu yaku zgodom pokrashimo dlya konkretnoyi movi programuvannya public interface ISpecification lt TEntity gt bool IsSatisfiedBy TEntity entity ob yednannya ISpecification lt TEntity gt And ISpecification lt TEntity gt other ISpecification lt TEntity gt Or ISpecification lt TEntity gt other ISpecification lt TEntity gt Not Dodamo abstraktnij klas yakij dozvolit nam ob yednyuvati specifikaciyi v lancyuzhki za dopomogoyu operatoriv bulevoyi logiki U C cej klas mozhna zaminiti na perevantazhennya operacij chi metodami rozshiren do ISpecification public abstract class CompositeSpecification lt TEntity gt ISpecification lt TEntity gt public abstract bool IsSatisfiedBy TEntity entity public ISpecification lt TEntity gt And ISpecification lt TEntity gt other return new AndSpecification lt TEntity gt this other public ISpecification lt TEntity gt Or ISpecification lt TEntity gt other return new OrSpecification lt TEntity gt this other public ISpecification lt TEntity gt Not return new NotSpecification lt TEntity gt this Realizacij konkretnih dekoratoriv public class AndSpecification lt TEntity gt CompositeSpecification lt TEntity gt private readonly ISpecification lt TEntity gt spec1 private readonly ISpecification lt TEntity gt spec2 public AndSpecification ISpecification lt TEntity gt spec1 ISpecification lt TEntity gt spec2 this spec1 spec1 this spec2 spec2 public override bool IsSatisfiedBy TEntity candidate return spec1 IsSatisfiedBy candidate amp amp spec2 IsSatisfiedBy candidate public class OrSpecification lt TEntity gt CompositeSpecification lt TEntity gt private readonly ISpecification lt TEntity gt spec1 private readonly ISpecification lt TEntity gt spec2 public OrSpecification ISpecification lt TEntity gt spec1 ISpecification lt TEntity gt spec2 this spec1 spec1 this spec2 spec2 public override bool IsSatisfiedBy TEntity candidate return spec1 IsSatisfiedBy candidate spec2 IsSatisfiedBy candidate public class NotSpecification lt TEntity gt CompositeSpecification lt TEntity gt private readonly ISpecification lt TEntity gt wrapped public NotSpecification ISpecification lt TEntity gt spec wrapped spec public override bool IsSatisfiedBy TEntity candidate return wrapped IsSatisfiedBy candidate Pripustimo sho vinikli nastupni zadachi znajti koristuvachiv za yih statusom znajti koristuvachiv po imeni za vvedenim znachennyam Todi konkretni specifikaciyi matimut nastupnij viglyad public class RoleSpecification CompositeSpecification lt User gt private readonly bool isUserAdmin public RoleSpecification bool isUserAdmin this isUserAdmin isUserAdmin public override bool IsSatisfiedBy User entity return entity IsAdmin isUserAdmin public class SearchByNameSpecification CompositeSpecification lt User gt private readonly string searchSubstring public SearchByNameSpecification string searchSubstring this searchSubstring searchSubstring public override bool IsSatisfiedBy User entity return entity Name Contains searchSubstring Vikoristannya matime nastupnij viglyad zadana predmetna oblast User users new User new User IsAdmin false Name User 1 new User IsAdmin false Name User 2 new User IsAdmin true Name User 3 konkretni specifikaciyi ISpecification lt User gt roleSpecification new RoleSpecification isUserAdmin false ISpecification lt User gt nameSpecification new SearchByNameSpecification searchSubstring User kompoziciyi specifikaciyi ISpecification lt User gt andSpecification nameSpecification And roleSpecification ISpecification lt User gt orSpecification nameSpecification Or roleSpecification rezultati vibirki Console WriteLine AND Specification foreach User user in users if andSpecification IsSatisfiedBy user Console WriteLine user Console WriteLine OR Specification foreach User user in users if orSpecification IsSatisfiedBy user Console WriteLine user Pokrashena versiya Pri vikoristanni iz LINQ specifikaciyi mozhna obgortati u funkciyi abo zh zabezpechiti specifikaciyi takoyu funkcionalnistyu interfejs public interface ISpecification lt TEntity gt bool IsSatisfiedBy TEntity entity Func lt TEntity bool gt AsExpression abstraktnij klas public abstract class CompositeSpecification lt TEntity gt ISpecification lt TEntity gt public abstract Func lt TEntity bool gt AsExpression public bool IsSatisfiedBy TEntity entity gt AsExpression Invoke entity operatori bulevoyi logiki public class AndSpecification lt TEntity gt CompositeSpecification lt TEntity gt public override Func lt TEntity bool gt AsExpression return entity gt spec1 IsSatisfiedBy entity amp amp spec2 IsSatisfiedBy entity konkretni specifikaciyi public class RoleSpecification CompositeSpecification lt User gt private readonly Func lt User bool gt isUserAdminPredicate public RoleSpecification bool isUserAdmin this isUserAdminPredicate user gt user IsAdmin isUserAdmin public override Func lt User bool gt AsExpression return isUserAdminPredicate vikoristannya foreach User user in users Where specification AsExpression Console WriteLine user Div takozhShabloni proyektuvannya programnogo zabezpechennya Ob yektno oriyentovane programuvannyaDzherelaPatern proyektuvannya Specifikaciya Arhivovano 19 veresnya 2013 u Wayback Machine Specifikaciya yak zamina repozitoriyu Arhivovano 2 veresnya 2019 u Wayback Machine