Sympriser

Synergising the Enterprise through Innovation & IT

Archive for the ‘Programming’ Category

Revisiting Fowler’s Video Store: Refactoring Code, Refining Abstractions

with 2 comments

Refactoring Book

Refactoring techniques and Design Patterns have been extensively propagated and advocated for over a decade now. Why we still find hard to change, anaemic or overly complex object oriented structures? What is missing in our design practices?

In this series, we use Fowler’s didactic Video Store program to show practical advanced refactoring strategies that improve design simplicity, expressiveness and flexibility beyond standard object-oriented practice.

Refactoring and Design Patterns

Refactoring is a modularity improving technique that is essential for any evolutionary software development process and indispensable in the skill set of any professional software developer.

In the extremely influential book “Refactoring: Improving the Design of Existing Code”, Martin Fowler defines Refactoring as “a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behaviour”.

Fowler’s Refactoring book and the GoF Design Patterns book have played a fundamental transformational role on the professional community, educating a generation of software developers on their move from the procedural to the object oriented design paradigm. More recently, Joshua Kerievsky published Refactoring to Patterns, leading refactoring to a new level of abstraction by explicitly combining both approaches.

Despite all these resources, why we still see complex object oriented structures, contrary to the objectives of simplicity, expressiveness and flexibility, also implied by the refactoring definition?

What is Missing?

High quality software designs are made of modular and expressive abstractions.

Refactoring and design patterns are misused by a large number of software developers. There is an excessive focus at the syntactical level. A broader and deeper perspective is needed when analysing modularity problems and directing the refactoring process towards greater relevance to its design objectives.

The missing points are:

  1. each refactoring step is an opportunity for refining abstractions;
  2. abstraction refinements should improve both modularity and expressiveness.

In summary: refactoring processes should consider the program’s context and domain semantics to explicitly rethink both its design structure and abstractions at each refactoring step.

Practical Strategies for Effective Design Improvement

Effective refactoring transcends the program’s text and syntax to improve modularity and keenly enhance simplicity, expressiveness and flexibility.

Fowler’s book introduces refactoring techniques with a small program for a video store, whose main function is to print a statement of a customer’s charges and his frequent renter points.

In this series, we use Fowler’s very simple and rich didactic example to illustrate the following refactoring strategies:

  1. Adopt Object-Oriented Modules. Apply proper object-oriented design to reduce syntactical noise and make programs easier and safer to work with. Prepare your program to implement most new requirements by small and focused interventions, without generating unintended side effects.
  2. Reveal the Relevant Domain Concepts. Listen carefully the domain experts to reduce semantic noise by making their relevant concepts explicit and evident in all design expressions.
  3. Align with Contextual Variance. Confront program invariants with contextual variances to help make design decisions that improve consistency and flexibility.
  4. Build Resonant Abstractions.

These refactoring strategies have been consistently beneficial to our software design practice over the years. In case you haven’t yet, we strongly suggest you try them in your daily software development activities. We will be very happy to listen to your feedback with ideas and experience about software design and refactoring.

Written by Rafael Peixoto de Azevedo

April 28, 2009 at 9:42 am

Revisiting Fowler’s Video Store: Variants and Invariants

without comments

Glove

Glove: supple and protective

“Nothing endures but change.”

Heraclitus (540-480 BC)

“A well-worn glove becomes supple at the points where the fingers bend, while other parts are stiff and protective.”

Domain-Driven Design, by Eric Evans

In our design and refactoring efforts, we can’t afford to join the epistemological quest for Heraclitus’ supreme Logos – the deep rational principle that rules all changes in the universe.

We can and should, however, reason about how change affects consistency in our software designs. For stateful programs, such as object-oriented software, this necessarily brings us to understand its variants and invariants and how they are translated to design flexibility and coherence in the face of contextual variance.

Our objective is to reflect on how to design software that fits our domain like a well-worn glove. Simple, flexible and durable. Easy to work with. Capable of bending without breaking where we most need. Consistent and adaptive in the face of change.

Program’s Invariants in the Face of Contextual Variance

An invariant is a logical expression that remains true throughout a sequence of transformations. Invariant analysis is traditionally employed for software design on a textual basis. Invariants are defined or identified and then verified for representation structures, iterations, objects and classes.  Here, we want to go beyond what is present in the software texts (the programs themselves).

This post resumes the Revisiting Fowler’s Video Store series. After making the relevant domain concepts explicit, we focus on another aspect of domain semantics, studying contextual variance under the perspective of the following issues:

  1. The Passage of Time;
  2. Changes in the Video Classification;
  3. Changes in Rental Prices and Terms.

The Passage of Time

The first variance in the context of any software system is the inexorable passage of time. In our case, it affects directly the video store rental periods, which are represented by a record of the number of days a video has been rented. This representation is not persistent: it needs to be incremented at the beginning of each business day, otherwise it would become invalid. Therefore, the representation is not consistent because it is incoherent with the program’s context.

We propose to replace the number of days by the initial rental date and to calculate the rental period through date arithmetic. The refactoring is internal to the Rental class, so there is no need to change its specification (or published interface).

The consistent rental period calculation can be readily implemented using the new RentalCalendar class, that applies the time point pattern with day granularity specifically for video rental business calendar. The alternative Rental class constructors and getDaysRented method are shown in the figure below.

public Rental(Video video, RentalCalendar initialDate) {

    _video = video;
    _initialDate = initialDate;
}

public Rental(Video video, int daysRented) {

    _video = video;
    _initialDate = RentalCalendar.pastDay(daysRented);
}

public int getDaysRented() {

    return _initialDate.daysTo(RentalCalendar.today());
}

Two additional reflections on this refactoring:

  1. the initial rental date is another relevant domain concept that we made explicit in our program;
  2. it confirms again an obvious truth: we should only persist what is persistent.

Changes in the Video Classification

Both the initial Video Store dicdatic program and its purely syntactical refactoring use a static classification scheme for pricing information. In the initial program, each video is  associated with a price code; purely syntactical refactoring replaces this association with an association to a price subclass.

There is an apparent contradiction in this model that should be questioned and clarified:

“Do New Releases stay in this category for ever?”

If the domain expert answers “No. Of course not.”, then the next question should be:

“If a video is reclassified, how does it affect the calculations for its current rentals?”

Programs must be perfectly adherent to all relevant business rules. If the domain experts affirm the calculations should remain the same as when the customer rented the video, we should transfer the pricing information from the  Video class to the Rental class, because it is transient to Video and persistent to Rental.

In the previous post, we introduced the Conditions class, created to represent the domain concepts that regulate the price and frequent renter points. Now we just need to adapt the Rental class constructors and its getCharge method, as shown in the following program fragment.

public Rental(Video video, RentalCalendar initialDate, Conditions conditions) {

    _video       = video;
    _initialDate = initialDate;
    _conditions  = conditions;
}

public Rental(Video video, int daysRented, Conditions conditions) {

    _video       = video;
    _initialDate = RentalCalendar.pastDay(daysRented);
    _conditions  = conditions;
}

public double getCharge() {

    return _conditions.getCharge(this.getDaysRented());
}

Changes in Rental Prices and Terms

I enjoy watching a video every week at home. I usually rent two or three for my family at my local video store. This week I saw something that made me rent seven videos! Look yourself at the picture below.

Simultaneous Rentals Promotion

Simultaneous Rentals Promotion

I think the store owner finally understood my needs and the result is more business and satisfaction. For us, here, this is a good example of change in rental prices and terms. For regular and children’s videos, the rental conditions now depend on the number of simultaneous rentals. Each additional video rent extends the rental term by one day. So, the initial term is 3 days for 3 videos, 4 days for 4 videos and so on up to 7 videos.

In the original didactic program the prices and terms of rental for each type of video are expressed as literals embedded in the calculation formulas. Standard object-oriented design practice refactors the conditional expressions into a specialisation solution based on inheritance and polymorphism that implements the strategy design pattern through a class hierarchy.

We replaced the conditional expressions by parameterised unified expressions, where the parameters are domain relevant concepts, originally implicit and then explicitly articulated as rental conditions. The program fragment below shows how easily the rental conditions value object can be moulded to face the changes in rental terms.

public Collection<Rental>
simultaneousPromotion (Collection<Video> videoCollection, RentalCalendar initialDate) {

    int simultaneous = videoCollection.size();
    List<Rental> rentalCollection = new LinkedList<Rental>();

    if (simultaneous >= 3 && simultaneous <= 7) {

        int initialTerm = simultaneous;
        Conditions conditions = new Conditions(initialTerm, 3.00, 1.50, 1);

        for (Video eachVideo : videoCollection) {

            Rental rental = new Rental(eachVideo, initialDate, conditions);
            rentalCollection.add(rental);
        }
    }

    return rentalCollection;
}

As we can see, there was no need to change the calculation logic. The refactoring solution based on making relevant concepts explicit is not only simpler and more expressive, but also and more flexible and consistent in the face of contextual variance.

Conclusions

Confronting the program invariants with relevant contextual variances illuminates the refactoring process. As usual, we should always listen the domain experts, strive for simplicity and use a maintenance need as an opportunity for refactoring.

In this post, we brought contextual variance to our refactoring process. Working through the program invariants in the face of contextual variance generated two contributions for our refactoring of the Video Store program:

  1. it made apparent the need to refactor the representation for the rental periods;
  2. it confirmed the value object based refactoring of the program’s conditional expressions.

Refactoring for coherence with relevant contextual variances improves the software design in terms of consistency and flexibility. Just like an well worn glove, our refactored program is now more consistent and flexible exactly where it makes most sense.

Design coherence at the syntactical level is achieved by colocating data and functions responsibilities. At semantic level, coherence means consistency and flexibility in the face of domain dynamics. This requires deeper thinking about the relevant contextual variance and the relevant domain concepts.

Written by Rafael Peixoto de Azevedo

April 24, 2009 at 1:21 pm

Revisiting Fowler’s Video Store: Making Implicit Concepts Explicit

with 5 comments

It's not just a Gear, it is a Clock!

Not just a Gear, a Clock!

“A deep model contains the central concepts and abstractions that can succinctly and flexibly express essential knowledge of the users’ activities, their problems and their solutions.”

“Ultimately … this should make the software more in tune with the way the domain experts think and more responsive to the users’ needs”

Domain-Driven Design, by Eric Evans

Domain-Driven Design situates the domain model in a central and convergent role, tightly articulating semantics and syntax for problem definition, solution design and software implementation.

In this post, we bring the domain semantics to our refactoring process. We want a deeper perspective to analyse modularity problems and to direct improvements towards greater relevance to our design objectives. So, let’s resume the Fowler’s Video Store refactoring and explore alternative design decisions based on greater emphasis on model semantics.

Fowler’s Video Store didactic program has two main functions: rental calculations and customer statement printing. This post focuses on the domain concepts that are most relevant to the rental calculations. Next posts will explore additional design alternatives for rental calculations and customer statement printing.

Initial Rental Calculations Design

Rental calculations include charge and frequent renter points. Both calculations are a function of the rental period and whether the video is either a new release, for children or regular.

The initial program defines a price code attribute for each video; this information is used to select the right calculation formulas embedded in switch programming statements inside the customer statement method. The complexity of such design grows as new price codes are added. It becomes specially dangerous if the statement method is duplicated to implement the HTML format.

Refactoring with Inheritance and Polymorphism

As we saw previously, the refactoring book evolves this design solution first by extracting the calculations to the methods getCharge and getPoints and moving them to the Video class.  This eliminates the need of duplicating the formulas when creating a new statement method.

The next step is to isolate the formulas from the Video class, replacing the price code with a new Price class. This class is then further specialised into the NewRelease, Children or Regular subclasses, so that the price code can be replaced by a polymorphic Price reference to an object of the respective subclass.

The following figure shows the design solution adopted by the refactoring book.

Video Store - Price Polymorphism

Video Store - Price Specialisation

The recommended refactoring improves modularity by using inheritance and polymorphism to adopt a typical object oriented syntax, that implements the strategy design pattern. The refactoring steps end up constituting the Replace Conditional Logic with Strategy refactoring pattern.

It looks like the right way to go. Doesn’t it? Well, let’s explore a design alternative.

Finding Relevant Implicit Concepts

The rental model for prices and terms is implicitly codified in the following formulas scattered along the Price class hierarchy implementation:

regularCharge = 2.0;
if (daysRented > 2) regularPrice += (daysRented - 2) * 1.5;

childrensCharge = 1.5;
if (daysRented > 3) childrensCharge += (daysRented - 3) * 1.5;

newReleaseCharge = daysRented * 3;

If we look closely enough at these formulas, we discover they are actually three instances of the same formula:

charge = initialPrice;
if (daysRented > initialTerm) charge += (daysRented - initialTerm) * extraPricePerDay;

The implicit concepts of initial term, initial price and extra price per day have just emerged as relevant to our design! They define a clearer, explicit, flexible and unified pricing model, replacing three static functions by a single meaningfully parameterised function. The same is true for the frequent renter points calculations. The emerged concepts can be articulated as rental conditions.

Naturally, all relevant domain abstractions must be institutionalised as part of the language effectively practised in the business and expressed in the software. Therefore, emerging concepts must be presented, discussed, reviewed where necessary and validated with the business domain experts.

Making the Relevant Concepts Explicit in the Design

As part of this this convergence process, we propose to explicitly define the Conditions value object as a substitute for the Price class hierarchy. The parameters initial term, initial price, extra price and extra points are invariant for regular, children’s and new releases. Therefore they can be defined at construction time:

Conditions regular    = new Conditions(2, 2.0, 1.5, 0);
Conditions childrens  = new Conditions(3, 1.5, 1.5, 0);
Conditions newRelease = new Conditions(1, 3.0, 3.0, 1);

The parameter daysRented is defined at calling time for the functions getCharge and getPoints.

public class Conditions {

  private final int    _initialTerm;
  private final double _initialPrice;
  private final double _extraPricePerDay;

  private final int    _INITIAL_POINTS = 1;
  private final int    _extraPoints;

  public Conditions (int initialTerm, double initialPrice, double extraPricePerDay, int extraPoints) {

    _initialTerm      = initialTerm;
    _initialPrice     = initialPrice;
    _extraPricePerDay = extraPricePerDay;
    _extraPoints      = extraPoints;
  }

  public double getCharge (int daysRented) {

    double charge = initialPrice;
    if (daysRented > initialTerm) charge += (daysRented - initialTerm) * extraPricePerDay;
    return (charge);
  }

  public int getPoints (int daysRented) {

    int points = _INITIAL_POINTS;
    if (daysRented > initialTerm) points += _extraPoints;
    return (points);
  }
}

After all changes the new domain model implementation is:

Video Store - Rental Conditions

Video Store - Rental Conditions

Conclusions

Refactoring at the semantic level improves the matching between business, model and implementation, adds flexibility where needed and addresses the major stakeholders’ concerns. As usual, we should always listen the domain experts, strive for simplicity and use a maintenance need as an opportunity for refactoring.

The initial program calculates rental charges and frequent renter points as conditional expressions. Standard object oriented design practice focuses on the conditions to specialise the expressions and implement the strategy design pattern for each calculation. This post focuses instead on the expressions themselves to identify the relevant domain concepts that generalise the expressions.

Bringing domain semantics to the process enabled us to propose a Extract Value Object refactoring, as an alternative to the Replace Conditional Logic with Strategy refactoring, with the following benefits:

  • decoupling: duplication in the multiple formulas is eliminated;
  • expressiveness: changes can be more clearly and easily applied;
  • simplicity: the program’s design structure is simplified.

Programs that instantiate an Anaemic Domain Model can be greatly improved by simple and straightforward refactoring aimed at adopting proper object oriented syntax. Additional improvements, however, require deeper thinking and carefully considering the domain model semantics in order to select the course of action from multiple design options.

Picture by Readerwalker’s photo stream. Original at Flickr.

Written by Rafael Peixoto de Azevedo

March 31, 2009 at 10:39 am

Revisiting Fowler’s Video Store: Adopting Object Oriented Syntax

without comments

Refactoring Book

Have you ever got a program that you needed to add, change or even fix a simple function, but you just didn’t know where to start or were afraid of breaking the program’s logical consistency?

Refactoring is a modularity improving technique that is indispensable in the skill set of any professional software developer. In this post, we use Refactoring to effectively adopt an object-oriented design syntax and make a program easier and safer to work with.

The extremely influential book “Refactoring: Improving the Design of Existing Code” introduces the technique with a small didactic program for a video store, whose main function is to print a statement of a customer’s charges and his frequent renter points.

The Initial Program

The initial program was structured with classes representing the domain concepts of Customer, Rental and Video. The statement method in the Customer class is reproduced below (updated to current Java syntax).

    public String statement() {

        double      totalAmount  = 0;
        int         renterPoints = 0;
        String      result       = "Rental Record for " + getName() + "\n";

        for (Rental each : _rentals) {

            double thisAmount = 0;

            // determine amounts for each line
            switch (each.getVideo().getPriceCode()) {
                case Video.REGULAR:
                    thisAmount += 2;
                    if (each.getDaysRented() > 2) {
                        thisAmount += (each.getDaysRented() - 2) * 1.5;
                    }
                    break;
                case Video.NEW_RELEASE:
                    thisAmount += each.getDaysRented() * 3;
                    break;
                case Video.CHILDRENS:
                    thisAmount += 1.5;
                    if (each.getDaysRented() > 3) {
                        thisAmount += (each.getDaysRented() - 3) * 1.5;
                    }
                    break;
            }

            // add frequent renter points
            renterPoints++;

            // add bonus for a two day new release rental
            if ((each.getVideo().getPriceCode() == Video.NEW_RELEASE) &&
                (each.getDaysRented() > 1)) {
                    renterPoints++;
            }

            // show figures for this rental
            result += "t" + each.getVideo().getTitle() +
                      "t" + String.valueOf(thisAmount) + "\n";
            totalAmount += thisAmount;
        }

        // add footer lines
        result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
        result += "You earned " + String.valueOf(renterPoints) +
                  " frequent renter points";

        return result;
    }

According to authors Martin Fowler and Kent Beck, a long method is a “bad smell in code” indicating “there is trouble that can be solved by a refactoring“. A quick analysis reveals the method is directly responsible for all of the following functions:

  • calculating the charge and frequent renter’s points for each customer’s rentals;
  • calculating the total charge and frequent renter’s points for the customer;
  • generating and formatting the statement.

A throughout program analysis confirms there is a modularity problem: it is not really an object oriented design. The classes are just data structures and the program’s main functionality is lumped into one single method. Take a look at the class diagram.

Video Store - Initial Class Diagram

Video Store - Initial Class Diagram

The dissociation between data and function responsibilities in a object oriented design is a modularity problem at the syntactic level. It means there is a problem in how the modules are composed and combined to form the program. This type of problem is usually indicated by the following symptoms:

  • long methods;
  • one object manipulating data defined in other objects;
  • long object/method chains intermediating access to other objects.

Modularity problems at syntactic level typically emerge as high priority issues as soon as any maintenance need touches them. Fortunately, they usually can be detected from program code analysis and solved simply by applying basic object-oriented design rules.

New Requirements Expose the Design Problem and Ignite Refactoring

Using Fowler’s own words, “there’s nothing wrong with a quick and dirty simple program”, as long as there is no need to change it. As expected, the program’s bad design became a relevant problem when the users asked to create an HTML version of the customer’s statement and told other changes would follow in the rules for video classification and rental calculations.

Although the change in the customer’s statement is confined to format and does not affect content, it is not possible to reuse the current statement generation and just add a specific method for the new HTML format.

There are two ways to immediately add the new function:

  1. duplicate the conditional logic for the intended format at each output point inside the statement method;
  2. duplicate the statement method itself, including the content generation logic for calculating charges and frequent renter points.

Duplication is unacceptable, because it adds complexity and threats the next changes. Replication breaks the program modularity by introducing coupled parts to the program’s structure. Whenever one of these parts changes, the respective couples must also change simultaneously. The resulting program would be harder to change and prone to inconsistency.

We should, instead, follow our masters advice:

When you find you have to add a feature to a program, and the program’s code is not structured in a convenient way to add the feature, first refactor the program to make it easy to add the feature, then add the feature.

Before jumping into the program’s code and making any changes, necessary precautions must be followed by any professional software developer:

  1. identify precisely the scope of the intervention: what is to be changed, what is to remain invariant;
  2. prepare, review and put in place solid tests covering both changes and invariants.

Cohering Data and Function Abstractions

The most basic design rule for object-oriented design is to use the classes as the loci for cohesive abstractions integrating data and function responsibilities. The statement method is a clear target for this rule.

Fowler decomposes the statement method into elemental functions relative to the classes already defined in the program and then delegates each of these functions to the appropriate class. The class responsibilities rearrangement is evident comparing the class diagrams before and after the refactoring.

Video Store - Rearranged Responsibilities

Video Store - Rearranged Responsibilities after Refactoring

The transformation is very beneficial to the statement method, now renamed textStatement. It became easily understandable after delegating all the logic for calculating charges and frequent renter points and restricting its responsibility to content generation and formatting.


    public String textStatement() {

        String result  = "Rental Record for " + this.getName() + "\n";

        for (Rental each : _rentals) {

            // show figures for this rental
            result += "t" + each.getVideo().getTitle() +
                      "t" + String.valueOf(each.getCharge()) + "\n";
        }

        // add footer lines
        result += "Amount owed is " + String.valueOf(this.getCharge()) + "\n";
        result += "You earned " + String.valueOf(this.getPoints()) +
                  " frequent renter points";

        return result;
    }

Refactoring the program made it easier and safer for us to add the new feature by creating the new htmlStatement method for the HTML format. The program still exhibits some duplication, but complexity was managed to a more reasonable level.


    public String htmlStatement() {

        String result  = "<P><H1>Rental Record for <EM>" + this.getName() + "</EM></H1></P>\n";

        while (Rental each : _rentals) {

            // show figures for this rental
            result += "t" + each.getVideo().getTitle() +
                      "t" + String.valueOf(each.getCharge()) + "<BR>\n";
        }

        // add footer lines
        result += "<P>Amount owed is <EM>" + String.valueOf(this.getCharge()) + "</EM></P>\n";
        result += "<P>You earned <EM>" + String.valueOf(this.getPoints()) +
                  "</EM> frequent renter points</P>\n";

        result += "</BODY></HTML>";
        return result;
    }

Conclusions

The initial Video Store didactic program instantiates the infamous Anaemic Domain Model, the archetype of a procedural design disguised under a object oriented appearance. Anaemic models gradually degenerate via duplication into coupled structures that are harder to change and prone to inconsistency.

Purely syntactic refactoring greatly improved the program modularity by simply applying the fundamental object-oriented design rule: integrate cohesive data and function abstractions into objects. The refactored program is easier and safer to change. The new requirement can be implemented by a smaller and more focused intervention, without generating any unintended side effects.

In this post the refactoring technique was merely used to change the program structure towards a proper object oriented syntax. There is plenty of room for additional improvement. The next post in the Revisiting Fowler’s Video Store series will explore the refactoring technique at a deeper level.

Written by Rafael Peixoto de Azevedo

January 14, 2009 at 2:36 am