Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[alg / java] Subclasses versus Wrappers

Pagina: 1
Acties:
  • 161 views sinds 30-01-2008
  • Reageer

  • YopY
  • Registratie: September 2003
  • Laatst online: 06-11 13:47
Een kwestie waar ik over het internet nogal tegenstrijdige berichten van krijg is het gebruik van subclasses en/of 'wrapper' klassen. In dit thread wil ik hier jullie meningen over hebben welke nu 'beter' of 'netjeser' is.

Als voorbeeld neem ik Java's List interface, een interface voor verschillende 'lijsten' met gegevens / objecten. Ik kwam erop toen ik een List wilde gebruiken in een JSP / JSTL pagina:

Java:
1
<c:out value="${list.size}"/>


welke, in theorie, de 'size()' methode van List aan moet vragen. Het probleem is echter dat JSTL (of wat dan ook, weet ik niet eens zeker) er van uit gaat dat alle objecten waar je gegevens uit haalt voldoet aan de JavaBeans specificatie. Het specifieke onderdeel hier van toepassing is het gebruik en de naam conventie van Accessors, namelijk dat ze altijd voorafgegaan zijn door 'get'.

In het bovenstaande geval probeert de JSP parser de methode 'getSize()' van List aan te roepen. Echter, List heeft geen getSize() methode, alleen een 'size()' methode.

Nu, meer bij het punt van dit topic. Je wilt nu eigenlijk een getSize() methode toevoegen aan de List interface. Gezien List een onderdeel is van de standaard Java library, wil je hier eigenlijk niet aan beginnen, gezien je dan eventuele aanpassingen in de Java standaard library ook telkens aan moet passen.

Dus, je wilt een klasse die de functionaliteit van List uitbreidt met een getSize() methode. Dit kan nu op in ieder geval twee manieren: een subklasse of een wrapper klasse.

Een subklasse is een uitbreiding op de bestaande klasse, zodat de nieuwe klasse ook automatisch de methoden van het origineel heeft. In dit geval is het een interface, gezien List ook een interface is.

Java:
1
2
3
4
5
6
interface CustomList extends List {
    /**
     * roept de superklasse z'n size() methode aan.
     */
    public int getSize();
}


De andere methode is een 'Wrapper' klasse, waar je een klasse maakt waar de List een veld in is:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ListWrapper {
  private List list;

  public ListWrapper(List list) {
    this.list = list;
  }

  public List getList() {
    return this.list;
  }

  public int getSize() {
    return getList().size();
  }
}


*puf*. Juist, nu weet ik sowieso dat je met de subclass methode het zogenaamde 'Fragile Base Class' probleem kunt krijgen, misschien niet in dit voorbeeld, maar anderen wel. Je bent afhankelijk van de implementatie van List, en wanneer deze verandert, moet je code ook veranderen (tenminste, indien de methode namen veranderen of indien je de accessors niet gebruikt).

Aan de andere kant heb je de wrapper class, waarbij je telkens de getList() methode moet aanroepen om bij je daadwerkelijke List te komen. Je kunt je ListWrapper natuurlijk List laten implementeren, maar dan krijg je een lange lijst met methoden die niets anders doen dan de aanroep delegeren naar de lijst die opgeslagen is in de wrapper.


Mja, om maar een discussie op gang te laten komen in dit forum: Wat denk jij, subclass of wrapper class, of heb je nog een ander idee?

  • whoami
  • Registratie: December 2000
  • Laatst online: 29-11 22:54
Alles hangt af van de situatie.

Is de class die je gaat maken een subclass-type, of ga je gewoon dat andere type gaan gebruiken ?

Maw, heeft de class die je gaat schrijven een IS-A relatie met het andere, reeds bestaande type, of niet ?

https://fgheysels.github.io/


  • YopY
  • Registratie: September 2003
  • Laatst online: 06-11 13:47
Soms wel, soms niet :). Het is bij bepaalde gevallen (in mijn geval dus) onduidelijk. Zo heb ik bijvoorbeeld een probleem waarbij multiple inheritance handig zou zijn. Ik heb bijvoorbeeld een Class... class, welke een Java class voorstelt met informatie als class name, de Package waar hij bij hoort, enzovoorts.

Nu is een class 3 dingen (tot nu toe): Het is een PackageExplorerElement, wat inhoudt dat hij in een element in mijn webapp kan (soort van package explorer / accordion) , het is Searchable (wat inhoudt dat hij een getIdentifier() methode heeft), en het is een ReportContainer, wat inhoudt dat hij een lijst met Report objecten heeft (welke rapporten voorstellen die van toepassing zijn op die Class).

Nu heb je bijvoorbeeld een Package en een Class, beide zijn ReportContainers. Het is nog wel zo dat beide op zich gewoon een ReportContainerImpl class kunnen extenden, om zodanig een lijst met Reports en toegangs / zoekmethoden voor die Reports te kunnen krijgen. Maar stel nu dat er velden / methoden in die beide klassen (en nog wat anderen) zijn die nog verder abstract gemaakt kunnen worden, en als zodanig in een andere klasse kunnen.

Je krijgt dan een klasse die tegelijk type A is (bv ReportContainer) en type B (bv euh, File, een Class is tegelijk een ReportContainer en een File op het moment). In zo'n geval heb je een klasse die een File en tegelijk een ReportContainer is. Je wilt de functionaliteit van beide klassen hebben, maar, je mag maar één klasse echt extenden (in C++ mag dat wel).

In zo'n geval moet je eigenlijk wel een wrapper gebruiken: De klasse heeft twee velden (File en ReportContainer), en implementeert alle methoden van File en ReportContainer, om ze te delegeren aan de beide velden.

Maar in ieder geval. Is het beter om, wanneer je maar één is-a relatie hebt, gewoon een subclass te maken, of kun je beter al gelijk ervan uit gaan dat in de toekomst er meerdere 'superklassen' voor een class komen en gelijk al het wrapper idee te implementeren?

  • Orphix
  • Registratie: Februari 2000
  • Niet online
In dit geval kan je in mijn ogen gerust een subclass maken aangezien de toevoeging zeer beperkt is (eigenlijk enkel een helper method), en volledige de publieke interface gebruikt van IList. De kans dat deze semantisch of functioneel wijzigt ach ik zeer, zeer klein.

Een probleem bij inheritance is dat je subclass zeer sterk gekoppeld is aan je baseclass. Een andere implementatie van IList gebruiken is niet mogelijk. Of je moet hier ook weer een subclass voor maken. Wil je meer flexibel zijn, en meerdere implementaties van IList ondersteunen, dan zal je hier inderdaad een wrapper voor moeten schrijven. Dit hoeft trouwens niet alleen met een getList() methode, je kan ook de list zelf private houden en IList zelf implementeren en de calls doorgeven aan de private list.

Interessant in dit opzicht is de toevoeging van 'extension methods' in C# 3.0. Dit kan op een eenvoudige manier bewerkstelligen wat jij wilt. Ik weet zo snel niet of je ook extension methods kan toepassen op een interface, maar dit zou eigenlijk wel mooi zijn. Ik ben er zelf nog niet helemaal over uit of extension methods ons leven als programmeur makkelijker maakt (eenvoudiger te schrijven) of moeilijker (minder overzichtelijk), maar dat zal de tijd uitwezen.

Een tijd geleden was er een thread waarbij afleiding van een collectie class géén goed idee was: C# Collectie capaciteit. Het hangt dus geheel van de situatie af of afleiden of compositie de beste oplossing is.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:22

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ik zou voor een losse wrapper class gaan, vooral omdat je het dan kunt laten werken op bestaande instanties van List. Een subclass vereist dat elke bestaande List in je app nu een CustomList moet zijn, en bij Lists uit andere libraries moet je je eigen klasse instantieren en de boel gaan zitten kopiëren. Daarnaast zullen er, zoals Orphix al zegt, ook meerdere afgeleiden van List zijn waardoor je een subclass moet maken van al die afgeleiden.

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.


  • ronaldmathies
  • Registratie: Juni 2001
  • Niet online
Het toepassen van de ${fn:length(list)} is vast niet hetgeen wat je wil weten?

3015 Wp-z 5360 Wp-nno op 2 x SMA-SB3600 TL-21, Warmtepomp: ERSC-VM2CR2 / PUHZ-SHW140 YHA, WTW Q350, EV Kia Ev6 GT-Line


  • YopY
  • Registratie: September 2003
  • Laatst online: 06-11 13:47
ronaldmathies schreef op dinsdag 06 november 2007 @ 16:57:
Het toepassen van de ${fn:length(list)} is vast niet hetgeen wat je wil weten?
Nu je het zegt, en indien dat is wat ik denk dat het is; jawel :). Is dat een truukje om de 'length' functie op de list uit te voeren?

En werkt dat ook indien je een functie aan wilt roepen met een argument, zoals bij een Map?

  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

YopY schreef op dinsdag 06 november 2007 @ 20:08:
[...]


Nu je het zegt, en indien dat is wat ik denk dat het is; jawel :). Is dat een truukje om de 'length' functie op de list uit te voeren?

En werkt dat ook indien je een functie aan wilt roepen met een argument, zoals bij een Map?
Het is geen trucje. fn is de prefix die door JSTL gebruikt wordt. En length is één van de standaard JSTL functies.

http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JSTL8.html

Fat Pizza's pizza, they are big and they are cheezy


  • citegrene
  • Registratie: Februari 2006
  • Laatst online: 02-11 03:00
class CustomList implements List {
/**
* roept de superklasse z'n size() methode aan.
*/
public int getSize();
}


Suggestie? Net zoals de java devs dat doen met de classes als ArrayList and LinkedList etc. Kwestie van spieken ;)

  • YopY
  • Registratie: September 2003
  • Laatst online: 06-11 13:47
Zo heb ik het ook gedaan ondertussen, ;).
Pagina: 1