Named arguments maken je code beter leesbaarder. In Visual Basic kon het, in Ruby kun je het met een hash en in Smalltalk is het gewoon verplicht. In de volgende versie's van C# en Scala komt het ook. In Java zal het wel nooit komen, maar er is wel een manier om het te simuleren in Java.
Wat is het nut van named arguments? Neem als voorbeeld de volgende methode.
1
| public void setMargin(int top, int bottom, int left, int right){..}; |
Bij de aanroep krijg je iets van:
Zonder de method signature te kennen weet ik niet precies wat de code doet. Met named arguments heb je volgende code:
setMargins(top:=2, bottom:=1, left:=2, right:=1);
Ik hoef nooit te kijken naar de method signature of het uit mijn hoofd te kennen om te begrijpen wat er staat. Maar hoe kunnen we het doen in java. Je kunt simpele syntactic sugar gebruiken door static wrapper methodes te gebruiken:
1
2
3
4
5
6
| int top(int i) {return i}
int left(int i) {return i}
int bottom(int i) {return i}
int right(int i) {return i}
setMargins(top(2), bottom(1), left(2), right(1)); |
Dit maakt de code wat leesbaarder, maar het is niet meer dan wat commentaar. En net zoals met commentaar weet je nooit zeker of het echt klopt. Het is nog steeds mogelijk om de volgorde te wisselen en de lezer op een verkeerd spoor te zetten.
Maar wat als elk argument een eigen type heeft i.p.v. een primitive:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| public void setMargin(TopMargin top, BottomMargin bottom, LeftMargin left, RightMargin right);
static TopMargin top(int i) {return new TopMargin(i)};
static BottomMargin bottom(int i) {return new BottomMargin(i)};
static LeftMargin left(int i) {return new LeftMargin(i)};
static RightMargin right(int i) {return new RightMargin(i)};
class TopMargin{
int value;
TopMargin(int i){
value := i;
}
int getValue(){
return i;
}
} |
Het definieren van de classes is wel veel extra werk, maar dit kun je via een template in je IDE laten genereren. Het blijft wel extra code waarin fouten kunnen sluipen. Doordat je constructors niet kunt overerven in Java zal een Margin superclass niet veel helpen in het reduceren van de code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| class Margin{
int value;
Margin(int i){
value := i;
}
int getValue(){
return i;
}
}
class TopMargin extends Margin {
TopMargin(i){
super(i);
}
} |
Ik heb deze techniek gebruikt bij het schrijven van JUnit testcases. Ik had daar veel literals als input, maar het was niet goed duidelijk of ik het had over een aantal, een prijs of over kosten.
1
2
| buyPhilips(100, 100, 10);
p.setCurrentQuote(createQuote(philips(), 200, 100, 100)); |
werd dan:
1
2
| buyPhilips(amount(100), price(100), costs(10));
p.setCurrentQuote(createQuote(philips(), price(200), lastYearPrice(100), lastQuarterPrice(100))); |
Het is nu duidelijk waar de argumenten voor staan en het is ook typesafe.