Refactoring With Types
Tags: refactoring
Published: 2019-09-08 14:41:19

<h2>Introduction</h2>
In this post, we will look at some refactoring techniques using types. &nbsp;Types can be used to represent the domain in a fine grained, well defined way. Additionally, types can be used to incorporate business rules in a manner that ensures code correctness. This enables writing simple and elegant unit tests to ensure correctness of code.
<h2>Refactoring With Types</h2>
Recently, while reviewing code, I came across the following class,
<pre lang="java" line="1">public class OrderLine {
  private int quantity;
  private Double unitPrice;
  private Double listPrice;
  private Double tax;
  private Double charge;
  //Rest of the implementation
}</pre>
The code above is a classic example of a code smell called&nbsp;<a href="https://sourcemaking.com/refactoring/smells/primitive-obsession"><em>primitive obsession.</em></a>

All the above parameters are represented using numbers. However are they just numbers. Is <em>UnitPrice</em>&nbsp;interchangeable with&nbsp;<em>ListPrice&nbsp;</em>or <em>Tax</em>.

In domain driven design these are indeed distinct things and not just numbers. Ideally, we would like to have specific types to represent these concepts.

The first level of refactoring is to create simple wrapper types for these classes
<pre lang="java" line="1">public class ListPrice {
    private ListPrice() {
    }
    private @Getter Double listPrice;
    public ListPrice(Double listPrice) {
        setListPrice(listPrice);
    }
    private void setListPrice(Double listPrice) {
        Objects.requireNonNull(listPrice, "list price can not be null");
        if (listPrice &lt; 0) {
            throw new IllegalArgumentException("Invalid list price: "+listPrice);
        }
        this.listPrice = listPrice;
    }
}</pre>
<pre lang="java" line="1">public class UnitPrice {
    private UnitPrice() {
    }
    private @Getter Double unitPrice;
    public unitPrice(Double unitPrice) {
        setUnitPrice(unitPrice);
    }
    private void setUnitPrice(Double unitPrice) {
        Objects.requireNonNull(unitPrice, "unit price can not be null");
        if (unitPrice &lt; 0) {
            throw new IllegalArgumentException("Invalid unit price: "+unitPrice);
        }
        this.unitPrice = unitPrice;
    }
}</pre>
This serves as a good starting point. We now have conceptual constructs for these. Any business rules which are required for a construct can now be wired within these constructs rather than being implemented in the container <i>OrderLine</i> class.

However, if we observe, there is duplicate code to check that the listPrice and unitPrice should not be <i>null</i> or <i>non-negative</i>. This check would most probably be applied to <i>quantity, tax and charge</i> as well.

Hence it makes sense to create a Type which represents a non-negative number concept.
<pre lang="java" line="1">public class NonNegativeDouble {
    private @Getter Double value;
    public NonNegativeDouble(Double value){
        this.setValue(value);
    }
    private void setValue(Double value) {
        Objects.requireNonNull(value,"Value cannot be null");
        if(value &lt; 0){
            throw new IllegalArgumentException("Value has to be positive");
        }
    }
}</pre>
Now we can safely refactor the UnitPrice and ListPrice classes to use this new construct of a non-negative double.
<pre lang="java" line="1">public class UnitPrice {
    private UnitPrice() {
    }
    private
    @Getter
    NonNegativeDouble unitPrice;
    public UnitPrice(NonNegativeDouble unitPrice) {
        setUnitPrice(unitPrice);
    }
    private void setUnitPrice(NonNegativeDouble unitPrice) {
        this.unitPrice = unitPrice;
    }
}</pre>
A simple test to validate the non-negative constraints for <i>UnitPrice</i>
<pre lang="java" line="1">@Unroll
class UnitPriceSpec extends Specification {
    def "#text creation of Unit Price object with value - (#unitPriceValue)"() {
        given:
        def unitPrice
        when:
        boolean isExceptionThrown = false
        try {
            unitPrice = new UnitPrice(new NonNegativeDouble(unitPriceValue))
        } catch (Exception ex) {
            isExceptionThrown = true
        }
        then:
        assert isExceptionThrown == isExceptionExpected
        where:
        text        |   unitPriceValue      |   isExceptionExpected
        'Valid'     |   120                 |   false
        'Valid'     |   12.34               |   false
        'Valid'     |   0.8989              |   false
        'Valid'     |   12567652365.67667   |   false
        'Invalid'   |   0                   |   false
        'Invalid'   |   0.00000             |   false
        'Invalid'   |   -23.5676            |   true
        'Invalid'   |   -23478687           |   true
        'Invalid'   |   null                |   true
    }
}</pre>
Although this showcases a simple use-case for refactoring using types, it applies to a lot of constructs which are modeled as primitive types, like, <i>Email, Names, Currency, Ranges and Date and Time</i>.
<h2>Refactoring - Using Types to make Illegal States unrepresentable</h2>
Another refactoring which provides a lot of value is to make illegal state unrepresentable in a domain model. As an example, consider the following java class.
<pre lang="java" line="1">public class CustomerContact {
	private @Getter EmailContactInfo emailContactInfo;
	private @Getter PostalContactInfo postalContactInfo;
	
	public CustomerContact(EmailContactInfo emailContactInfo, 
	PostalContactInfo postalContactInfo){
		setEmailContactInfo(emailContactInfo);
		setPostalContactInfo(postalContactInfo);
	}
	private void setEmailContactInfo(EmailContactInfo emailContactInfo){
		Objects.requireNonNull(emailContactInfo,"Email Contact 
                Info cannot be null");
		this.emailContactInfo = emailContactInfo;
	}
	private void setPostalContactInfo(PostalContactInfo postalContactInfo){
		Objects.requireNonNull(postalContactInfo,"Postal Contact 
                Info cannot be null");
		this.postalContactInfo = postalContactInfo;
	}
	
}
</pre>
Based on the previous refactoring, we have already extracted the domain level constructs<i> EmailContactInfo and PostalContactInfo</i>. These are true domain level constructs as opposed to beings <i>strings</i>.

Lets assume a simple business rule, which states that
<i> A customer contact must have either email contact information or postal contact information</i>.

This implies that there should be at least one of either the <i>EmailContactInfo</i> or the <i>CustomerContactInfo</i>. Both can also be present. However, our current implementation requires both to be present.

In order to apply the business rule, a first attempt might look like this.
<pre lang="java" line="1">public class CustomerContact {
    private @Getter Optional<emailcontactinfo> emailContactInfo;
    private @Getter Optional<postalcontactinfo> postalContactInfo;

    public CustomerContact(PersonName name, Optional<emailcontactinfo> emailContactInfo,
                           Optional<postalcontactinfo> postalContactInfo){
        setEmailContactInfo(emailContactInfo);
        setPostalContactInfo(postalContactInfo);
    }
    private void setEmailContactInfo(Optional<emailcontactinfo> emailContactInfo){
        this.emailContactInfo = emailContactInfo;
    }
    private void setPostalContactInfo(Optional<postalcontactinfo> postalContactInfo){
        this.postalContactInfo = postalContactInfo;
    }

}
</postalcontactinfo></emailcontactinfo></postalcontactinfo></emailcontactinfo></postalcontactinfo></emailcontactinfo></pre>
Now, we have gone too far the other way. The rule requires that the <i>CustomerContact</i> should have at least one of email or postal contact. However, with the current implementation it is possible for the <i>CustomerContact</i> to not have any of them.

Simplifying the business rule leads to the following
Customer Contact = Email Contact or Postal Contact or Both Email and Postal Contact

In functional language, such conditions can be designed using <i><a href="https://chadaustin.me/2015/07/sum-types/" rel="noopener" target="_blank">sum types</a></i>. However in languages like java, there is no first class support for these constructs. There are libraries like <a href="https://github.com/pakoito/JavaSealedUnions" rel="noopener" target="_blank">JavaSealedUnions</a> which provide support for <i>Sum and Union</i> types in java.

Using <i>JavaSealedUnions</i> we can implement the business rule as follows
<pre lang="java" line="1">
public abstract class CustomerContact implements Union2<emailcontact, postalcontact=""> {

    public abstract boolean valid();

    public static CustomerContact email(String emailAddress) {
        return new EmailContact(emailAddress);
    }

    public static CustomerContact postal(String postalAddress) {
        return new PostalContact(postalAddress);
    }
}

class EmailContact extends CustomerContact {

    private final String emailAddress;


    EmailContact(String emailAddress) {
        this.emailAddress = emailAddress;        
    }
    
    public boolean valid() {
        return /* some logic here */
    }

    public void continued(Consumer<emailcontact> continuationLeft, Consumer<postalcontact> continuationRight) {
        continuationLeft.call(value);
    }

    public <t> T join(Function<emailcontact, t=""> mapLeft, Function<postalcontact, t=""> mapRight) {
        return mapLeft.call(value);
    }
}

class PostalContact extends CustomerContact {

    private final String address;


    PostalContact(String address) {
        this.address = address;

    }
    
    public boolean valid() {
        return /* some logic here */
    }

    public void continued(Consumer<emailcontact> continuationLeft, Consumer<postalcontact> continuationRight) {
        continuationRight.call(value);
    }

    public <t> T join(Function<emailcontact, t=""> mapLeft, Function<postalcontact, t=""> mapRight) {
        return mapRight.call(value);
    }
}


// Example

CustomerContact customerContact = getCustomerContact();
if (customerContact.valid()) {
    customerContact.continued(customerContactService::byEmail(), customerContactService::byPostalAddress())
}
</postalcontact,></emailcontact,></t></postalcontact></emailcontact></postalcontact,></emailcontact,></t></postalcontact></emailcontact></emailcontact,></pre>
This post shows some of the ways where thinking in terms of Types can help in having a cleaner design. It also helps to think in terms of Types to avoid having ambiguity around business rules. The approaches shown above can also be used in other scenarios to either capture allowed states or success and failure cases.

A few great posts for reference are

<a href="http://tomasp.net/blog/type-first-development.aspx/" rel="noopener" target="_blank">Why type-first development matters</a>

<a href="https://fsharpforfunandprofit.com/ddd/" rel="noopener" target="_blank">Domain Driven Design With the F# type system</a>