When discussing 'generalisation' here, I refer to the act of extracting the more common out of the specific, like handling the visual representation of an 'invoice' as a 'document' and an 'employee' as a 'person'.
I advocate that a system developer or anyone being part of the design process should always analyse whether a certain entity or an action is case-specific or if it could be regarded part of a more general domain. Take the invoice - obviously a non-discussable entity in any system billing customers, and in this respect it deserves its own class and may not be seen as anything but an 'invoice'. But parts of the system deal with document production, conversion between formats, publishing/distribution - and to those, using the term 'document' will be better.
Bare with me on these examples, my intention has not been to create a framework for an actual application...merely illustrating the point of generalisation:
Ungeneralised: function bill(orderId) createInvoice(orderId) -> returns invoiceId invoiceToPdf(invoiceId) -> returns pdf saveInvoice(invoiceId, pdf, 'invoice_12345.pdf') sendInvoice(invoiceId) Generalised: function bill(orderId) createInvoice(orderId) -> returns invoiceId createReport('invoice', invoiceId, 'pdf') -> returns pdf saveFile(invoiceid, pdf, 'invoice_i2345.pdf') sendDocument(pdf, 'email', 'firstname.lastname@example.org', 'Invoice 12345')
Nothing much different, part from the naming....or is it ? The sendInvoice clearly requires reference to all information needed to locate the appropriate .pdf and how to acquire the customer email, for one. Also, the sendInvoice() may not be used if customer requests sending order confirmations as well.
- Clearification: In this example, it is quite obvious that the action of sending the completed invoice in e.g. .pdf format via email isn't different from sending any other .pdf document to the customer, like information on changed terms, etc.. Avoiding the term 'invoice' in this part of the system helps making this clear.
- Extendability: When the system owner at a later stage wants to start sending order confirmations, the functionality to do so will be there and even if the purpose differs slightly from what was originally designed.
- Reuse: When the developer starts working on the next system, he or she will already have a working solution on sending documents via email. Also for totally different kinds of systems. The code might not be ported, but the solution concept would possible be.
- Architectureal flexibility: Avoiding the send-file functionality from knowledge on actually sending an invoice helps avoiding dependencies, especially two-way ones. The billing module may need references to the distribution module, but it significantly simplifies the system if the distribution module needs no references to the billing module. This comes in handy when e.g. wanting to move distribution module to another platform.
- Domain knowledge: The philosophy of trying to divide functionality into different aspects, involving extracting the general aspects, leaves you with knowledge on the real domain-specific aspects. What is the domain-specific aspect of billing ? In most cases, it will be the fact that invoicing is a formal action in addition to creating a document and distributing it. The accounting rules would normally disallow for changes in issued invoices - they would have to be annulled and replaced with a new one. Also, that creating an invoice requires insertion of rows in the database up front and generating the invoice from these records combined with data from the customer / order / product tables.
- Aspects may be connected to entities, technologies, domain areas, application structure and many others.
- Knowledge: The cognitive aspect of this philosophy is about gaining knowledge - it enables you to build on previous experience because you have managed to extract entities and terms that may be used to solve another case although being a different one. If all handling of the invoice is created with the actual customer-specified invoice in mind, there is a chance that you would not be able to see that you have experience on creating and distributing documents when facing the specification of yor next assignment.
Generalisation - also for agile development?
Agile development somehow advocates you should not do thorough up front modelling, avoiding over-complexity and ensuring you're actually building what the client needs. This could lead to another take on the invoicing, but I believe it is still a good idea to do a minimum of generalisation. Developing through iterations and refactoring isn't an argument for starting with bad design like unneccesary dependencies and writing classes that may absolutely not be reused. Also, noone benefits from developers not building knowledge. Let's be pragmatic and write code that wouldn't necessary be reused as-is in the next project, but spend the extra effort to actually have a better understanding of what you're constructing. And that's a "document distribution module", not an "invoice distribution module".