Design pattern “Specification”

Disclaimer

This article is mostly for beginners at programming, who know only some programming patterns or don’t know them at all.

About design patterns

Let’s take definition of design pattern from Wikipedia.

In software engineering, a design pattern is a general reusable solution to a commonly occurring problem in software design. A design pattern is not a finished design that can be transformed directly into code. It is a description or template for how to solve a problem that can be used in many different situations. Object-oriented design patterns typically show relationships and interactions between classes or objects, without specifying the final application classes or objects that are involved.

For instance we can imagine that we are accomplishing a task “by analogy”. Or, for example, solving the same equation, but with another concrete numbers.

Pattern “Specification”- is a pattern of supplement’s conduct. The result of the implementation will be a Boolean variable; giving you the input of the operator of conditional transfer you control the behavior of the program.

With the help of the following techniques you can:

  • Make your code more readable and concise
  • Avoid duplications of code
  • Easier to make changes to the implementation

As always, the work of the complex mechanism better shows a concrete example. Let’s start with simple things, gradually moving to the complex. I will try to avoid situations in which revealed only a simple example, but as it is applied in real life – unclear.

Areas of usage

A pattern “Specification” is useful with branching algorithms, when there are a lot of checks. When you do typical checks of conditions in different parts of the program. I suppose, on further examples, you’ll be able to fully experience where it is better to use this pattern.

So, let us suppose we have a class for goods Ware

public class Ware {
    public string Articul { get; set; }
    public string Name { get; set; }
    public DateTime ProduceDate { get; set; }
    public DateTime BestBefore { get; set; }
}

and we have a domain service WareCalculation, which in any way handles lists of goods. Suppose we have a method that allows calculating the number of goods, which are at the end of shelf life. In the simplest case, it will be disposed of as follows:

public class WareCalculation {
    public int CountExpiredWares(List<Ware> wares) {
         return wares.FindAll(w => w.BestBefore < DateTime.Now.AddMonths(4)).Count;
    }
}

To control the correctness of the subsequent refactoring and patterns, there should be tests. In our case, we just need this test:

[TestMethod]
public void ShouldCountExpiredWares() {
    var list = new List<Ware> {
        Create.Ware(w => w.BestBefore = DateTime.Now.AddMonths(6)),
        Create.Ware(),
        Create.Ware()
    };

    var expiredWare = new WareCalculation().CountExpiredWares(list);

    Assert.AreEqual(2, expiredWare);
}

This is our simple example in which I will show the main technique of using. The test used DSL (Domain Specific Language) that can be read about in my earlier articles. In the source code can be found its implementation.

In a domain method, we have the condition, with the help of which we find needful goods. This condition can be replaced by the specification. General view of the class specification is as follows:

public abstract class Specification<T>{
    public abstract bool IsSatisfiedBy(T item);
}

As you can see, the class Specification is parameterized and otherwise how to check the conditions in the method IsSatisfiedBy? We do not know what type of object would come to us as a parameter. In your own program you won’t use only one specification, but its using should be unified, what I mean is that it should be easy to pass as a parameter. This is why we needed to declare a class abstract.

Now we can refactor the method CountExpiredWares. To do this we will create a new class specification WareExpireDateSpecification.

public class WareNearExpireDateSpec: Specification<Ware> {
    public bool IsSatisfiedBy(Ware item) {
        return false;
    }
}

Remember, that “without test there is no code” and that’s why the method usually sent as a false, until we write tests.

[TestMethod]
public void WareNearExpireDateSpecificationTest() {
    Assert.IsTrue(new WareNearExpireDateSpec().IsSatisfiedBy (
                    Create.Ware(w => w.BestBefore = DateTime.Now)));

    Assert.IsFalse(new WareNearExpireDateSpec().IsSatisfiedBy (
                    Create.Ware(w => w.BestBefore = DateTime.Now.AddMonths(10))));

    Assert.IsTrue(new WareNearExpireDateSpec().IsSatisfiedBy (
                   Create.Ware(w => w.BestBefore = DateTime.Now.AddMonths(-10))));
}

And the realization of the specification:

public class WareNearExpireDateSpec : Specification<Ware> {
    public bool IsSatisfied(Ware item) {
        return item.BestBefore < DateTime.Now.AddMonths(4);
    }
}

The body of the domain method can now use the specification

public int CountExpiredWares(List<Ware> wares) {
    var wareNearExpireDateSpec = new WareNearExpireDateSpec();
    return wares.FindAll(wareNearExpireDateSpec.IsSatisfiedBy).Count;
}

In my opinion the readability of the method improved. But we will not stop and will continue to improve the specification.

Improving of «Specification» pattern

At this stage it may think that in general all is good, some excess of code is not even on the contrary can be seen the clearly increasing, but this is temporary. The number of the months to check is written hard (obviously) and with such an approach would be difficult to choose products that are better sent to a nearby supermarket for the quickest implementation, and which to send to a long journey.

In this case, a standard to test is passed to the constructor specification.

public class WareNearExpireDateSpec : ISpecification<Ware> {
    private readonly int monthsLeft;

    public WareNearExpireDateSpec(int monthsLeft) {
        this.monthsLeft = monthsLeft;
    }

    public bool IsSatisfied(Ware item) {
        return item.BestBefore < DateTime.Now.AddMonths(monthsLeft);
    }
}

Composition of Specifications

Suppose that we have a method in the domain service, which provides counts of goods by some criteria for a special stock.

public int CountSpecialWares(List<Ware> wares) {
    return wares.FindAll(w =>
           w.BestBefore < DateTime.Now.AddMonths(4) &&
           w.BestBefore > DateTime.Now.AddMonths(2) &&
           w.Articul.StartsWith("Special") &&
           w.ProduceDate > DateTime.Now.AddMonths(-2)
       ).Count;
}

Shares may be any number and have a variety of conditions, can also be a different number of conditions on the goods. Would he have to write countless methods with different input set of parameters or specifications? Not at all. Pattern “specification” will allow us to write about the following method code:

public int CountSpecialWares(List<Ware> wares, ISpecification<Ware> specification) {
    return wares.FindAll(specification.IsSatisfiedBy).Count;
}

The second parameter will be any combination of specifications, which can be used for the class Ware.

To combine the specifications suggest making 2 classes,

  • AndSpecification
  • OrSpecification

which will be the container for specific implementations of the pattern. They will be similar in many respects, so that one can immediately identify the base class for them CompositeSpecification <T>. This class is also can be used as abstract. It will contain a collection of specifications, methods for their addition and removal, as well as property that will return the content in the form of unedited collection. In the constructor can be submitted any number of specifications.

public abstract class CompositeSpecification<T> : Specification<T> {
    protected readonly List<Specification<T>> specifications;

    protected CompositeSpecification(params Specification<T>[] specifications) {
          this.specifications = new List<Specification<T>>(specifications);
    }

    public ReadOnlyCollection<Specification<T>> Specifications {
        get { return specifications.AsReadOnly(); }
    }

    public CompositeSpecification<T> Add(Specification<T> specification) {
        specifications.Add(specification);
        return this;
    }

    public CompositeSpecification<T> Remove(Specification<T> specification) {
        specifications.Remove(specification);
        return this;
    }
}

After that, classes AndSpecification and OrSpecification will be enough simple. In the first of classes will need to examine all the specifications and make sure that the transmission element simultaneously satisfies all the specifications.

public class AndSpecification<T> : CompositeSpecification<T> {

    public AndSpecification(params Specification<T>[] specifications) : base(specifications) { }

    public override bool IsSatisfiedBy(T obj) {
         return specifications.All(specification => specification.IsSatisfiedBy(obj));
    }
}

For the specification class OrSpecification check until the first successfully completed test condition.

public class OrSpecification<T> : CompositeSpecification<T> {
    public OrSpecification(params Specification<T>[] specifications) : base(specifications) { }

    public override bool IsSatisfiedBy(T obj) {
        return specifications.Any(specification => specification.IsSatisfiedBy(obj));
    }
}

I assume that you have written separate specifications for the conditions of the test at the beginning of this section. In any case, these specifications are in the annex to the article. An example of use can serve a test:

[TestMethod]
public void CountSpecialWaresSpec() {
     var list = new List<Ware> {
           Create.Ware(w => {
                 w.BestBefore = DateTime.Now.AddMonths(9);
                 w.Articul = "Specia...";
                 w.ProduceDate = DateTime.Now.AddMonths(-2);
               }),
          Create.Ware(),
          Create.Ware()
    };

    var actionSpec = new AndSpecification<Ware>(
            new WareArticulStartsWithSpec("Spec"),
            new WareExpireDateBetweenSpec(8, 10),
            new WareProduceDateMoreSpec(1));

    var expiredWare = new WareCalculation().CountSpecialWares(list, actionSpec);

    Assert.AreEqual(1, expiredWare);
}

In this approach you should do flexible user settings, the results of which would form a necessary set of specifications. While reading the code, you’ll be able to determine the meaning of checks without going into the branching conditions for checking a set of fields. In addition, the specification in real programs is realized also by more comprehensive checks which make you tired while writing. Even it will be more bored to go into them a month or two later.

Another example, which may be useful in reality – the searching by the pattern. Suppose you have a form to search for the goods, which are filled all or only some of the field. By clicking the “Search” button, you can create a new product with the information entered by the user and pass it with a parameter to the constructor specification.

When searching with the help of a database or other data source, you need only add a search specification.

The advantage of this approach is that while changing the domain object, you will need to change only the method IsSatisfiedBy in the pattern, rather than hunt around the code using the search and editing. If you miss a certain place, the compiler will not tell, because all the code is correct, all these fields exist and that there are new and they are essential to search, it do not care.

Best Known Methods (BKM)

Empty Specification

In practical null specification was helpful, which always returns true for any object.

public abstract class Specification<T> {
    public static Specification<T> Null = new NullSpecification<T>();

    // other code here
}

public class NullSpecification<T> : Specification<T> {
    public override bool IsSatisfiedBy(T obj) {
        return true;
    }
}

Overriding operators

To permanently unable to write AndSpecification or OrSpecification, you can use overloaded operators & and | respectively. So the form

var actionSpec = new AndSpecification<Ware>(
    new WareArticulStartsWithSpec("Spec"),
    new WareExpireDateBetweenSpec(8, 10),
    new WareProduceDateMoreSpec(1));

can be changed to

var actionSpec =
     new WareArticulStartsWithSpec("Spec") &
     new WareExpireDateBetweenSpec(8, 10) &
    new WareProduceDateMoreSpec(1);

The realization will be in the abstract class Specification

public static Specification<T> operator |(Specification<T> left, Specification<T> right) {
     return new OrSpecification<T>(left, right);
}

public static Specification<T> operator &(Specification<T> left, Specification<T> right) {
    return new AndSpecification<T>(left, right);
}

Extract Specification from the set of them

While working with composite specification is useful to know is whether or not a specification in the set or not. Might be a useful opportunity to get a specification of the set and edit it. For this case, we will create a method ExtractSpecification.

public TSpecification ExtractSpecification<TSpecification>() {
     return (TSpecification)(object)ExtractSpecification(typeof(TSpecification));
}

private Specification<T> ExtractSpecification(Type specificationType) {
    if (GetType() == specificationType) {
        return this;
    }

    if (this is AndSpecification<T>) {
        return ((AndSpecification<T>) this)
                   .Specifications
                   .Select(specification => specification.ExtractSpecification(specificationType))
                   .FirstOrDefault(superset => superset != null);
    }

    return null;
}

This is the example of using and checking with the test:

[TestMethod]
public void CanExtractSpecification() {
    var specification = new AndSpecification<object>();
    var actual = new AlwaysTrueCriterion();

    specification
        .Add(actual)
        .Add(new AlwaysFalseCriterion());

    var extracted = specification.ExtractSpecification<AlwaysTrueCriterion>();

    Assert.AreEqual(extracted, actual);
}

So, this is all what I wanted to tell you about specifications. As usual, the source code of the project with examples can be easily download and play with the code

Hard’n’Heavy!

Tagged . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>