در اینجا، قابلیت هایی که یک ساختار composite می تواند به specification بدهد را بررسی می کنیم. specification را یک interface برای مدل کردن یک predicate در نظر بگیرید.
برای مثال یک interface داریم که یک entity را دریافت می کند و مشخص می کند که آیا این entity یک شرط را پاس می کند یه خیر.
public interface ISpecification<T> { public bool IsSatisfiedBy(T entity); }
یک predicate داریم که در مورد اعداد زوج صحبت می کند.
public class EvenNumbers : ISpecification<long> { public override bool IsSatisfiedBy(long entity) { return entity % 2 == 0; } }
و یک predicate داریم که در مورد اعداد مثبت می باشد.
public class PositiveNumbers : ISpecification<long> { public override bool IsSatisfiedBy(long entity) { return entity > 0; } }
بنابراین اجزای یک آرگومان منطقی را به ruleهای کوچک تبدیل می کنیم. هر کدام از این اجزا را می توانیم به عنوان یک leaf در نظر بگیریم.
حالا یک And Specification را در نظر بگیرید که دارای دو specification می باشد. این کلاس قسمت راست و چپ را دریافت می کند و در IsSatisfiedBy نتیجه آن ها را با هم and می کند.
public class AndSpecification<T> : ISpecification<T> { private readonly ISpecification<T> _leftSpec; private readonly ISpecification<T> _rightSpec; public AndSpecification(ISpecification<T> leftSpec, ISpecification<T> rightSpec) { _leftSpec = leftSpec; _rightSpec = rightSpec; } public override bool IsSatisfiedBy(T entity) { return _leftSpec.IsSatisfiedBy(entity) && _rightSpec.IsSatisfiedBy(entity); } }
زمانی که client یک specification را دریافت می کند، مهم نیست که یک leaf یا یک and را دریافت کرده است.
var rule = GetRules(); var result = rule.IsSatisfiedBy(10); private static ISpecification<long> GetRules(){ return new AndSpecification<long> (new EvenNumbers(), new PositiveNumbers()); }
در واقع چیزی که client می بیند، ISpecification می باشد که پشت آن می تواند یک ساختار flat و یا یک المان مشخص باشد.
در ادامه می توانم یک ساختار برای Or و یک ساختار برای Not اضافه کنم.
public class OrSpecification<T> : ISpecification<T> { private readonly ISpecification<T> _leftSpec; private readonly ISpecification<T> _rightSpec; public OrSpecification(ISpecification<T> leftSpec, ISpecification<T> rightSpec) { _leftSpec = leftSpec; _rightSpec = rightSpec; } public override bool IsSatisfiedBy(T entity) { return _leftSpec.IsSatisfiedBy(entity) || _rightSpec.IsSatisfiedBy(entity); } }
public class NotSpecification<T> : ISpecification<T> { private readonly ISpecification<T> _targetSpec; public NotSpecification(ISpecification<T> targetSpec) { _targetSpec = targetSpec; } public override bool IsSatisfiedBy(T entity) { return !_targetSpec.IsSatisfiedBy(entity); } }
ورود composite objectهای جدید به ما این امکان را می دهد که ساختار را هر طور که می خواهیم ایجاد کنیم.
یک predicate در مورد اعداد صفر نیز ایجاد می کنیم.
public class IsZeroSpecification : ISpecification<long> { public override bool IsSatisfiedBy(long entity) { return entity == 0; } }
و در لحظه استفاده می توانیم از ساختار دلخواه خود استفاده کنیم.
var rule = GetRules(); var result = rule.IsSatisfiedBy(10); private static ISpecification<long> GetRules() { var positiveEvenNumber = new AndSpecification<long> (new EvenNumbers(), new PositiveNumbers()); var zero = new IsZeroSpecification(); return new OrSpecification<long>(positiveEvenNumber, zero); }
بنابراین ساختار را در قالب یک درخت مدل کردیم.