Legacy code concepts II: the Seam Model

Hi audience,

According to my last post, there are some important concepts related to legacy code that it is worth writing down and, therefore, have a clearer picture. Sensing and separation were two of them, both concepts tightly related to breaking dependencies. Moving forward, in line with those, I’m going to write about the Seam Model, introduced by Michael C. Feathers in his book Working Effectively with Legacy Code.

The Seam Model


The Seam Model is a convenient name to represent the ways by which we can perform tests without producing code changes in order to get a class into a test harness.

A Seam

Quoting the author,
“A seam is a place where you can alter behaviour in your program without editing that place”
Important to highlight: alter behaviour without editing. So how can we change behaviour without changing the code? We can actually do it in different ways or manners so far:

Seam types

  • Preprocessing seam: used in languages that allow preprocessing, such as C++
  • Link Seam: used in compiled languages like C, Java, C++, C# when building up the executable.
  • Object Seam: Valid for all OO languages
The most versatile, IMHO, is the one that take advantage of OO features, such as polymorphism. I’m talking about the Object Seam. Others might be also important depending on the code and the language, it can be worth bearing them in mind when the time comes.

Enabling point for an Object Seam

The enabling point for a seam is the place in the code where the door is open to allow a change of behaviour. 

See example below:


public function getDiscountedAmount(array $discounts, $price): float
{
   // IDiscount $d param is the Enabling Point
    $f = function (IDiscount $d) use ($price) {
       if ($d->isActive()) {
           // calculateDiscountOn and isActive are object seams
           $r = $d->calculateDiscountOn($price);
       } else {
           $r = 0;
       }

       return $r;
   };

   $sum = function (array $array) use ($f) {
       $vAux = array_map($f, $array);
       return array_sum($vAux);
   };

   return $sum($discounts);
}

An array with Discount objects on it (all of them implementing the interface IDiscount) is used to calculate the total discount to apply on a net price. Discounts can be found as percentage, fixed rate or get N pay M where N>M therefore, each kind of Discount class has a different way to get maths done.

Here, the seam is the calculateDiscountOn method because we are able to pass an array of fake discounts which actually implement that method, and, on the other hand, the enabling point is the IDiscount $d parameter at the closure function because the injection into the closure is using an interface instead of a concrete class, allowing to use those fake discounts.


As a consequence, we can create a unit tests for getDiscountedAmount where the array of discounts can be made up of fake discounts just to get a result and be able to compare it to an expected value afterwards.

Comments

Popular posts from this blog

Bulk Inserts and the Performance Holy Grial (Part II)

Bulk Inserts and the Performance Holy Grial (Part I)