PHP in Action

More beautiful code

dagfinn | 07 November, 2008 20:34

I got some interesting comments to my previous post on "beautiful code". Some were pretty strong disagreements.

So am I wrong? Did I get carried away? Did my critical faculty go on vacation somewhere nice and sunny? I admit that sometimes I deliberately look at the positive and ignore the negative. (And sometimes I do the opposite; It's a good exercise if you're careful.)

I wasn't drunk, anyway. But let me take a closer look at the particular line of code I was praising:

$this->assertThat($form->hasSelect(withName('statusConfirm'))->hasValues(),
    array('Yes','No'));
 

My main point is that it's close to plain English. Not everyone agrees that that's a good thing, but I argue that we're built (genetically wired, in fact) to understand natural languages, not program code. Therefore code should be easier to understand when it approximates natural language and expression. And we're trying to create or approximate a Domain Specific Language (DSL), which should express exactly what's required for the domain and not the demands of the technical implementation.

So for this experiment, let's translate this one into a (plain English sentence:

Assert that Form (this particular one) has a select menu with the name "statusConfirm" and values "yes" and "no"

Translating back into code, it might look more like this:

$this->assertThat($form)->hasSelect()->withName('statusConfirm')->andValues('yes','no');
 

To me, this is even more natural than the other one. I think we've gotten rid of some syntax that has to do with implementation details rather than making the API simple to use.

It also seems clear to me how this could be implemented. All of the method calls could be to an assertion object that would take all these various inputs and always return itself at the end of the method so you can chain the calls in what's known as a fluent interface.

Share and Enjoy:These icons link to social bookmarking sites where readers can share and discover new web pages.
    blogmarks del.icio.us digg NewsVine Reddit

Comments

Re: More beautiful code

Ilia Jerebtsov | 07/11/2008, 22:34

Trouble is, it's not a DSL, and it's not English. It's still PHP code, and when I look at what I expect to be code, I will parse it as code. It might be helpful for a tester, but a programmer would stil see it as a set of method and function calls.

$this->assertThat(
$form->hasSelect(
withName('statusConfirm')
)->hasValues(),
array('Yes','No')
);

This format actually makes it more dificult for me to count the references - something I do automatically when reading code.

If you want a DSL, then you might as well make it a full blown DSL, like SQL:

$this->assert( 'FORM HAS SELECT ( WITHNAME "statusConfirm" AND HASVALUES ("Yes", "No") )' );

Re: More beautiful code

zilenCe | 08/11/2008, 02:40

The problem with the chaining implementation is that it does not know, when it is the last on the chain and when to check the assertion. You need a method like ->assert() or ->end() or ->check() or whatever it is named, thus it is required to add an additional call at the end of the assertion, making it less fun to read.

Also, considering that most methods have simplified names where you can interpret a lot of stuff into them you may end up having a unit-test class with several hundred methods (noone can remember that). Using assertTrue and assertFalse is much easier than having ->hasValue(false) ->isFalse ->isNotTrue ->isNotFalse ->isTrue ->isTheValueIWant(false) just to create easier "sentences".

Re: More beautiful code

Dougal Matthews | 08/11/2008, 04:52

If you really want something that reads more like english... try python!

Re: More beautiful code

dagfinn | 08/11/2008, 05:22

dagfinn

I must say I'm fascinated by the number of objections I'm getting here. Let me answer some of them.

@Ilja: Of course you could have a full-blown DSL, but it's more work to implement.

@zilenCe: I haven't designed this in detail, and I might want to do it differently after thinking more about it. Still, I don't think you would have to add an extra call at the end. The chain would involve the object generated by the assertThat() call. $this, the test case, would receive the final result and be able to check the assertion.

You say that assertTrue and assertFalse are easy to use. Of course, but they are unable to express anything complex. Which is what I'm trying to do here.

Re: More beautiful code

Simon | 10/11/2008, 02:08

Thanks for raising this subject.

I see your point, but I have so many objections to so-called "fluent" interfaces that I never know where to start. Please take none of this personally!

i) I already know PHP and - for example - xUnit, why do I have to learn your own hand-rolled language simply to maintain your code?

ii) "Fluent" interfaces _never_ read like fluent English. They, without exception, read like a clumsy, uncomfortable hybrid of English and whatever programming language is in use.

iii) If the interface did manage to read like English...how does that help the millions of people that are fluent in, say, PHP but don't think in English in the first place?

Re: More beautiful code

dagfinn | 10/11/2008, 07:05

dagfinn

@Simon: That's OK, I don't take it personally. Objections help me think.

i) It's an extra API on top of the existing test framework's. You don't have to use it if you don't want to, but the intention is to get more compact and less cryptic code.

ii) Perhaps it looks clumsy. My criterion is that it takes less time to understand the code if you haven't read it in the last month and need to update it.

iii) Any understandable API needs to use words in some natural language. English is just the usual choice. I'm just discussing the principles. If you want to use words from German, Esperanto or Buganda, the principles are the same.

Re: More beautiful code

brem | 13/11/2008, 08:25

The problem with your second "sentence" is that it denatures the meaning of entities.

A function that has the name "hasValues" has an expected behaviour. It will tell me if the calling structure or object will contain the values in parameters. Whereas, a function called "andValues" means absolutely nothing.

So yes, you get a pretty english sentence, but you lose the meaning of what you're doing. So in the context of your little sentence, it works fine, but in the greater context, in the next lines of code, a function "andValues" has "little value". :)

brem

Re: More beautiful code

dagfinn | 13/11/2008, 11:56

dagfinn

Good point. If I wanted to implement it this way (I'm not sure, not having considered it sufficiently) andValues() would probably be a piece of syntactic sugar, implemented as an alias for hasValues(). The Ruby mock framework Flexmock has something similar: "returns is an alias for and_return".

Re: More beautiful code

Open | 30/11/2008, 09:09

This api is not easy in use. The code is very long, more complicated things are hard to describe.

 
Accessible and Valid XHTML 1.0 Strict and CSS
Powered by LifeType - Design by BalearWeb