The Specification pattern is a very powerful design pattern which can be used to remove a lot of cruft from a class’s interface while decreasing coupling and increasing extensibility. It’s primary use is to select a subset of objects based on some criteria, and to refresh the selection at various times. Take a look at the example below:
Interface: IUser
1 2 3 4 5 6 7 8 9 | namespace Specification { public interface IUser { string FirstName { get; set; } string LastName { get; set; } int Age { get; set; } } } |
Class: SelectFromList
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | namespace Specification { public class SelectFromList { private readonly List<IUser> _Users; public SelectFromList() { _Users = new List<IUser>(); _Users.Add(new User() { FirstName="Mark", LastName="Nijhof", Age=31 }); _Users.Add(new User() { FirstName = "Mona", LastName = "Nijhof", Age = 30 }); _Users.Add(new User() { FirstName = "Milo", LastName = "Nijhof", Age = 2 }); _Users.Add(new User() { FirstName = "Thalia", LastName = "Nijhof", Age = 1 }); } public List<IUser> GetUsers() { List<IUser> result = new List<IUser>(); foreach (IUser user in _Users) { if (user.FirstName.StartsWith("M") && (user.Age > 10 || user.Age < 2)) { result.Add(user); } } return (result); } } } |
Interface: ISpecification
1 2 3 4 5 6 7 8 9 10 11 | namespace Specification { public interface ISpecification { bool IsSatisfiedBy(IUser candidate); ISpecification And(ISpecification other); ISpecification Or(ISpecification other); ISpecification Not(ISpecification other); } } |
Class: Specification : ISpecification
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | namespace Specification { public class Specification : ISpecification { public virtual bool IsSatisfiedBy(IUser candidate) { throw new NotImplementedException(); } public ISpecification And(ISpecification other) { return new AndSpecification(this, other); } public ISpecification Or(ISpecification other) { return new OrSpecification(this, other); } public ISpecification Not(ISpecification other) { return new NotSpecification(this, other); } } } |
Class: AndSpecification : Specification
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | namespace Specification { public class AndSpecification : Specification { private readonly ISpecification _One; private readonly ISpecification _Other; public AndSpecification(ISpecification one, ISpecification other) { _One = one; _Other = other; } public override bool IsSatisfiedBy(IUser candidate) { return _One.IsSatisfiedBy(candidate) && _Other.IsSatisfiedBy(candidate); } } } |
Class: OrSpecification : Specification
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | namespace Specification { public class OrSpecification : Specification { private readonly ISpecification _One; private readonly ISpecification _Other; public OrSpecification(ISpecification one, ISpecification other) { _One = one; _Other = other; } public override bool IsSatisfiedBy(IUser candidate) { return _One.IsSatisfiedBy(candidate) || _Other.IsSatisfiedBy(candidate); } } } |
Class: NotSpecification : Specification
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | namespace Specification { public class NotSpecification : Specification { private readonly ISpecification _One; private readonly ISpecification _Other; public NotSpecification(ISpecification one, ISpecification other) { _One = one; _Other = other; } public override bool IsSatisfiedBy(IUser candidate) { return _One.IsSatisfiedBy(candidate) && !_Other.IsSatisfiedBy(candidate); } } } |
Class: StartWith : Specification
1 2 3 4 5 6 7 8 9 10 | namespace Specification { public class StartWith : Specification { public override bool IsSatisfiedBy(IUser candidate) { return (candidate.FirstName.StartsWith("M")); } } } |
Class: OlderThan : Specification
1 2 3 4 5 6 7 8 9 10 | namespace Specification { public class OlderThan : Specification { public override bool IsSatisfiedBy(IUser candidate) { return (candidate.Age > 10); } } } |
Class: JongerThan : Specification
1 2 3 4 5 6 7 8 9 10 | namespace Specification { public class JongerThan : Specification { public override bool IsSatisfiedBy(IUser candidate) { return (candidate.Age < 2); } } } |
Class: SelectFromList
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | namespace Specification { public class SelectFromList { private readonly List<IUser> _Users; public SelectFromList() { _Users = new List<IUser>(); _Users.Add(new User() { FirstName="Mark", LastName="Nijhof", Age=31 }); _Users.Add(new User() { FirstName = "Mona", LastName = "Nijhof", Age = 30 }); _Users.Add(new User() { FirstName = "Milo", LastName = "Nijhof", Age = 2 }); _Users.Add(new User() { FirstName = "Thalia", LastName = "Nijhof", Age = 1 }); } public List<IUser> GetUsers(ISpecification specification) { List<IUser> result = new List<IUser>(); foreach (IUser user in _Users) { if (specification.IsSatisfiedBy(user)) { result.Add(user); } } return (result); } } } |
Usage
1 2 3 4 5 | ISpecification startsWith = new StartWith(); ISpecification olderThan = new OlderThan(); ISpecification jongerThan = new JongerThan(); SFL.GetUsers(startsWith.And(olderThan.Or(jongerThan))); |
I hope you found these examples helpful. Also take a look at the following links:
http://www.martinfowler.com/apsupp/spec.pdf
http://www.mattberther.com/2005/03/25/the-specification-pattern-a-primer/
http://en.wikipedia.org/wiki/Specification_pattern
No comments:
Post a Comment