The Eleventh Principle: An idiom should only implement the functionality needed.

This is a continuation of the blog post 12 Principles of Good Software Design.

11. An idiom should only implement the functionality needed.

One of the coolest things about writing software is that we are creating something out of virtually nothing but our imagination and our ability to design something that works. And so it allows us to test our pet theories about architectural design, about the best way to solve problems with designs that are extensible and which can be generalized to solve even larger problems.

The temptation to do this exists on all projects. Even though we are warned constantly about premature optimization, we still do the equivalent when it comes to solving the immediate problems we have in front of us by extending them to more generalized solutions which we expect will help us in the future. But they almost never do: instead of providing us an easily extensible architecture which can solve future problems, we instead create something too complicated to maintain. And the new problems we face don’t fit into the solution we previously built.

Years ago I wrote an essay on how not to write Factorial in Java.

Another example of solving problems we don’t have yet, only to make things confusing: suppose we have a table view which only shows a subset of the items in the table depending on a bit of business logic. This business logic essentially filters out the results of the table according to a function which takes an object from our database and determines if it should be visible or not.

In the process we decide that what we really need here is considerable flexibility in determining if rows are visible or not.

So we decide to use the Specification Pattern, which provides us nearly unlimited flexibility in assembling simple conditional statements into complex logic classes. We build our specification protocol:

@class ModelObject;

@protocol Specification <NSObject>
- (BOOL)isVisible:(ModelObject *)obj;
@end

and we build our classes which handle the various boolean operations:

#import "Specification.h"

@interface AndSpecification : NSObject <Specification>
- (id)initWithLeft:(id<Specification>)left right:(id<Specification>)right;
@end

@interface OrSpecification : NSObject <Specification>
- (id)initWithLeft:(id<Specification>)left right:(id<Specification>)right;
@end

@interface NotSpecification : NSObject <Specification>
- (id)initWithSpecification:(id<Specification>)spec;
@end

And of course comes our test classes which test potential propositions:

@interface HasNameSpecification : NSObject<Specification>
- (BOOL)isVisible:(ModelObject *)obj;
@end

@interface HasAddressSpecification : NSObject<Specification>
- (BOOL)isVisible:(ModelObject *)obj;
@end

Our View Controller class then runs our filter as we refresh the contents:

@interface ViewController () <UITableViewDelegate, UITableViewDataSource>
@property (strong, nonatomic) id<Specification> filter;
@property (strong, nonatomic) NSMutableArray<ModelObject *> *allObjects;
@property (strong, nonatomic) NSMutableArray<ModelObject *> *filtered;
... (other fields) ...
@end

...

- (void)refreshContents
{
	[self.filtered removeAllObjects];
	for (ModelObject *obj in self.allObjects) {
		if ([self.filter isVisible:obj]) {
			[self.filtered addObject:obj];
		}
	}
	[self.tableView reloadData];
}

For now, of course, we only have one filter–so we construct that by hand in the viewDidLoad method:

- (void)viewDidLoad {
	[super viewDidLoad];
	...
	self.filter = [[AndSpecification alloc] initWithLeft:[[HasNameSpecification alloc] init] right:[[HasAddressSpecification alloc] init]];
	...
}

Nearly a half-dozen classes, a hundred lines of glue code or so, but we are now prepared for that future request we know that is coming, of a server which may send over an arbitrary filter specification to our client which alters the behavior of what items we show on the table view.

This design model is conceptually relatively easy, too: if we have other predicates we may filter against, such as if the zip code is well formed or if the phone number is present, we simply create new classes for each test–then assemble them into a tree hierarchy for evaluation.

And when the back end sends us a promised XML file which contains the new filter specification, such as:

<or>
    <filter name="HasName"/>
    <filter name="HasAddress"/>
</or>

we can easily parse that using our specification classes and reload the filter used in our code.


A beautiful architectural generality to solve the present problem of filtering items if they have a name and address against a general purpose structure that can form any filter we can imagine from an arbitrary specification.

But will we ever use this generalized solution?


Well, in order to support this filter method, the back end needs to send an XML specification for the new filter. The XML needs to be authored–which implies a back-end page which administrators can go to in order to create new filters. The page needs to be created and maintained; the database entries need to be built and tested, the end-user administrators need to be trained, and the new filters need to be validated before they are deployed.

That’s a lot of back-end work.

Instead of sending this XML, it would be far easier to simply send a single keyword or index: “filterNameAddress” for our present filter, or “filterNameOrAddress” for an Or operation. Or even better, that could be represented by a magic number.

Dozens of lines of code in our application, and in the end, this is how we’d wind up using our classes:

- (void)viewDidLoad {
	[super viewDidLoad];
	...
	switch (self.filterIndex) {
		default:
		case FILTER_NAME_ADDRESS:
			self.filter = [[AndSpecification alloc] initWithLeft:[[HasNameSpecification alloc] init] right:[[HasAddressSpecification alloc] init]];
			break;
		case FILTER_NAME_OR_ADDRESS:
			self.filter = [[OrSpecification alloc] initWithLeft:[[HasNameSpecification alloc] init] right:[[HasAddressSpecification alloc] init]];
			break;
	}
	...
}

Meanwhile we could have eliminated the nearly half dozen classes with two methods and a switch statement instead:

- (void)refreshContents
{
	[self.filtered removeAllObjects];
	for (ModelObject *obj in self.allObjects) {
		BOOL addFlag;
		switch (self.filterIndex) {
			default:
			case FILTER_NAME_ADDRESS:
				addFlag = [self testNameAddress:obj];
				break;
			case FILTER_NAME_OR_ADDRESS:
				addFlag = [self testNameOrAddress:obj];
				break;
		}
		if (addFlag) {
			[self.filtered addObject:obj];
		}
	}
	[self.tableView reloadData];
}

Worse, this is how our code would evolve. In the present, we haven’t reached that point. Our present problem is to filter against one pre-existing filter, so why couldn’t have we written this instead?

- (void)refreshContents
{
	[self.filtered removeAllObjects];
	for (ModelObject *obj in self.allObjects) {
		if (obj.name && obj.address) {
			[self.filtered addObject:obj];
		}
	}
	[self.tableView reloadData];
}

Dozens of lines of specification code scattered across 5 separate classes against a protocol interface which is hard for a programmer to understand–literally replaced by one line of code:

if (obj.name && obj.address) ...

And that’s nearly a half-dozen classes and dozens of lines of code we now have to maintain, explain, and explain why we’re doing things the hard way–for a future that never came.


Life is hard enough as a software developer for us to solve problems we don’t currently have.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s