Click here to Skip to main content
15,895,192 members
Articles / Programming Languages / C#

Specification

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
6 Feb 2010CPOL2 min read 7.4K   5  
Specification

Introduction

Generally saying, Specification is a predicate that determines if an object does or does not satisfy some criteria. By using Specifications, you could easily recombine business logic together using boolean logic.

Have you ever thought that bool TryParse(string s, out int result) could be seen as some pattern? Yes, we could talk about this method like about specification of integer represented in string, and this is validation usage. This pattern also could be used not only for Validation, but also for Querying and Building purposes.

Let us imagine that we need to validate if some Patient is eligible for drugs procedures at home provided by nurse.

So this, we will need two specifications. If they are both satisfied, we could say that this patient is eligible for drugs procedures at home.

First Specification

C#
public class EligibleForDrugs : ISpecification
{
    public bool IsSatisfiedBy(Patient patient)
    {
        return patient.IsActive && patient.HasPayer;
    }
}

Second Specification

C#
public class EligibleForNurseVisit : ISpecification
{
    public bool IsSatisfiedBy(Patient patient)
    {
        return patient.IsActive && patient.IsAtHome;
    }
}

As you guess, ISpecification interface looks like:

C#
internal interface ISpecification
{
    bool IsSatisfiedBy(Patient patient);
}

Usage could look like:

C#
public List<Patient> FetchPatientsForVisitWithDrugs(List<Patient> patients)
{
    var eligiblePatients = new List<Patient>();
    ISpecification drugsSpec = new ElegibleForDrugs();
    ISpecification nurseVisit = new ElegibleForNurseVisit();

    foreach (var patient in patients)
    {
        if(drugsSpec.IsSatisfiedBy(patient) && nurseVisit.IsSatisfiedBy(patient))
        {
            eligiblePatients.Add(patient);
        }
    }
    return eligiblePatients;
}

You could say that we can put all verification in our method FetchPatientsForVisitWithDrugs. Yes, but this is not right way, because your specifications could be used in different locations and also if see this from DDD perspective, you always need to bring concept things into the light.

Another question: Don't you see this syntax:

C#
if(drugsSpec.IsSatisfiedBy(patient) && nurseVisit.IsSatisfiedBy(patient))
to be boring syntax? Yes, especially if you have many specifications.

Let Us Improve Our Design

First, we will add some methods to our interface like here:

C#
 public interface ISpecification
 {
     bool IsSatisfiedBy(Patient patient);
     ISpecification And(ISpecification secondSpec);
     ISpecification Or(ISpecification secondSpec);
     ISpecification Not(ISpecification secondSpec);
}

And also, we will add CompositeSpecification which will be abstract base class for our two existing.

C#
public abstract class CompositeSpecification : ISpecification
{
    public abstract bool IsSatisfiedBy(Patient patient);

    public ISpecification And(ISpecification secondSpec)
    {
        return new AndSpecification(this, secondSpec);
    }

    public ISpecification Or(ISpecification secondSpec)
    {
        return new OrSpecification(this, secondSpec);
    }

    public ISpecification Not(ISpecification secondSpec)
    {
        return new NotSpecification(secondSpec);
    }
}

classes returned by different method of this new CompositeSpecification are used to combine few specifications in order to build a new complicated one. They could look simple like this AndSpecification class:

C#
public class AndSpecification : CompositeSpecification
{
    private ISpecification firstOne;
    private ISpecification secondOne;
    public AndSpecification(ISpecification firstSpec, ISpecification secondSpec)
    {
        firstOne = firstSpec;
        secondOne = secondSpec;
    }

    public override bool IsSatisfiedBy(Patient patient)
    {
        return firstOne.IsSatisfiedBy(patient) && secondOne.IsSatisfiedBy(patient);
    }
}

Let's move to our existing specifications and how they changed with our new design:

C#
public class EligibleForDrugs : CompositeSpecification
{
    public override bool IsSatisfiedBy(Patient patient)
    {
        return patient.IsActive && patient.HasPayer;
    }
}

This all gives us the ability to have build specifications querying in more sweet way.

New Usage

C#
ISpecification specification = new EligibleForDrugs()
                                           .And(new EligibleForNurseVisit());

Now, we work with this new specification as with simple single one: if(specification.IsSatisfiedBy(patient)) With other specifications like OrSpecification and NotSpecification, we could build more complicated queries.

For example, I will show some possible usage of this in Nhibernate:

C#
public DetachedCriteria PatietQuery(DetachedCriteria criteria)
{
    criteria.Add(criteria.EqualTo("patient.IsActive", true));

    criteria.Add(
            Expression.Not(
                Expression.Or(
                    Expression.Eq("patient.Type", PatientStatus.Discharge),
                    Expression.Eq("patient.Type", PatientStatus.Death)
                )
            )
        );
    return criteria;
}

Advantages of the Specification

  • Specification declares requirements to the output, but does not expose how those results are reached.
  • Rules are defined explicitly. This means that developer could know what to expect from the specification even without knowledge of how that is realized.
  • You get flexible interface, which could be easily enhanced. You also could build your composite specifications for querying.
  • Another good advantage is the possibility to test everything easily. You just define fail, non-fail states of the object and verify by checking boolean result.
This article was originally posted at http://andriybuday.blogspot.com/feeds/posts/default

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer SoftServe
Ukraine Ukraine
I'm very pragmatic and self-improving person. My goal is to become successful community developer.
I'm young and love learning, these are precondition to my success.

Currently I'm working in dedicated Ukrainian outsourcing company SoftServe as .NET developer on enterprise project. In everyday work I'm interacting with lot of technologies which are close to .NET (NHibernate, UnitTesting, StructureMap, WCF, Win/WebServices, and so on...)

Feel free to contact me.

Comments and Discussions

 
-- There are no messages in this forum --