Faoileag's Nest

My Home On The Net

Faoileag On...


Keep It Small and Simple

General principle

The general principle behind KISS is that any one functional unit should only do one job (but that job it should do well). A good example is the suit of little shell utilities well known from Linux and other **ixes: grep, awk and all the others.
The philosophy behind them is quite simple: every utility concentrates on doing one job well. So grep is very good at finding lines matching a regex pattern in a text file; it is not so good at displaying just parts of those lines, say the second word. This, however, is something that awk is quite good at, so all you have to do is pipe the result of grep into awk for formatting.

Such a modular concept is very lightweight. If you don't need to format the output and just start grep the memory footprint is lighter than with grep and awk combined.
If, instead, you would have just one utility that does both (let's call it prettygrep), the memory footprint would always be large, because even if you wouldn't want to format the results, prettygrep would always be started with the functionality to do so included.

The principle of doing one job well is also called the Single Responsibility Principle, especially when applied to class and method design.
To quote Michael C. Feathers: "The Single Responsibility Principle tells us that classes should have a single resposibility. If that's the case, it should be easy enough to write it down in a single sentence" (Working Effectively With Legacy Code, Prentice Hall, 2010, p. 260).

Small and Simple Methods

Keeping methods simple usually means not to combine behaviour. Think of a method that checks stock prices on the internet. You would do this by querying a webpage that provides free stock price information from a webserver and then perform some operations on that webpage (e.g. regular expressions to extract the stock price). Now, if you do this in one method you cannot test this method with a unit test if, for whatever reasons, the remote server is not available. If, instead, you break up the job into two parts, querying the web page and parsing it, and put each part into a method of its own like queryWebpage and parseWebpage, then you can at least unit test the method parseWebpage, even if the webserver is down.

Lets look at some code for the example above. Instead of having just one does-it-all method

double getStockPrice(string& stock, IWebConnection& con)

you would habe two methods

string getWebPage(string& stock, IWebConnection& con)

double extractStockPriceFrom(string& webpage)

which you could then call consecutively:

string page = getWebPage("MSI", mInetConn);
double price = extractStockPriceFrom(page);

When applied to methods, this principle is sometimes also called separation of concerns and it is easy to demonstrate that with the example above:

double extractStockPriceFrom(string webpage) { ... }

does not have to be concerned about how the webpage is fetched, all it has to do is parse it once it has been fetched.

Keeping methods small and simple does stop at separation of concerns, though. It also means avoiding having methods that run over hundreds of lines of code.
Try to break them down into smaller methods that each fits entirely into the window of your favourite editos; refactoring tools that support "extract method" are quite helpful here.

Small and Simple Classes

What is good for methods is good for classes as well. Keep them simple, concentrating on just one responsibility per class. That means that instead of cluttering a class with all sorts of methods that are somehow related to the problem, you group methods of related functionality into their own classes.

Take, for example, an email-handling class CEmail. This class might contain two methods encodeBase64() and decodeBase64() because emails can contain Base64 encoded content.
But does it need to? The Single Responsibility Principle says no. Instead of having two methods doing the encoding and decoding, CEmail should have a member that instantiates CBase64, a seperate class that provides Base64 encoding and decoding methods.

Such modularity makes for far better testability since now you can write explicit unit tests for CBase64 which otherwise might not have been possible: why should CEmail expose the two Base64 related methods as public? Just so that they could be unit tested? That would violate all encapsulation principles. Because others might use those methods for different purposes? That's even worse, because then you use an email-related class to perhaps encode images for transport in a messenger system...
So, better create CBase64 and use that in CEmail and wherever else you might need Base64 encoding.