Hallo.
Ik ben wat aan het piekeren om op een mooie en handige manier (tijdelijke) informatie van algoritmes bij te houden, zonder dat je de gebruiker van het algoritme verplichtingen oplegt over de definitie van zijn classes. Laat ik het uitleggen ahv. een theoretisch voorbeeldje.
Stel je hebt volgende interface & implementatie:
Stel nu dat je een (maakt niet uit wat het doet) algoritme hebt dat werkt op een List<SomeInterface>, en stel dat het algoritme moet bijhouden welke elementen uit de lijst hij reeds behandeld heeft (en dus ook welke niet).
Nu kan je in principe elk element uit de lijst in een Map<SomeInterface, Boolean> steken. Deze map houdt dus per element bij welk element het algoritme reeds behaldend heeft (true) en welke niet (false). Deze aanpak heeft volgens mij minstens de volgende problemen:
- In een List kunnen dubbels (volgens .equals()) voorkomen, maar in een Map niet.
- Ook al heb je geen dubbels, de tijdcomplexiteit van het algoritme kan stijgen, aangezien een lookup in de Map niet noodzakelijk in O(1)-tijd hoeft te verlopen. (bijvoorbeeld als de gebruiker een slechte hashcode-functie geïmplementeerd heeft)
- SomeInterface is een interface, en List<SomeInterface> is dus een lijst van willekeurige implementaties van SomeInterface (in het voorbeeld hierboven heb ik er eentje gegeven: SomeClass). Het is (volgens mij) niet gebruikelijk dat verschillende implementaties van een interface vergeleken kunnen worden met elkaar. Hiermee bedoel ik: de meeste programmeurs implementeren de equals(Object o) van een class Foo implements EenInterface als volgt:
Samengevat: een Map is no-go!
Toen kwam ik op het idee om het Decorator pattern te gebruiken, op de volgende manier: ik maak een speciale implementatie van SomeInterface (laat ik hem SomeInterfaceDecorated noemen), waarmee ik kan aangeven of een element (SomeInterface-element) al dan niet behandeld is. De functies uit SomeInterface stuur ik door naar het SomeInterface-element dat gedecoreerd wordt. Deze class declareer ik private in mijn algoritme klasse:
In plaats van dat mijn algoritme runt op een List<SomeInterface>, laat ik hem runnen op een List<SomeInterfaceDecorated>.
In principe ben ik nu klaar: ik kan aangeven of elementen uit de lijst reeds behandeld zijn, zonder dat ik de gebruiker verplicht om zijn classes/interfaces aan te passen.
Mijn vraag is nu: is dit een goede manier? Kan het beter? Volgens mij is dit een mooie toepassing van het Decorater pattern, maar ik weet niet goed of het op zijn plaats is. Ik vind het o.a. 'jammer' dat ik elk element uit de input-lijst moet kopiëren in een nieuwe lijst (uiteraard kopieer ik de reference, maar dat telt ook), wat misschien problemen kan geven indien de input-lijst erg groot is. Sterker nog, omdat ik elk element moet kopiëren, verloopt het algoritme vanaf nu in minstens O(n) tijd, terwijl het theoretisch mogelijk kan zijn om het algoritme te runnen in O(log n) tijd (ik heb met opzet vermeld dat het een willekeurig algoritme is). Al bij al geen goede oplossing dus. Bestaan hier betere standaardoplossingen voor?
bedankt, en excuses voor de lange post, ik wou zo duidelijk mogelijk zijn
edit: Ik ben dus eigenlijk op zoek naar een oplossing waarmee ik de elementen NIET hoef te kopiëren, en daardoor de tijdcomplexiteit van het algoritme dus niet minstens O(n) bedraagt.
Ik ben wat aan het piekeren om op een mooie en handige manier (tijdelijke) informatie van algoritmes bij te houden, zonder dat je de gebruiker van het algoritme verplichtingen oplegt over de definitie van zijn classes. Laat ik het uitleggen ahv. een theoretisch voorbeeldje.
Stel je hebt volgende interface & implementatie:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
| interface SomeInterface { public int getSomething(); } class MyClass implements SomeInterface { private int value; MyClass(int value) { this.value = value; } public int getSomething() { return value; } } |
Stel nu dat je een (maakt niet uit wat het doet) algoritme hebt dat werkt op een List<SomeInterface>, en stel dat het algoritme moet bijhouden welke elementen uit de lijst hij reeds behandeld heeft (en dus ook welke niet).
Nu kan je in principe elk element uit de lijst in een Map<SomeInterface, Boolean> steken. Deze map houdt dus per element bij welk element het algoritme reeds behaldend heeft (true) en welke niet (false). Deze aanpak heeft volgens mij minstens de volgende problemen:
- In een List kunnen dubbels (volgens .equals()) voorkomen, maar in een Map niet.
- Ook al heb je geen dubbels, de tijdcomplexiteit van het algoritme kan stijgen, aangezien een lookup in de Map niet noodzakelijk in O(1)-tijd hoeft te verlopen. (bijvoorbeeld als de gebruiker een slechte hashcode-functie geïmplementeerd heeft)
- SomeInterface is een interface, en List<SomeInterface> is dus een lijst van willekeurige implementaties van SomeInterface (in het voorbeeld hierboven heb ik er eentje gegeven: SomeClass). Het is (volgens mij) niet gebruikelijk dat verschillende implementaties van een interface vergeleken kunnen worden met elkaar. Hiermee bedoel ik: de meeste programmeurs implementeren de equals(Object o) van een class Foo implements EenInterface als volgt:
Java:
Een object van het type Foo kan je dus niet vergelijken met bijvoorbeeld een object van het type Foo2, met Foo2 gedefinieerd als class Foo2 implements EenInterface. Een lookup in de Map zou dus ook foutlopen.1
2
3
4
5
6
7
8
9
10
| public boolean equals(Object o) { if (!(o instanceof Foo)) { return false; } MyClass foo = (Foo) o; ... } |
Samengevat: een Map is no-go!
Toen kwam ik op het idee om het Decorator pattern te gebruiken, op de volgende manier: ik maak een speciale implementatie van SomeInterface (laat ik hem SomeInterfaceDecorated noemen), waarmee ik kan aangeven of een element (SomeInterface-element) al dan niet behandeld is. De functies uit SomeInterface stuur ik door naar het SomeInterface-element dat gedecoreerd wordt. Deze class declareer ik private in mijn algoritme klasse:
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
| class Algorithm { private class SomeInterfaceDecorated implements SomeInterface { private boolean done = false; private SomeInterface myClass; public SomeInterfaceDecorated(SomeInterface myClass) { this.myClass = myClass; } public boolean isDone() { return done; } public void setDone() { done = true; } public int getSomething() { return myClass.getSomething(); } public boolean equals(Object o) { return myClass.equals(o); } public int hashCode() { return myClass.hashCode(); } public String toString() { return myClass.toString(); } } public Algorithm(List<SomeInterface> input) { List<SomeInterfaceDecorated> inputDecorated = new ArrayList<SomeInterfaceDecorated>(); for (SomeInterface someClass : input) inputDecorated.add(new SomeInterfaceDecorated(someClass)); // run algoritme op inputDecorated, ipv. input } } |
In plaats van dat mijn algoritme runt op een List<SomeInterface>, laat ik hem runnen op een List<SomeInterfaceDecorated>.
In principe ben ik nu klaar: ik kan aangeven of elementen uit de lijst reeds behandeld zijn, zonder dat ik de gebruiker verplicht om zijn classes/interfaces aan te passen.
Mijn vraag is nu: is dit een goede manier? Kan het beter? Volgens mij is dit een mooie toepassing van het Decorater pattern, maar ik weet niet goed of het op zijn plaats is. Ik vind het o.a. 'jammer' dat ik elk element uit de input-lijst moet kopiëren in een nieuwe lijst (uiteraard kopieer ik de reference, maar dat telt ook), wat misschien problemen kan geven indien de input-lijst erg groot is. Sterker nog, omdat ik elk element moet kopiëren, verloopt het algoritme vanaf nu in minstens O(n) tijd, terwijl het theoretisch mogelijk kan zijn om het algoritme te runnen in O(log n) tijd (ik heb met opzet vermeld dat het een willekeurig algoritme is). Al bij al geen goede oplossing dus. Bestaan hier betere standaardoplossingen voor?
bedankt, en excuses voor de lange post, ik wou zo duidelijk mogelijk zijn
edit: Ik ben dus eigenlijk op zoek naar een oplossing waarmee ik de elementen NIET hoef te kopiëren, en daardoor de tijdcomplexiteit van het algoritme dus niet minstens O(n) bedraagt.
[ Voor 3% gewijzigd door Verwijderd op 06-02-2005 20:08 ]