Separation of concerns
Programming usually follows this pattern.
- Get it working - At this stage we are just trying to get things working, we are trying to prove our concept and show that it can work. It is easy to use sloppy programming techniques in the name of expediency and in an effort to just get the thing working.
- Extend its functionality - At this stage we have a basic working system and we decide to add additional features to extend its usefulness. This stage can often times reveal problems with the style of coding we used in step 1.
I promise to never...
After doing a project or two we usually come to the realization that we need to enforce more rigor into our programming style. This can be a huge win for step 2 and a huge loss for step 1. Sometimes the inertia necessary to start a project is thwarted by past projects that became unmanageable.
This post aims to offer some help in programming styles that will not be so overly burdensome to stifle the creative process we all love in step 1 and yet also foster a more manageable more maintainable code base for future extensibility.
Separation of concerns
Lets start with a simple example. Suppose we have a database that stores
We might built a piece of code to display all my friends
Get It Working
<< friends=dbsearch('frienddb','FRIENDSTATUS="y"',1,1000) >> <html> <h1>My Friends</h1> <table> <tr><td>Name</td><td>City</td></tr> << for name=friends rowname=x do display '<tr><td>'+x+'</td><td>'+x+'</td></tr>' /for >> </table> </html>
This code is basically step 1. It does exactly what we want it to do but it doesn't extend very well. So we might want to encapsulate it all into a single monlithic function and call it our friend function or our friend widget
Extend its functionality
<< function friends(db,title,allorjustfriends) locals friends,x,text,thesearch do if allorjustfriends='all' then thesearch='' else thesearch='Y' /if friends=dbsearch(db,'FRIENDSTATUS="'+thesearch+'"',1,1000) text='<h1>'+title+'</h1>'+lf+ '<table>'+lf+ ' <tr><td>Name</td><td>City</td></tr>'+lf+ for name=friends rowname=x do text=text+'<tr><td>'+x+'</td><td>'+x+'</td></tr>' /for text=text+'</table>' return text /return /function >> <html> <<friends('frienddb','My Friends','JUSTFRIENDS')>> </html>
I have followed this exact pattern more times that I can count. The only problem is that it is completely wrong and doesn't actually make my code any more extensible. All it really did is create a black box that I can use on other pages, but that black box is to rigid.
So the temptation is to try to tack on extensibility to a wrong pattern. I might add additional parameters to send to my function etc.
The problem is this pattern will not extend and is destined to be relegated to code that once was cool but now is a pain to maintain.
So the fundamental flaw here is the failure to separate concerns. There are at least 2 concerns going on.
- We Get Data
- We Present Data
So we should really be separating our code into...
- Code that gets data
- and Code that presents data
And this pattern can be expressed in several ways. We can decide to go with functions or we could decide to go with pages. Both are perfectly fine.
Here is a page approach...
<< # # Begin GET DATA # # First we have all the logic to get data # notice there IS NO HTML here # If you feel the need to add HTML here then STOP and # think it through, make sure you are keeping your # concerns separate /# if mypref='showall' then thedata=getfriends('all') else thedata=getfriends('justfriends') /if # # End GET DATA /# # # Begin Presentation /# if mystyle='modern' then goto 'modern.html' else goto 'traditional.html' /if # # End Presentation /# >>
<< # This page expects the following data # THEDATA - A Six column table record,name,address,city,state,friendstatus /# >> <html> << for name=thedata rowname=x do display x+'~'+x+'<br>' /display /for >> </html>
<< # This page expects the following data # THEDATA - A Six column table record,name,address,city,state,friendstatus /# >> <html> << for name=thedata rowname=x do display x+'|'+x+'<br>' /display /for >> </html>