Wednesday, August 29, 2007

Example of mutable extension to immutable domain model

I must not have communicated the mutable extension idea clearly enough, as I've been accused of breaking the immutability and encapsulation of the domain model which would, I agree, make the domain model sloppy. I deplore mutability in domain models and anemic / 'behaviourally absent' nonsense such as setters / getters.

I haven't done either of those things (as far as production code is concerned) here's a worked example which demonstrates this.

Here's the immutable, encapsulated, distinctly unsloppy domain model:

class Person {
private final String name;
private final int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}
}

Here's code that's only available for testing:

class MutablePerson extends Person {
private static final String DEFAULT_NAME = "Bob";
private static final int DEFAULE_AGE = 32;

public MutablePerson() {
super(DEFAULT_NAME, DEFAULT_AGE);
}

public MutablePerson withName(String name) {
return new MutablePerson(name, getAge());
}

public MutablePerson withAge(int age) {
return new MutablePerson(getName(), age);
}

private String getName() {
return accessor.get(this, "name", String.class);
}

private int getAge() {
return accessor.get(this, "age", Integer.class);
}

private final ReflectiveAccessor accessor = new ReflectiveAccessor();
}

Now it's true that the testing code has broken encapsulation but only as far as the testing code is concerned, not for the production code. The domain model itself is unchanged. It should be noted that there is a one-to-one relationship between the immutable domain model class and the mutable extension and that the break in encapsulation for a given domain model class propagates no further than the corresponding mutable extension; and again this break only exists in the testing code.

So I would say the testing code has a minor encapsulation breach which allows the testing code to have a more succinct form than possible with the test builder (it essentially removes the need for all the build() methods).

Unlike the Test Builder it would not be suitable to move the Mutable Extensions to the production code. Immutability & non-anemicness helps keep domain models from becoming 'bags-o-data', having a mutable version of each domain model in the production code would be too much of a temptation.

Tuesday, August 28, 2007

Mutable extension to immutable domain model: an alternative to the Test Data Builder pattern

Nat Pryce recently blogged about an alternative to the Object Mother pattern: creating a builder class for each class in the domain. He's able with this idea to get testing code to look like this:
Invoice invoiceWithNoPostcode = new InvoiceBuilder()
.withRecipient(new RecipientBuilder()
.withAddress(new AddressBuilder()
.withNoPostcode()
.build())
.build())
.build();

I think I can improve on this and achieve code that looks like this:

MutableInvoice invoiceWithNoPostcode = new MutableInvoice()
.withRecipient(new MutableRecipient()
.withAddress(new MutableAddress()
.withNoPostcode()));

Instead of creating a builder for each domain class I've created a mutable extension to each domain class, it is the mutable extension I'm using in the test.

I don't quite like the code above, I think the following looks better:
Invoice invoiceWithNoPostcode = new Invoice()
.withRecipient(new Recipient()
.withAddress(new Address()
.withNoPostcode()));

I've changed the code to use the same name for the mutable extension (with a different package of course) because I figure the tests can just refer to the mutable versions and it saves me having the 'Mutable' prefix on everything. Naturally I only make the mutable extensions available for testing.

Here's how the mutable Invoice extension might look:

package model.mutable;

public Invoice extends model.Invoice {
public Invoice(Recipient recipient, InvoiceLines invoiceLines) {
super(recipient, invoiceLines);
}

public Invoice() {
// choose the same defaults here as you would in the InvoiceBuilder
this(null, null);
}

public Invoice withRecipient(Recipient recipient) {
return new Invoice(recipient, getInvoiceLines());
}
}

I've had to add protected accessors to the domain model; this bothers me a little as they make it easier to slip towards an anemic data model. It might be worth using some reflective code in the extension class instead:

public class Invoice extends model.Invoice {
...

private InvoiceLines getInvoiceLines() {
return new ReflectiveAccessor().get(this, "invoiceLines", InvoiceLines.class);
}
}


public class ReflectiveAccessor {
public T get(Object fieldOwner, String fieldName, Class fieldClass) {
...
}
}

Monday, April 30, 2007

Branching by Abstraction

Ok Paul, I'll bite.

Some time ago I was working for a client which was experiencing merge hell, come to think of it all the large clients I've worked for routinely experience merge hell, hopefully this isn't correlated with my presence. At the time I mused that when someone creates two implementations of an interface they are effectively branching and so I
voiced that instead of using version control to branch some code perhaps we should Branch by Abstraction. I was working with Paul Hammant at the time and he's apparently ran with this idea and achieved some notable improvements with it.

I don't think BBA it is really that novel, it's just another way of saying 'Hey!, Separate your Concerns', but since a lot of coding-land seems to be engaged in a energetic fight to do the opposite it's probably good to have as many ways of stating the bleedin' obvious as possible.

I'm quite a lazy creature by habit so I've only drafted an article on this on C2, and intend (honest) to polish it up over time, here's a copy of the first draft:


Summary: Don't separate differing concerns by using a VCS, use an abstraction instead.

Long story: Conceive the changes of a project as providing alternate implementations of abstractions already present in the software, or of extracting out an abstraction and making it explicit and them providing an alternate implementation. By doing so the abstraction and the varying implementations provide the separation and branches are need much less.

Consider a possible project that aims to add more data to a business report, say a customer invoice. Suppose that the data being added is the date on which a transaction took place. The current CustomerInvoice might obtain a bunch of LineItems and render them. The new requirement is to add date information to the LineItem and display this on the invoice. At the same time as this project is going on bug fixes are being applied the existing invoice, so we can't just make changes to the invoice.

Step 1) Extract out abstractions needed by the project which are not currently explicit in the code.

1a) Extract the code which provides line items into an interface and implementation: LineItemProvider, NameAndCostLineItemProvider, make the implementation the default the CustomerInvoice uses. Deprecate the implementation with a note to its impending replacement. Test and release to production.

1b) Extract the code which renders line items into an interface and implementation: LineItemRender, NameAndCostLineItemRenderer, make implementation the default the CustomerInvoice uses. Deprecate the implementation with a note to its impending replacement. Test and release to production.

Since the changes in Step 1 should be expressible as a series of refactorings it should be possible to make the test step very short, or arguably unnecessary.

Step 2) Create alternate implementations of the abstractions created in step 1, or already existing before the project began. NameCostAndDateLineItemProvider, NameCostAndDateLineItemRenderer.

Step 3) Release the new implementations in a QA environment configured to use the new implementations.

Step 4) Release the new implementations in production configured to use them.

Optional step) Back out the implementations from production, revert back to the previous implementations at run time.


Whilst most of the project is going on (steps 2 & 3) bug fixes can still be applied to all other aspect of the system, and the dated-invoice project just receives these changes for free, as they are only changing the line item providing / rendering aspect. If a bug fix is needed in the line item providing / rending aspect the bug fix team easily become aware of the new implementation via the deprecation notice, alternately the dated-invoice team can monitor changes to the undated-implementation.

If another project starts up whilst the dated-invoices is chugging along then it will have the same steps, and will involve work on some other abstraction. Should another project need to do work on the same abstractions they too will have the deprecation as a notice to be careful and consider interaction with the ongoing work on dated-invoice, otherwise they can work merrily in ignorant.

So in short the encapsulation that abstractions provide can work for projects just as well as they work for code. When changes occur in other projects then as long as it doesn't involve the abstractions I'm working on in my project then I don't need to care.

Compare this with BranchByVcs. In this one creates a fork of a much larger set of code, probably all of it. Other projects / bug fixes will occur in the same code and later the two changes will be merged together, the merge will take place a series of poorly understood textual changes with an unspecified focus, additionally the merging of changes from some project is usually done by people not on the project.

Thursday, January 25, 2007

Poetic / pathetic version control

Old codebase
Clearcase update
Flop

Saturday, January 20, 2007

Anti-censorship

A colleague raised a question about whether he should self-censor to remain visible to people behind the 'great firewall of China' or retain prohibited material and 'deny' all his content, prohibited or not.

I offer the links below as my answer.


Appeasing Dictatorship: From Munich to Hong Kong: http://www.aynrand.org/site/News2?page=NewsArticle&id=5137

Save Tibet website:
http://www.savetibet.org/index.php

Tiananmen Square, Wikipedia article:
http://en.wikipedia.org/wiki/Tiananmen_Square_protests_of_1989

Taiwan independence, Wikipedia article:
http://en.wikipedia.org/wiki/Taiwan_independence

China Human Rights Fact Sheet:
http://www.christusrex.org/www1/sdc/hr_facts.html