Daily Challenge #246 - Readable Specification Pattern

dev.to staff - May 21 '20 - - Dev Community

Specification pattern is one of the OOP design patterns. Its main use is combining simple rules into more complex ones for data filtering. For example, given a bar menu you could check whether a drink is a non-alcoholic cocktail using the following code:

drink = # ...

is_cocktail = IsCocktail()

is_alcoholic = IncludesAlcohol()

is_non_alcoholic = Not(is_alcoholic)

is_non_alcoholic_cocktail = And(is_cocktail, is_non_alcoholic)

is_non_alcoholic_cocktail.is_satisfied_by(drink)

But, this code is terrible! We have to create lots of unnecessary variables, and if we were to write this specification in one line, the result would be getting more and more unreadable as its complexity grows:

drink = # ...

And(IsCocktail(), Not(IncludesAlcohol())).is_satisfied_by(drink)

You don't want to write such code. Instead, you should implement a much more beautiful specification pattern by:

  • using the &, |, ~ operators instead of creating superfluous And, Or, Not classes
  • getting rid of those annoying class instantiations
  • calling the specification directly without any is_satisfied_by methods

Make something like this instead:

drink = # 
...(IsCocktail & ~IncludesAlcohol)(drink)

To do so you have to create a class Specification which will be inherited by other classes (like aforementioned IsCocktail or IncludesAlcohol), and will allow us to do this magic.

Note: whenever a class inheriting from Specification (e.g. IsCocktail) or a complex specification (e.g. IsCocktail & ~IncludesAlcohol) is called, instead of constructing and initializing a truthy/falsey instance, True/False must be returned.

Try it out and tell us what you've made!


This challenge comes from FArekkusu on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .