September 6, 2010

Rock S.O.L.I.D. Sept 16th

 

Talking of OO design, we still have a handful of places left on the OO design master class being run at (and in aid of) Bletchley Park on Sept 16th.

It's in a very good cause, and it's a packed and hands-on course that will provide a practical kick-start for OO newbies and stretch experienced developers.



"Ah, But What About Interface Segregation?" I Hear you Cry

 

Ah, but what about Interface Segregation, I hear you cry?

Yes, it's true. There is an OO design principle that tells us that objects should present client-specific interfaces. So if a Woman plays the role of Mother with respect to her Daughter (who is also a Woman) and a Boss with respect to her Employee and an Author with respect to her Book then we might want to have Woman implement a Mother interface for the maternal responsibilities, a Daughter interface for the other end of that relationship, a Boss interface for barking orders and an Author interface for collecting her royalties.

But didn't I say that interfaces should be implemented more than once? Nope. What I said was that if we follow a process of discovering abstractions during refactoring than we might expect to find that interfaces are implemented more than once. A nitpicking point I know. But it's important to clarify that one is the goal (discovery of abstractions) and the other is an indicator.

But even then, it's absolutely true that on this point (and this point alone) I'm not aligned with the GOOS school of OO design. I tend to feel that an object that's playing multiple roles often turns out to be breaking another OO principle - namely that classes should only have one reason to change.

It all gets a bit handwavy and vague here, I'm afraid. Which is probably why the debate goes round in circles. Truth is, we just don't know. Not for sure. In OO design, we tend to find that there's no principle or rule of thumb that's always true in all cases.

Firstly, Steve and Nat aren't saying something as simplistic as "program to interfaces". If that were true then their approach wouldn't work in, say, PHP. Just as I'm not saying "interfaces should be implemented more than once". They're saying that objects should collaborate with roles (Mother, Daughter, Boss, Author) not types (Woman). They just so happen to model roles using interfaces in their chosen languages. From a dependency management standpoint, that's good advice. "Program to interfaces", on the other hand, is not. Which is why they don't say it. Even if that's what some people choose to take away from what they actually do say, just as some might choose to take away "implement interfaces more than once" from what I say, instead of "introduce abstractions when you see multiple examples of something during refactoring". And as Kent Beck rightly points out, even that is oversimplifying it.

You will find in my code classes that have multiple responsibilities and perform multiple roles, and - gasp! - have multiple interfaces. But in my code they are probably rarer than you might find in other people's code.

Here's how I slice it and dice it: if a class plays multiple roles that all relate to the same data, then splitting that class up into multiple role-specific classes would introduce higher coupling, since the methods on those new classes would have to refer back to the fields of the original. Feature envy, basically.

On the other had, if we had loosely coupled clusters of methods and related fields for each of those roles inside that original class, I'd probably split the class up.

In both schools, we favour organising our code by roles and responsibilities. The Reused Abstractions Principle (RAP) is a counterweight to balance out the tendency to put interfaces on everything which is increasingly widespread. So when I teach OO I throw it in there as a "please don't go mad with the interfaces" kind of appeal, and also to give extra weight to the discipline of discovering rather than inventing abstractions. Because I'm really a feedback-driven design kind of a cat.

I may have a lower tolerance for interfaces than some. And that's based on my subjective experience, is all. Those who apply them more liberally also do so based on their subjective experience.

And that's fine. Because OO design is an art - even though there's some science underpinning it. Y'know. Some people can't listen to anything that isn't in 4/4 time. As a coder, I'm wired differently to many other coders.

The ultimate arbiter is the quality of the end result. So I would always - after hours of handwaving and telling people what I "like" in a design - encourage people to collect some more objective evidence by analysing the dependencies in their code and seeing if, overall, things are better or worse in the dimensions of quality that interest us, like coupling and cohesion. My confidence in my approach comes from that, but I know there will always be room for improvement and that other approaches can achieve similar results.

What I have less time for is overly simplistic design formulas and "rules of thumb". To their credit, and there's a lot to credit them for, the GOOS guys don't fall into this trap. Their approach is nuanced and they give ample room for skill and judgement - things that take years of practice to develop.

The danger with any approach is that inexperienced developers might choose just to take away the simpler truisms and apply them religiously as the "formula for good design". In reality, there's nothing we can do to stop that, and some developer communities seem more inclined this way than others. RAP has always been an attempt to balance some of these formulas, and should not be considered an OO design principle of the first order.

Now, some people argue (with some merit) that we've all got to start somewhere, and that these formulas are useful for novice OO designers. I would be more open to that idea if there was such a thing as "novice OO design". I can teach a novice chef to boil an egg. Software, in practice, starts challenging and goes downhill from there. You can't turn a fiendishly difficult ski slope into a "novice" ski slope just by telling people "try to keep to the left" before you send them hurtling down. So I avoid the truisms whenever possible and try to focus more on the underlying mechanics of OO design - which is the mechanics of dependencies - right from the start. The rest is a process of helping them to build up their experience in applying these basic principles, which is where the real knowledge comes from.

Indeed, it's very hard to discuss any aspect of this complex discpline without unwittingly introducing "certainties", just by dint of the way language works. The most meaningful way to make a point is using examples. And it takes a lot of examples to really get a grip on OO design. Hundreds, possibly thousands. And then we develop a knack for it. And the trouble with knacks is they are nigh on impossible to communicate with any fidelity. If you don't believe me, try teaching someone to ride a bicycle using PowerPoint slides.

Crikey. Now it's definitely time for some lunch.


September 5, 2010

Reused Abstractions Principle (RAP)

 

Just time to revisit an OO design principle that I include in my courses - the Reused Abstractions Principle (RAP).

Planned reuse has been widely discredited in software development, especially after the Big Push For Reuse that we saw in the 90's.

Writing code on the basis that it may be applicable in multiple future scenarios is speculation, and speculative generality is considered a code smell. The danger is that when the future comes, the reusable generalisation we planned turns out to be not quite what is actually needed, or doesn't quite fit all the scenarios, or - more usually - that the future eventuality never comes up.

Software development is hard enough when we have to write code that satisfies today's requirements. Adding in a whole bunch of "what if?" code makes it even harder, we have found.

The Agile approach to software design is more scientific. When we see two examples of something, we refactor to remove that duplication. This may involve introducing a common base class, or a common interface. In short, we discover these abstractions during refactoring. They are then guaranteed to be reused, because they demonstrably already have been.

Reuse is a bit of a red herring economically. The real killer is the not the cost of writing new code. It's the cost of changing old code. So we should focus on combatting duplication as soon as it emerges, and not speculate that it might emerge later and on what form it might take.

If I was looking for signs of this discipline of discovering abstractions, then there's a clear indicator. Just as TDD implies very high test coverage (if your coverage is not high, you must have written code that wasn't required to pass a failing test), discovery of abstractions implies that abstractions are used more than once. Super classes have more than one subclass. Interfaces are implemented more than once. Abstract methods are overridden multiple times. And so on.

I introduced this principle 10 years ago when discussing OO design to counter the trend for speculative abstractions in many of the projects I encountered. Most particularly, the trend for putting interfaces everywhere, regardless of whether that interface represented some commonality betweren two or more types of object - which is what they're really for.

So I will stress that "programming to interfaces" is not good OO design, any more than "don't use getters and setters" is. These are overly simplistic formulas that mask more fundamental and nuanced principles. If the only class that ever implements the Customer interface is CustomerImpl, you don't really have polymorphism and substitutability because there is nothing in practice to substitute at runtime. It's fake generality. All you have is indirection and code clutter, which just makes the code harder to understand.


August 20, 2010

[Video] Rock S.O.L.I.D. - S is for Single Responsibility

 



Book your place on Rock S.O.L.I.D., Bletchley Park, Sept 16th. All proceeds got to charity.



July 12, 2010

Object Oriented Design Master Class, London Aug 21-22

 

This summer's run of budget-friendly weekend master classes in software craftsmanship ends with a workshop on object oriented design on August 21-22.


In this very hands-on course, you'll learn about OO design principles (S.O.L.I.D. and more), using real code examples to illustrate how to refactor designs that violate these principles. You'll also get practical experience of using code analysis tools to measure OO design quality and detect problems more easily, even in large code bases. The workshop will also give you a chance to apply simple OO analysis and design techniques in a test-driven approach to development, as we as looking at how up-front design can be balanced with refactoring to produce optimal designs without falling into the shark-infested waters of "Big Design Up-Front" and "no conscious design at all".


As with all the workshops I'm running this summer, there'll be the minimum of talk from me and the maximum of hands-on practical experience and learning by doing, working in pairs with other attendees.


And - as if the price wasn't good enough already - everyone who attends both the TDD and refactoring courses will get to go on this one absolutely FREE. I must be mad! Book your place now before I change my mind!



July 10, 2010

Give it a bloody REST!

 

Another year and yet another way to make software on one box talk to software on another box.


The latest* in a long and ignominious succession of standards and protocols for remote communication between applications is REpresentational State Transfer (or "REST", for short). The title implies that the notion of representing state in some way that can be transferred is news to many of us. Of course, every single RPC standard has to find a way to represent state so that it can be transferred. That's kind of fundamental to it.


REST uses the transport protocols of the web in a very simple and straightforward way. HTTP POSTs and GETs, DELETES and so on. Objects are identified in URIs, much like they are in every dynamic web application ever built. But instead of returning us a web page with our DVD information rendered within, it returns some XML or something like that.


References to other related objects can be embedded in this XML as hyperlinks, so the client application can get a handle on another REST call if it needs to navigate.


Now, the ex-architect in me gets the screaming heeby-jeebies when I see the kind of data-centric architecture that REST seems to encourage (essentially, CrUD for web-based objects, if you think about it). We espouse responsibility-driven design at the object level in code and a "tell, don't ask" style of design to encourage fewer and higher-level interactions between objects as a way of minimising dependencies. REST does not look like responsibility-driven design to me. It looks like web-based data/object stores that can save, retrieve and delete objects via the appropriate HTTP command. I think we will live to regret this. If dependencies at the code level hurt, dependencies at the enterprise level can be lethal.


Honestly, though I'm glad we're pairing it back to something simpler than SOAP, I just can't get excited about this. I've lived through CORBA, COM, DCOM, RMI, .NET remoting, XML-RPC, SOAP and blah blah blah blah blah. And I'm here to tell you that the protocol has never been the challange. Not ever. We cracked that problem years ago.


The problem has always been: what do the messages mean? The challenge in building distributed, asynchronous, high-volume, heterogenous enterprise systems - be they web-based or mainframes - has always been co-ordinating design and testing across multiple systems, written by multiple teams, often in multiple locations, working for multiple organisations, sometimes speaking multiple languages and having multiple cultures.


The glue that binds the systems together is trivial, and irrelevant if those joins don't dovetail neatly together in the first place. SOAP was not a problem.


Know why? Because automated tools generated the glue in many (most) cases. just like it did with COM and CORBA. We never saw it and never cared until it went wrong and we had to fiddle. I see teams all the time now fiddling with their REST interactions, because we're back to hand-rolling the glue for the umpteenth bloody time!


I repeat. The problem has been solved. So stop solving it already and focus on the logic of those inter-system interactions and let a tool do the gluing.


* Although, technically, as old as the web almost. But now fashionable

April 27, 2010

Gorman's Law

 

Gorman's Law of Software Design: Any aspect of software design that is not expressed in code does not exist.

Take heed, PowerPoint jockeys!



April 24, 2010

Software Is Both Art & Science. Can We Move On Now?

 

Bonjour, mes enfants.

SEMAT has gotten me thinking. Any mention of "engineering" and "science" in software development seems to polarise opinion.

Undoubtedly, there's a large section of the software development community who believe those words simply do not apply. Software development is not a science. It's an art. Or a craft. Like basket weaving. It's not engineering. No sir!

And there's an equally large section of the community who believe the exact opposite. Software development is a science. It is engineering. We can apply scientific principles to shape predictable, well-engineered end products.

Of course, they're both wrong.

Anyone who dismisses the notion of any kind of scientific basis for software development is running away from reality. Everything that exists has a scientific basis. Even American Idol. You just have to understand it. That we don't fully understand the science of software does not mean that no such science exists or that we'll never understand. I just can't help being reminded of UFOlogists who claim that "science cannot be applied to the study of UFOs". What they mean, of course, is "I've got a good thing going here selling my unscientific ideas to these schmucks, and I'd like to keep it that way, thanks".

And anyone who believes that software development can be completely tamed by science is equally deluded. There are sciences - emerging in recent decades - that teach us that there are many things in the world that, while it's possible that we can fully understand them, we will never be able to control them. Chaos is science. And software development is mostly chaos. Any vision of "software engineering", where pulling lever A causes X and pulling lever B causes Y with certainty, is the product of naivity at the macro, project scale. Life just isn't like that. Clockwork might be, but life isn't. Software development is intractably complex and unpredictable.

In the real world, biology has come up with processes that deal effectively - but not predictably - with intractably complex problems. Evolution is one such process. Evolution solves complex, multi-dimensional problems by iterating through generations of potential solutions. That is science. We can understand it. But we cannot even begin to predict what solution a process of evolution will reach, or how long it will take to reach it.

The clockwork, Newtonian paradigm of "software process enginering" is fundamentally flawed. And anyone who believes that it's possible to attain "value" deterministically is deeply mistaken. "If we pull lever A, we'll ship an extra 10,000 units". Give me a break!

Flawed, too, is any notion that this means that there's no engineering at all to be done in creating good software. I doubt anyone would claim that making rock music is "engineering" - well, anyone sane, at least. But there is science that can be applied within this process.

It's possible mathematically to predict what effect the choice of software compressor and the compression settings used will have on the amplitude of a recording across a certain frequency range. Indeed, it is helpful in getting the best-sounding mix. There is such a thing as "audio engineering" within the music production process. Granted, it's chiefly a creative process. But there is useful science we can appky within in to help tweak the results closer to perfection.

Similarly, while software design is chiefly a creative process, it can be useful to know if the code we're writing "smells" in any significant way. Some code smells can be detected just by looking at the code and using our judgement. Others are more subtle and harder to spot, but just as damaging to maintainability. Code analysis tools, as they grow more sophisticated, can complement our "eye for good design" every bit as much as audio engineering tools complement our "ear for a good mix" and music composition aids can complement our "ear for a good tune".

The trick, I believe, is to find the balance and work within the limitations of science and engineering and creative disciplines. Trying to figure out the formula for "valuable software" is every bit as futile as looking for a formula for making "hit records". And relying entirely on your eyes and ears to refine an end product has severe limitations. For millenia, we've used tools and theory to tweak and refine all manner of creative end products. We even have a word for it: "machine-tooled". The fact that you can fit a computer more powerful than all of the computers in the world were 30 years ago in your breast pocket, and it looks good, is testament to this symbiosis of science and art.

We can continue to refine and extend our scientific understanding of code and coding through research and exploration, just like any science. And new and useful tools will emerge from that understanding that will make it possible for us to produce better quality code, for sure.

And we can also continue to develop and refine the art of software development. Through reflection, practice and sharing.

There is such a thing as "software engineering", but it has limited scope, just like "audio engineering". Specifically, it's limited to things we can predict and can control, like what happens to coupling and cohesion if we move a class from one package to another.

The bigger picture of "delivering value" is a complex human endevour, and creativity, judgement and more than a sprinkling of luck is all we have that we can bring to bear in any meaningful way at this level. We may be capable of understanding, with the benefit of hindsight, why Feature X was used more than Feature Y when the software was released. But then, with hindsight, we understand quite a lot about volcanos and hurricanes, too. These are things that can only be understood with hindsight, really. We don't see them coming until they're almost upon us, and we have two choices - stay and risk everything or get out of the way and live to fight another day.

In years to come, I'll probably notice more and more a difference between "hand-rolled" software and software that has been written with some help from "software engineering" tools - the more grown-up descendants of tools like XDepend and Jester.

But I sincerely doubt I will ever be able to tell at the start of a project whether the resulting software will enjoy success or not. Sure, I'll be able to look back on projects and say "hey, y'know what we got wrong?" But far in advance, the outcome is every bit as unknowable as a hurricane, volcanic eruption or hit record. So where things like requirements and processes and "enterprise architeture" are concerned, I'll stick with arts and crafts.


February 15, 2010

Wheel-driven Reinvention

 

One aspect of software development which is at once both amusing and troubling is the ability of us young whippersnappers to completely ignore what's gone before and reinvent established wisdom in our own image - often stealing the credit.

Take testing as an example. What do we know about testing software today that we didn't know, say, thirty years ago? Sure, we have new tools and testing has to fit within new approaches to the process of writing software as a whole, but fundamentally what have we discovered in the last decade or so?

Testing behaviour still works, by necessity, much as it has always worked by necessity. We must put the system under test in some desired initial state, then we must provide some stimulus to the system to trigger the behaviour we wish to test, then we must make observations about the final state of the system or about any behaviours that should have been invoked (e.g., a remote procedure call or a database request) in response to the combination of our stimulus and the initial conditions. And this process must be repeatable and predictable, like any good scientific test.

Though the culture of testing software may have evolved, much of it for the better, and the technology may have improved (though that is questionable), and though there are undoubtedly more people testing their systems today, when it comes to the business of writing and executing tests, there's really nothing new under the sun.

The same is true of many aspects of contemporary software development. Like it or nay, iterative and incremental development is older than C. We just weren't doing it back then, in the main.

Indeed, pick any "new" aspect of development and trace it back to its roots, and we discover that most novelties are actually much older than many of us thought. Objects are an invention from the sixties. Use cases hail from the seventies. Responsibility-driven design was being practiced before Frankie told us to Relax. UML existed in a fragmentary form before the Berlin Wall came down. People were writing code to satisfy tests back when those tests were stored on magnetic tape. Indeed, some of the descriptions of programming that was done for the very first computers rings bells with those of us who practice that black art today.

Younger developers like me, though, seem to readily believe that our time around is the first time around and feel no compunction to educate ourselves about the achievements of "old-timers", preferring instead to invent things anew - with sexier names and shinier tools, admittedly.

Our desire to reinvent goes as far as redefining words that already have a well-established definition. "Agile" no longer means "nimble" , "quick" or "spry". Today it apparantly means "communication, feedback, simplicity and courage". Or "iterative and incremental". Or "evolutionary". Or "Scrum-Certified". I caught someone the other day proferring their definition of "testable", which apparantly now requires us to go through "public interfaces". This is bad news for many scientists, who must now rewrite their peer-reviewed papers to incorporate the appropriate programming language with which to express the "testability" of their theories.

If software development was physics, we might expect newcomers to work through and understand the current body of knowledge before they start adding to it. That way, at the very least, we could avoid a great deal of duplication of effort. We may also avoid the tendency of our industry to throw "old-timers" on the scrapheap just because, even though they are probably just as current in their practical ability to deliver working software, they're not "down with the kids" on all the latest street slang for concepts that have been kicking around the block for decades.

The thinking of our elders and betters is far from irrelevent and outmoded. We can still learn a thing or two from the likes of Jacobson, Knuth and Hoare, should we choose to reject fashion in favour of substance in the approach we take to our work.

November 12, 2009

Our Goal Is To Minimise Coupling Between Classes. Not To Eliminate Getters And Setters.

 

As I'm sure Jesus and Marx would attest, publish and be damned.

There's always a danger when you make a point that's in any way nuanced or open to interpretation that the huddled masses will switch their brains off and choose not to think about what you've said, but to cut it down to a set of pithy, out-of-context, oversimplified truisms - often completely missing the point you were trying to make.

We're very guilty of that in software design. Why go to all the trouble of agonising about the many and complex design tradeoffs that need to be made to create high quality software when we can just learn a handful of soundbites which we can mindlessly apply regardless of the reality of the situation?

From the eponymous "GOTO considered harmful" to more recent design-isms like "singletons are bad" or "use dependency injection", there's no shortage of easy-to-learn rules and formulae we can follow that will exhonorate us from having to actually think about the code in front of us and what would be best for that.

Now I'm hearing a new design-ism: don't use getters and setters. I've watched discussions unfold on the Interwaste which display an alarming lack of insight or even a vague interest in why we shouldn't be using getters and setters.

And, let's be frank, just as with GOTO, singletons and dependency injection, it completely misses the point. Our goal is to minimise dependencies between classes and to maximise the internal cohesion of classes. We want to put the behaviour where the data is, and we want to avoid wherever possible sharing data between classes.

But, just like income tax, we can't avoid it altogether. If we can't share data between classes, then we'd have to put everything - data and behaviour - in one big class. You cannot have modularity and reuse without sharing something. So there's a trade-off that must be made.

The "tell, don't ask" school of design requires us to favour interactions between objects where A tells B to do X, and B has everything it needs to do X. As opposed to A asking B for all the data needed to do X, and then having A do it.

A simple example would be to have a Java servlet use getters on a Customer object to retrieve the customer's first name, last name and title (Mr, Mrs, Dr etc) and then concatenate them together to give the full name for display. Since Customer owns that data, such behaviour arguably belongs in Customer, not in the servlet.

But if the same servlet wants to format an HTML table containing the customer's first name, last name and title then that might be a different situation. I would argue that this is a clearly seperate responsibility relating to what the servlet knows about, and doesn't belong in the Customer class. So how does the servlet get hold of these bits of data without using getters?

Does it pass in a TableFormatter object with an InsertData() method that the Customer object can call with first name, last name and title? That seems like quite a wanky solution to me.

There are times when getters are needed. You may come up with some clever method names to try and disguise your getters. But they'll be in there somewhere. End of.

That we seek to minimise those occasions is important. But our goal here is not to eliminate getters, and it's vitally important that we remember that.