Mindre kod, mer struktur

I väldesignad kod ser man ofta att mängden sammanhängande (jag använder en eufemism sedan ”procedurell” blivit ett skällsord) kod går mot noll, och mängden strukturelement mot oändligheten. Med ”strukturelement” menar jag språkets definitionselement — för typiska OO-språk skulle det vara klass- och metoddefinitioner.

Den här tendensen förstärks t.ex. av objektorienterade designmönster, som i sin iver att utnyttja polymorfism ökar antalet klasser för att kunna minska mängden villkorslogik.

Se på ett litet C++-program:

class Greeting
{
public:
Greeting(const std::string& target)
: m_target(target)
{
}

void Utter(std::ostream& channel)
{
channel << "Hello " << m_target << "!"; } private: std::string m_target; }; int main() { Greeting greeting("Dolly"); greeting.Utter(std::cout); return 0; } [/sourcecode] Vi har egentligen bara tre rader procedurell kod här, implementationen av Greeting::Utter och användandet av Greeting i main. Det betyder att 20 rader består av strukturelement. Av dem innehåller i sin tur tre (Greeting, Utter och main) namn som skänker någon sorts betydelse till programmet. Resterande 17 är mest syntaktiskt oväsen.

Fast jag hade inte tänkt smutskasta C++ & co här, läs Fowlers artikel för mer tankar om olika språks uttrycksfullhet.

Snarare vill jag kasta ut en fundering: skulle man kunna använda ration mellan strukturelement och kodelement som ett mått på designkvalitet? Något säger mig att det skulle funka ganska bra för språk som C++, Java och C#…

Annonser

Intra-testning

John Carter beskrev en teknik på TDD-listan för ett tag sedan, som jag tog djupt intryck av.

Han beskriver någon sorts fattigmans Design By Contract, som i vaga ordalag går ut på att man strösslar assertions i sin kod för att beskriva antaganden.

Det Carter säger, och som jag till slut förstod, var att detta egentligen är samma sak som en assertion i ett enhetstest — bara inbäddat i koden. Vad jag inte hade skänkt så mycket eftertanke var att dessa assertions, precis som dem i enhetstester, faktiskt möjliggör säkrare refactoring.

Jag vill kalla det här för intra-testning, eftersom systemet i någon mån verifierar sig självt (jag var sugen på endo-testing, men det visade sig vara upptaget. Skit också.)

Vi kanske inte ska kasta xUnit till grisarna riktigt ännu. Förutom att beskriva antaganden är en solid test-svit också ett överlägset sätt att automatiskt köra igenom all kod som täcks av testerna i de spår som dragits upp av testfallen. Att lämna assertions i vår produktionskod ger oss fortfarande inget stöd i att driva den maskinellt.

Men jämför med hur du skulle testa en förändring utan vare sig tester eller assertions. Manuellt, eller hur? Peta och klicka lite, kolla så att allt ser rätt ut? Utan automatiserade tester är det det bästa vi har för att driva systemet, men genom att lägga till intra-testning kan vi ersätta trött okulär besiktning med automatisk evaluering av systemets tillstånd.

Jag säger inte att man bör föredra intra-testning framför enhetstestning, men jag tror att det kan användas för att göra de första penseldragen stadigare när man omarbetar otestad kod för att kunna få den under test.

Michael Feathers nämner aldrig den här tekniken i Working Effectively With Legacy Code, men den hör nog hemma tillsammans med de andra under rubriken ”How do I know that I’m not breaking anything?” som beskriver diverse sätt att bryta beroenden.