The When, What and How of Code December 21, 2011at00:10
When partitioning your code it is important to consider the when, the what and the how. It can decide if your program will be well structured or become a tangled mess. These three aspects should be separated whenever possible. It is a strategy for thinking about dependencies and separation of concerns.
An action could be triggered by an event or signal, a click on a button in a user interface, the call to a command-line tool, receiving a message on a socket. The infrastructure that holds and decide when the what is performed should be kept separated whenever possible.
Further, the when should have no direct knowledge of the what, so invocation should be done using hocks and other means of abstractions. This results in a sound infrastructure, isolated from the specific problem domain, lending itself for reuse and easier modification through extension.
This can imply different registration-mechanisms; where the application construction wires/glues the application together by instantiating and registering functions that performs the what to be called conditioned by specific triggers. Hence making the infrastructural-framework-core the decision maker of the when.
What should be done! Think of it as a recipe for solving the problem at hand or creating whatever it is you are trying to accomplish. It is expressed in terms of abstractions. You shouldn’t be concerned with how, only what should be done.
This is usually placed in service-objects/services. It could be the EditEmployee-method that restores an employee object from persisted data, modifies it using accessors on the employee object and then write the changes back to the data storage. It does not concern it self with how Employee objects are restored, how they are edited or how they are saved. Only that these things should be done as part of the Edit-operation.
This allows the higher-level logic to be expressed in terms of abstractions that are consistent, cohesive, that can make use of reusable object as well as making it easier to understand the intent of the expressed functionality.
This is the low-level classes that deal with concrete implementations of the lowest building blocks of the application. It could be the EmployeeDB class that knows how to persist an Employee instance to the database/persistence layer. It is tiny building blocks that are easy to test, easy to understand, easy to replace and easy to reuse.
The Test Framework – An Example
In order to keep a testing framework clean and simple to use, you should separate your whens, whats and hows. The core framework should deal with the when. The test-runner dictates when a test case is run, when its before-method and after-method (setup/teardown) hocks are executed and when the procedure for recovery should be invoked. However, the core framework should not concern itself with what it means to recover and cleanup; only provide hocks that allow other code to inject the what.
The test cases should hold the what. It decides what is created (setup/before-test-method), what is destroyed upon cleanup (teardown/after-test-method) and what is executed as part of the test. It however, does not concern itself with the how.
The how is taken care of by the production code; and if needed in order to keep the test-case-code clean, help-libraries that raises the level of abstraction in the scope of a test case. The help-libraries should not be part of the framework.
This might seem as an overzealous approach to separation of concerns. However, if not abided by, it becomes all to easy to put too much functionality into the framework making it harder to reuse, and also forcing you to create mechanisms for excepting special behavior.