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.
This is a very modular concept und therefore 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 (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" ([1], p. 260).
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 can not test
this method with a unit test if you are offline. If, instead, you
break up the job into two portions ("query webpage", "parse
webpage") and put each portion into its own method, you can
at least unit test the "parse webpage" bit (provided you keep
a sample webpage for testing).
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 have two more specialized methods
string getWebPage(string& stock, IWebConnection& con)
{ ... } and double extractStockPriceFrom(string& webpage)
{ ... } which you would call consecutively:
[...]
string page = getWebPage("MSI", mInetConn);
double price = extractStockPriceFrom(page);
[...]
When applied to methods, this principle is sometimes 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.
But keeping methods small and simple does also mean avoiding methods
whose implementation takes hundreds of lines of code. Try to break them
down using the refactoring "extract method" until they are short
enough to fit in their entirety into the window of your favourite editor in
a standard setting.
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. That class
might contain two methods encodeBase64() and
decodeBase64() because emails can contain Base64 encoded content.
But does it need to? The Single Responsibility Principle in this case would
mean that instead of being members of CEmail,
encodeBase64() and decodeBase64() should rather be
public methods of their own class CBase64 which can then be used
by CEmail.
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?
[1] "Working Effectively With Legacy Code". Michael C. Feathers, Prentice Hall, 2010