Now that we've set up the space and the tone within the team, we now need to look at how the team deals with code. Our objective here is to ensure that all the code being developed by pairs is integrated seamlessly, and is consistent with a standard acceptable to the team. By driving step two we'll go a long way to support step one.
Coding standards
Whether you decide to go forward with Agile or not, coding standards are an excellent best practice. This step involves having the team create a set of coding standards that the they fully understand and adhere to. Coding standards give us some advantages:
- They make it easy to read each other's code and thus anyone can make changes.
- The code becomes an excellent source of information for future teams working on it, or if a team member leaves.
- New team members have a set of guidelines -- no guessing.
CamelCaseThis is just a short example from something that is evolving. Again, start simply, keep it basic and get agreement from everyone.Everything's in CamelCase, class names start with caps, method and field names don't.
Don't ever put curly brackets on the same line as anything else.
Interface exposure
Fields start with an underscore: _fieldname Variables don't: variableName Method: public void methodName(String stringValue) Public methods go at the top of a class, followed by protected methods, followed by private ones. It may also be a good idea to place any methods which inherit from abstract classes or implement interfaces towards the top.
It should be possible to flick onto a class and instantly get a feel for its function and how it fulfils that function, without having to scroll down or squint.
Method and class names
Descriptive as they need to be. Noticed different developers have different thresholds for readable methods, prefer to glean context from the surrounding class, and even the parameters in the method. This is debatable, but certainly it should be possible to get the meaning from a method by reading its name, so:
doIForAllX()
is bad, but:
setupAllTableRowItems()
is good.And possibly:
createRows()
is better.[getVar vs calculateVar, straight getter vs a method]
[don't mix query with assignment]
Method abstraction
Code inside a method should be of the same level of abstraction as all the other code in the same method. In this way the natural course of events in a class can be made clear. For example:
public void initializeDataBase()
{
_connection = createConnection ();
setUpTable();.
For (int i=0;i<tableRows;i++)
SetUpTableRow(i);
}This is not so much hard to read as it makes you squint. We at 3Q value our eyesight so we pull the code into neat little steps, like so:
public void initializeDataBase()
{
setUpConnection();
setUpTable();
setUpTableRow();
}This makes it possible to:
The Law of Demeter
- Get a feel for what's going on
- Easily navigate into the exact part of the class we want (if we're interested in the table row setup, we ctrl-click on setUpTableRow().
A class should have access only to those methods directly accessible from its fields or variables. References to objects sent in or objects the class instantiates itself are good to go also.
Basically, don't do this...
public int calculateRetirementFund()
{
return getClient().getRetirementDetails().getRetirementFund();
}...but do this instead:
public void calculateRetirementFund (RetirementDetails details)
{
return details.getRetirementFund();
}This helps give scope to the class and cuts down unneeded method calls and delegation.
Sequence Selection Iteration
Methods can generally categorised into these three types. A sequence of events, one after the other, a search or filter on a collection, and an iteration over a collection or array.
Collecting methods, Vector create, vector setup, vector dosomething
Collections crop up over and over again, and with the same problems each time, mainly to do with typecasting. If you have arbitrary runtime casting built into a collection, there is a chance that the wrong type will find its way in, resulting in cast exceptions.
Making the collection type-specific allows for compile-time checking against what's added into the collection, as well as making it easier to fit in custom collection methods based around that type.
Don't use temporary vars - replace temp with query
Reference the Refactoring book for this -- "Replace temp with query", best not to hold on to temporary variables, it increases the complexity of the code for the reader, and reduces the options for further refactoring of the algorithm.
Testing antipatterns
Large setup indicates an ill-conceived pattern. You should only need to set up those objects which directly relate to the class you are testing.
Make unit tests as fine-grained as possible, this allows for more portable code, as well as pushing it towards cleaner, more decoupled implementation.
Testing through backpointers
Backpointers are just plain nasty and should be avoided. There are noticeable exceptions to this case, the state pattern being one. Know your reasons for implementing a backpointer. If the reason is "it'll work" then you're missing something.
View testing - instantiating triads in tests.
In a well-built app, the view layer should be abstracted away from the domain to such an extent that you need never create a view to test the app. Coarse-grained testing will need to be changed more often. See testing antipatterns.





