A big deal in the world of programming has to do with what's called "separation of concerns." Put simply, parts of a system (in this case, a computer program) should have as little overlap as possible. This ends up making things more modular, which is techy-talk for having individually replaceable (or change-able) parts. Think of it like parts in a car. You can usually replace your tires without fear of screwing up the starter, because the two aren't really connected even though they both go into the making and design of a car. You can do this because parts of a car are distinct and discrete. Tires come off. Seats and doors and windows are removable.
Separating concerns leads naturally into the "single responsibility principle," a term coined by the great Uncle Bob. When designing your parts, give them only one responsibility (or concern, if you prefer). This takes separating concerns to its extreme by giving everything only one, which is as separate as you can get.
Of course, like many things in life, these are more ideals than truly attainable goals. Like perfection, these "rules" are generally seen as worth shooting for but with the understanding that sometimes you'll have to break 'em. Getting close (instead of exact) keeps the time required down to realistic levels, since shipping is the ultimate goal.
So many other generally lauded ideas in programming stem from this separation/modular mentality. Test-driven design fits neatly into this because you design and build your parts organically from test requirements. This very easily leads to pieces that do only what they need to pass the tests (which are derived from the requirements of the application) and no more. Inversion of control and dependency injection solve the problem of putting all of the modular parts into a working whole.
At the heart of all of this is the desire to make pieces of code independent of others. The bugs in another part are the other part's problem and can thus be isolated and dealt with specifically without touching or even looking at the other parts that might indirectly or even directly use it. It means you won't need to go snooping through the engine to see and fix the hole in your tire. In fact, you need not even care that there is an engine at all, because the hole and tire can be serviced independent of the rest even by someone who has no knowledge of cars.
Isolation. Independence. Where have we heard these ideas before, outside of programming? Oh. Right.
Much of the principle of freedom that liberty freaks such as myself espouse goes right back to this basic thinking. Parts should be independent from others. Reducing overlap. My land is independent of your land (a discrete and literal line in the sand is drawn) and I am free to do whatever I want within that space so long as I do not infringe upon your ability to do the same in yours.
Parts of computer systems work by "contracts," usually enforced by the language. A piece of code presents this contract to other consumers (pieces of code that want to use it) as a way of guaranteeing its intended use. Bugs, generally, are when the practical use is out of step with the contract or intended use. Consider a simple math function (in most C-like languages):
public int Add(int x, int y)
- He is public, so anyone can use him. This establishes who may use him.
- He returns an integer type of data. Using this function, he promises to return an integer of some kind.
- He is called Add. This is usually only for distinction purposes, but it also usually serves to tell the human programmer, in English, the basic purpose.
- Here we can plainly see he is called Add, so we, the consumer, can assume he will add things and return an integer. So far so good!
- He takes two parameters, both integers, named x and y. Finally, we get the whole picture.
From this basic prototype, the code has established a contract. As someone wishing to use this function, we can and should, barring any bugs, assume that he will work as such: take two integers, add them together, and return the result to us.
He does one thing, and he (hopefully) does it both validly and reliably. It is valid if he actually adds them, not subtracts or something else. It is reliable if he does it in a consistent speed and manner, without wild fluctuations based on when you happened to use it or what parameters you passed it.
It is a simple and contrived example, but he clearly has one concern: adding two integers. It is assumed he will have no dependencies or interactions with other parts, like HTML parsers or audio encoders. He won't mess up your fancy new division function you've been working on and any problems he has are clearly contained within him. So long as Bill down the hall is around, you can yell at him if you find problems (since he wrote it). Likewise, he'll holler back at you if he has trouble with your division function.
Contracts. Expectations. Arbitration. We do the same things in life. I pay AT&T to provide me internet access. This is a contract we've agreed to. Should I have trouble with it or should I fail to pay, we can try to work out fault and correct it. Should either side be unwilling, then third parties can be called in (in this case, courts or collection agencies and the like; in the previous programming example, this might be your project lead or some kind of boss/manager).
But my contract with AT&T doesn't and shouldn't interfere with your life. My use or non-use of AT&T or any other ISP doesn't and shouldn't matter to you.
When you force me to use Comcast, that's unjust, even if Comcast is perfectly fine and works just as well. The end results being practically equal doesn't justify the act of coercion.
As liberty nuts, this is the root of our criticism of most of what governments do. Banning the sale of incandescent light bulbs; mandating the maximum water a toilet can use per flush; requiring my kids go to school; forcing me to purchase health care. Some of these may have good and desirable results that I agree with. I use mostly CFLs anyway, I don't like that our old toilets drive up my water bill bit by bit, I value strongly a good education, and I purchase and use a health care plan. Even so, it's not your place to coerce me to do any of these.
Good system design involves separating concerns and keeping parts independent of the others. It may be an unrealistic ideal: my integer Add function might need to use a common Math library and our government might need to steal money from us to fund courts and military. Even so, striving for independence produces better code and better nations.
No comments:
Post a Comment