[alg]Optimale lengte van een methode/functie

Pagina: 1 2 Laatste
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Jan_V
  • Registratie: Maart 2002
  • Laatst online: 21:22
Woy schreef op donderdag 20 januari 2011 @ 09:16:
[...]
DI hoeft niet perse via de constructor te gaan natuurlijk

[...]
Hoeft niet natuurlijk, maar als je het niet aan de constructor mee geeft, waar zou je het dan willen doen?
In een overload van een methode of iets dergelijks, of een property?
Nadeel is dan dat je weer in de gaten moet houden of je wel tegen het juiste object praat, anders zit je alsnog het hele systeem te testen.

Of zou je het op een andere manier willen doen?

Battle.net - Jandev#2601 / XBOX: VriesDeJ


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 10:15

Janoz

Moderator Devschuur®

!litemod

Topicstarter
Mijn voorkeur gaat altijd uit naar setters (of properties bij .net). Dat maakt het imho altijd wat leesbaarder dan een reeks parameters in de constructor.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • Davio
  • Registratie: November 2007
  • Laatst online: 06-01 16:46
Janoz schreef op donderdag 20 januari 2011 @ 13:23:
Mijn voorkeur gaat altijd uit naar setters (of properties bij .net). Dat maakt het imho altijd wat leesbaarder dan een reeks parameters in de constructor.
Ja, maar in .NET kun je de properties gelijk initialiseren bij het aanmaken (Object Initialization), werkt zo:

C#:
1
2
3
4
5
6
MyObject o = new MyObject()
{
    PropString = "Jaep",
    PropInt = 3,
    PropBool = false
};


Dan hoef je je constructor niet onnodig te overloaden. Parameters in constructors zou je eigenlijk alleen moeten gebruiken voor verplichte properties / fields.

Acties:
  • 0 Henk 'm!

  • Jan_V
  • Registratie: Maart 2002
  • Laatst online: 21:22
Hmm, als je met properties gaat werken, zou een dergelijke implementatie dan niet handig zijn?
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private IOtherClass1 _otherClass1 = null;

public IOtherClass1 OtherClass1
{
    get
    {
        if (_otherClass1 == null)
        {
            _otherClass1 = new OtherClass1();
        }
        return _otherClass1;
        }
    set{ _otherClass1 = value; }
}

Default je 'normale' object terug krijgen en als je iets anders wilt, dan een andere klasse setten.

Heb er nog steeds een minder warm gevoel bij als bij een constructor overloaden, je moet nu namelijk nog steeds weten welke klassen er allemaal worden gebruikt binnen je te testen 'unit'. Wanneer je deze in de constructor hebt staan, dan weet je zeker dat ze allemaal zijn geinitialiseerd met je eigen gekozen object. Wellicht initialiseer je teveel (heb je maar 1 object nodig ipv alle 5).

Misschien moet ik er eens een nachtje over slapen om het voordeel te zien.

Battle.net - Jandev#2601 / XBOX: VriesDeJ


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 10:15

Janoz

Moderator Devschuur®

!litemod

Topicstarter
Davio schreef op donderdag 20 januari 2011 @ 13:52:
[...]

Ja, maar in .NET kun je de properties gelijk initialiseren bij het aanmaken (Object Initialization), werkt zo:

C#:
1
2
3
4
5
6
MyObject o = new MyObject()
{
    PropString = "Jaep",
    PropInt = 3,
    PropBool = false
};


Dan hoef je je constructor niet onnodig te overloaden. Parameters in constructors zou je eigenlijk alleen moeten gebruiken voor verplichte properties / fields.
Ik ken .NET niet, maar zo te zien zou dat zeker mijn voorkeur hebben. Bij spring (DI bij java) kun je middels een annotatie op de setter aangeven dat een dependecy verplicht is. Dan krijg je nog steeds een foutmelding wanneer je (runtime) vergeet het verplichte veld te vullen.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • Davio
  • Registratie: November 2007
  • Laatst online: 06-01 16:46
Ik zie dat Microsoft er zelf ook een mooi verhaal over geschreven heeft: http://msdn.microsoft.com/en-us/magazine/cc163739.aspx

Acties:
  • 0 Henk 'm!

  • Killemov
  • Registratie: Januari 2000
  • Laatst online: 24-08 23:40

Killemov

Ik zoek nog een mooi icooi =)

Davio schreef op donderdag 20 januari 2011 @ 13:52:
[...]

Ja, maar in .NET kun je de properties gelijk initialiseren bij het aanmaken (Object Initialization), werkt zo:

C#:
1
2
3
4
5
6
MyObject o = new MyObject()
{
    PropString = "Jaep",
    PropInt = 3,
    PropBool = false
};


Dan hoef je je constructor niet onnodig te overloaden. Parameters in constructors zou je eigenlijk alleen moeten gebruiken voor verplichte properties / fields.
Huh? Zo maak je in Java een instantie van een ter plekke gedefinieerde class. (Anonymous inner class dus.)

De lengte van een method of function is ondergeschikt aan zo'n beetje al het andere. Een aardige tip voor de refactorjunkies is kijken naar de oppervlakte van de code. Als je veel geneste constructies hebt, 10 geneste for-loops anyone?, dan loont het om te onderzoeken of er een aantal uit kunnen worden getrokken.

Hey ... maar dan heb je ook wat!


Acties:
  • 0 Henk 'm!

  • Killemov
  • Registratie: Januari 2000
  • Laatst online: 24-08 23:40

Killemov

Ik zoek nog een mooi icooi =)

Janoz schreef op donderdag 20 januari 2011 @ 15:25:
[...]

Ik ken .NET niet, maar zo te zien zou dat zeker mijn voorkeur hebben. Bij spring (DI bij java) kun je middels een annotatie op de setter aangeven dat een dependecy verplicht is. Dan krijg je nog steeds een foutmelding wanneer je (runtime) vergeet het verplichte veld te vullen.
Dat is enigzins vreemd. De DI constructie zou er voor moeten zorgen dat de container die de class load dat automatisch invult. Dat lijkt me juist het hele doel van DI. Toch?

Hey ... maar dan heb je ook wat!


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Davio schreef op donderdag 20 januari 2011 @ 13:52:
[...]

Ja, maar in .NET kun je de properties gelijk initialiseren bij het aanmaken (Object Initialization), werkt zo:

C#:
1
2
3
4
5
6
MyObject o = new MyObject()
{
    PropString = "Jaep",
    PropInt = 3,
    PropBool = false
};
Dat is echter ook niks anders dan een andere manier van schrijven van
C#:
1
2
3
4
MyObject o = new MyObject();
o.PropString = "Jaep";
o.PropInt = 3;
o.PropBool = false;

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • Davio
  • Registratie: November 2007
  • Laatst online: 06-01 16:46
Nee, het is niet anders, maar ik vind het wel handiger, anders heb je overal die variabelenaam weer voor staan en die is vaak wel wat langer dan 'o'.

Acties:
  • 0 Henk 'm!

  • YopY
  • Registratie: September 2003
  • Laatst online: 13-07 01:14
Meuh, ik vind dat constructor initialisatie altijd de voorkeur zou moeten hebben over dit. Waarom runtime controleren wat je compile-time (of, in het geval van een beetje IDE, schrijf-time) kunt controleren? Nee, je hebt geen named variables bij constructors, maar qua syntax kun je nog wel zoiets doen:

Java:
1
2
3
4
5
Object obj = new Object(
   pietje,
   henkje,
   klaasje
);


Een lelijke oplossing om toch een soort van 'named' params te krijgen is het builder pattern misbruiken. Nee dit moet je niet overnemen, nee dit is niet ongelofelijk slim om te doen, en ja, named arguments zou een goed iets zijn:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class PietjeBuilder {
    public String pietje;
    public Object klaas;
    public int henk;
}

class Pietje {
   private final String pietje;
   private final Object klaas;
   private final int henk;

   // ja ook dit zou beter moeten kunnen. Zelfs met IDE hulp wordt je hier simpel van.
   public Pietje(String pietje, Object klaas, int henk) {
      this.pietje = pietje;
      this.klaas = klaas;
      this.henk = henk;
   }
}

public static void main(String[] args) {
    PietjeBuilder pietjeBuilder = new PietjeBuilder();
    pietjeBuilder.pietje = "HENKJE";
    pietjeBuilder.klaas = RandomObjectBuilderFactory.getFactory().newInstance(new Random().nextInt(1337));
    pietjeBuilder.henk = 1337;

    Pietje pietje = new Pietje(
       pietjeBuilder.pietje,
       pietjeBuilder.klaas,
       pietjeBuilder.henk
    );
}


Named parameters, verplicht in te vullen @ build-time, wat wil je nog meer? :+

Fake edit: Ow wacht, dit kan ook nog:

Java:
1
2
3
4
5
6
   PietjeBuilder pb = new PietjeBuilder();
   Pietje pietje = new Pietje(
      pb.pietje = "henk",
      pb.klaas = new Object(),
      pb.henk = 3771
   );


:+ :+ :+ maar het werkt wel. Je moet alleen voor het object dat je wilt initialiseren een nieuw builder object maken met dezelfde velden.

[random idee] Misschien is er een tooltje te bouwen die automagisch dit soort builders voor elk object aanmaakt oid[/random idee]


Echte edit: Wat ook nog kan: initializer block misbruiken, bouw je een subclass inline. Helaas geen compile-time-verplichte-velden-verplichting.

Java:
1
2
3
4
5
Pietje pietje = new Pietje() {{
    setPietje("henk");
    setKlaasje("piet");
    setLeetje(1337);
}};


Echte edit 2: Wat V zegt dus.

[ Voor 6% gewijzigd door YopY op 20-01-2011 22:03 ]


Acties:
  • 0 Henk 'm!

  • RSchellhorn
  • Registratie: Augustus 2001
  • Laatst online: 09:01
Killemov schreef op donderdag 20 januari 2011 @ 17:00:
[...]

Huh? Zo maak je in Java een instantie van een ter plekke gedefinieerde class. (Anonymous inner class dus.)
Inderdaad, maar je moet een initializer blok gebruiken om de variabelen/setters aan te roepen. Correcte Java syntax is dus:

Java:
1
2
3
4
5
6
MyObject obj = new MyObject() {
  {
    setA("a")
    setB("b")
  }
};


Nadeel is dat dit natuurlijk niet voor final klassen werkt.
YopY schreef op donderdag 20 januari 2011 @ 21:58:
Nee, je hebt geen named variables bij constructors...
Moderne JVM talen (lees: Scala) hebben dit wel, geen builders meer nodig! En dus significant minder code :)

Scala:
1
2
3
case class Foo(aap: String, noot: String, mies: String)

val foo = Foo( mies = "mies", noot = "noot", aap = "aap" )

[ Voor 28% gewijzigd door RSchellhorn op 20-01-2011 22:10 ]

"Ik heb zo veel soep gegeten, dat kan een mens niet aan. Ik heb zo veel soep gegeten, kan bijna niet meer staan. Ik zat daar maar te slurpen achter die grote kop en als ik bijna klaar was, dan schepten ze weer op!" (Hans Teeuwen)


Acties:
  • 0 Henk 'm!

  • Killemov
  • Registratie: Januari 2000
  • Laatst online: 24-08 23:40

Killemov

Ik zoek nog een mooi icooi =)

RSchellhorn schreef op donderdag 20 januari 2011 @ 22:00:
[...]
Inderdaad, maar je moet een initializer blok gebruiken om de variabelen/setters aan te roepen. Correcte Java syntax is dus:

Java:
1
2
3
4
5
6
MyObject obj = new MyObject() {
  {
    setA("a")
    setB("b")
  }
};
Ah, het ging me even om het verschil tussen C# en Java. Kleine brainfuck als je van de een naar de ander gaat dus.

Hey ... maar dan heb je ook wat!


Acties:
  • 0 Henk 'm!

  • YopY
  • Registratie: September 2003
  • Laatst online: 13-07 01:14
RSchellhorn schreef op donderdag 20 januari 2011 @ 22:00:
Moderne JVM talen (lees: Scala) hebben dit wel, geen builders meer nodig! En dus significant minder code :)

Scala:
1
2
3
case class Foo(aap: String, noot: String, mies: String)

val foo = Foo( mies = "mies", noot = "noot", aap = "aap" )
Todo: Scala leren :+.

Nu kun je met Spring Framework op een paar manieren een object initialiseren:

* 'Gewoon', dwz params op volgorde doorgeven
* Per positie, ongeveer hetzelfde als 'gewoon' maar dan geef je een index-parameter mee
* Per type, dus een ctor die een string en een int verwacht geef je een int en een string en Spring zoekt vanzelf uit welke waarbij hoort (hoef je de volgorde niet te weten / aan te passen)
* Per naam (sinds Spring 3.0). Copypasta-voorbeelden:

XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  <!-- gewoon -->
  <bean id="foo" class="x.y.Foo">
      <constructor-arg ref="bar"/>
      <constructor-arg ref="baz"/>
  </bean>

  <!-- positie / index -->
  <bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
  </bean>

  <!-- type -->
  <bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
  </bean>

  <!-- naam -->
  <bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateanswer" value="42"/>
  </bean>


Natuurlijk is dit allemaal runtime / startup time (alhoewel er mogelijk tools zijn die dit on-the-fly nakijken), en is het veel typ-intensiever dan het gewoon in de code te doen. Aan de andere kant, zo heb je wel veel controle over je applicatie zonder dat je de daadwerkelijke code in hoeft te duiken. Maar het nut en de onzin van Spring's DI en objectinitialisatie kun je een topic op zich aan wijden.

Acties:
  • 0 Henk 'm!

  • RayNbow
  • Registratie: Maart 2003
  • Laatst online: 21:41

RayNbow

Kirika <3

.oisyn schreef op woensdag 19 januari 2011 @ 21:38:
[...]

Ja, en als die functie is opgesplitst in 5 niveaus diep, dan spring je en spring je en wordt de code zo onoverzichtelijk als de pest.
Als het noodzakelijk is dat je de diepte in moet springen, dan is de code gewoon brak. Een functie behoort (grotendeels) begrijpbaar te zijn zonder de intieme details van elke aangeroepen functie te kennen. Het is soms geen eens mogelijk om de intieme details van een aangeroepen functie te kennen, omdat je niet over de broncode beschikt.

Het voorbeeld in het Extract till you Drop artikel vind ik in ieder geval onduidelijk. Op basis van de naamgeving van de functies alleen kun je niet de werking van de class uitvogelen. Je wordt hier genoodzaakt om door de code heen te springen.


Maar goed, als we het toch over springen-naar-definities hebben, hoe zou je de definitie van primes in m'n eerder geposte Haskell voorbeeld proberen te begrijpen?
Primes "call" graph
^ Toch niet door telkens naar elke definitie te springen, hoop ik?

Ipsa Scientia Potestas Est
NNID: ShinNoNoir


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-09 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

RayNbow schreef op vrijdag 21 januari 2011 @ 10:34:
Het voorbeeld in het Extract till you Drop artikel vind ik in ieder geval onduidelijk.
Exactly my point. Dus we zijn het gewoon eens ;)
Maar goed, als we het toch over springen-naar-definities hebben, hoe zou je de definitie van primes in m'n eerder geposte Haskell voorbeeld proberen te begrijpen?
Die is vrij simpel te begrijpen. Of ie ook correct is is een ander verhaal (als in, ik zie dat je definities kloppen, maar of de recursie ook goed gaat heb ik wat meer tijd voor nodig).

[ Voor 10% gewijzigd door .oisyn op 21-01-2011 11:20 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • alienfruit
  • Registratie: Maart 2003
  • Laatst online: 18:28

alienfruit

the alien you never expected

Ik doe het altijd op deze manier (builders):
https://gist.github.com/779495

Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 18:51
@RayNbow: de vraag is echter of jou code duidelijker wordt van het afsplitsen van méér functies. Ik vind de aparte declaratie van oddprimes in jouw code zeker overbodig. Dit is korter en leest makkelijker:
Haskell:
1
primes = 2 : filter isPrime [3,5..]

Op dezelfde manier zou ik isPrime gewoon zo schrijven:
Haskell:
1
isPrime n = primeFactors n == [n]

Maar dit heeft niet echt met het afsplitsen van functies te maken.

Voor de rest zijn alle functies die je gebruikt gewoon "nodig" in de zin dat je ze niet redelijkerwijs kunt inlinen zonder het gehele programma aanzienlijk complexer te maken, ook al zijn de functies zelf relatief kort (wat niet ongebruikelijk is in functionele programmeertalen).

Dus kom ik weer uit op de cyclomatic complexity van eerder. Het is geen perfecte metric, maar het lijkt me geen slechte leidraad om de complexiteit zo laag mogelijk te houden, en van programma's met vergelijkbare complexiteit de kortste de voorkeur te geven. (In het voorbeeld hierboven zou ik bijvoorbeeld zeggen dat "primeFactors n == [n]" simpeler is dan de variant met een case-statement, en dus de voorkeur geniet, zeker aangezien in dit geval er geen verschil in efficiëntie is.)

[ Voor 10% gewijzigd door Soultaker op 21-01-2011 13:38 ]


Acties:
  • 0 Henk 'm!

  • RayNbow
  • Registratie: Maart 2003
  • Laatst online: 21:41

RayNbow

Kirika <3

.oisyn schreef op vrijdag 21 januari 2011 @ 11:20:
[...]

Exactly my point. Dus we zijn het gewoon eens ;)
Ah, okay. :)
[...]

Die is vrij simpel te begrijpen. Of ie ook correct is is een ander verhaal (als in, ik zie dat je definities kloppen, maar of de recursie ook goed gaat heb ik wat meer tijd voor nodig).
Om welke recursie gaat het? Die wederzijdse recursie tussen primes en primeFactors (waarbij het van belang is dat primeFactors niet meer elementen van primes consumeert dan dat er al geproduceerd zijn) of de recursie van de hulpfunctie factor?
Soultaker schreef op vrijdag 21 januari 2011 @ 13:36:
@RayNbow: de vraag is echter of jou code duidelijker wordt van het afsplitsen van méér functies. Ik vind de aparte declaratie van oddprimes in jouw code zeker overbodig. Dit is korter en leest makkelijker:
Haskell:
1
primes = 2 : filter isPrime [3,5..]
In dit specifieke voorbeeld zou ik zeggen dat er voor beide definities wat te zeggen valt, maar ik vind de een niet moeilijker of makkelijker te lezen dan de ander.

Er bestaan trouwens wel alternatieve manieren om een lijst priemgetallen te genereren waarbij het wel handig is om een naam te geven aan de oneven priemgetallen.
Op dezelfde manier zou ik isPrime gewoon zo schrijven:
Haskell:
1
isPrime n = primeFactors n == [n]

Maar dit heeft niet echt met het afsplitsen van functies te maken.
Die definitie is inderdaad korter en makkelijker te begrijpen.

(Het kost wel een extra vergelijking tegenover een simpele pattern match, maar da's verwaarloosbaar :p)

Ipsa Scientia Potestas Est
NNID: ShinNoNoir


Acties:
  • 0 Henk 'm!

  • YopY
  • Registratie: September 2003
  • Laatst online: 13-07 01:14
Builders zijn leuk, maar je hebt geen compile-time checken of je wel alle waarden toekent - dat krijg je pas @ runtime, waardoor bugs later opgemerkt worden.

Tijd voor een taalfeature waarbij je @compile-time verplicht functies aan kunt laten roepen.
Pagina: 1 2 Laatste