[java] objecten van een type uit registry halen

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
Ik ben een Registry aan het schrijven. Welke van deze twee opties vinden jullie het beste:

Java:
1
2
3
4
5
6
7
8
9
public class Registry {

public <T> T get(String name) {
 return (T) someObject; // warning
}

}

registry.<Car>get("mycar").drive();


Java:
1
2
3
4
5
6
7
8
9
public class Registry {

public Object get(String name) {
 return someObject;
}

}

((Car) registry.get("mycar")).drive();


Ik neig zelf naar de eerste, omdat ik de code voor het opvragen van een object daar beter te lezen vind. In het tweede voorbeeld krijg je veel haken. Helaas krijg ik bij de eerste wel een warning (type safety) op de aangegeven regel, die bij mijn weten niet te vermijden is. Ook weet ik niet of er performanceverschil tussen de twee methoden zit.

Acties:
  • 0 Henk 'm!

  • thies
  • Registratie: December 1999
  • Laatst online: 22-09 20:38
wat is er mis met een Map ?

Acties:
  • 0 Henk 'm!

  • Robtimus
  • Registratie: November 2002
  • Laatst online: 19:03

Robtimus

me Robtimus no like you

Die warning is er niet voor niets. Je cast nu naar T zonder te weten, of zelfs maar te kunnen controleren, of het wel een instance van T is. Dat is vragen om problemen.

Qua performance is er overigens geen verschil; beide vereisen een cast die al door de compiler wordt afgehandeld, en allebei zullen een ClassCastException geven als het object geen Car is.

More than meets the eye
There is no I in TEAM... but there is ME
system specs


Acties:
  • 0 Henk 'm!

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
Die warning is er niet voor niets. Je cast nu naar T zonder te weten, of zelfs maar te kunnen controleren, of het wel een instance van T is. Dat is vragen om problemen.
Dat klopt, maar dat is in het geval van de cast in het tweede voorbeeld ook. Tja, ik kan moeilijk niet casten, dan kan ik niks met zo'n object. :/
wat is er mis met een Map ?
Mijn Registry gebruikt een Map, maar heeft nog wat uitgebreide functies. Zo kun je hem een falback-Registry meegeven. Daar gaat hij dan kijken als hij in zijn eigen gegevens iets niet vindt.

Acties:
  • 0 Henk 'm!

  • thies
  • Registratie: December 1999
  • Laatst online: 22-09 20:38
2playgames schreef op woensdag 28 januari 2009 @ 13:46:
[...]

Mijn Registry gebruikt een Map, maar heeft nog wat uitgebreide functies. Zo kun je hem een falback-Registry meegeven. Daar gaat hij dan kijken als hij in zijn eigen gegevens iets niet vindt.
extend dan een standaard implementatie van een Map, HashMap of zo, en voeg daar je eigen functionaliteit aan toe.

Acties:
  • 0 Henk 'm!

Verwijderd

thies schreef op woensdag 28 januari 2009 @ 13:53:
[...]


extend dan een standaard implementatie van een Map, HashMap of zo, en voeg daar je eigen functionaliteit aan toe.
Wat is dan het voordeel tov een eigen klasse maken (Registry) welke intern een (hash)map gebruikt?

Acties:
  • 0 Henk 'm!

  • thies
  • Registratie: December 1999
  • Laatst online: 22-09 20:38
Verwijderd schreef op woensdag 28 januari 2009 @ 14:17:
[...]

Wat is dan het voordeel tov een eigen klasse maken (Registry) welke intern een (hash)map gebruikt?
Dat je daar nog eigen functionaliteit aan kan toevoegen, bovenop wat die Map doet. Als je alleen maar extra functionaliteit wilt toevoegen is compositie iets handiger ipv inheritance, wil je op een transparante manier een fallback map gebruiken dan extend je 'm en override je de get.

Acties:
  • 0 Henk 'm!

  • JeroenTheStig
  • Registratie: Mei 2000
  • Laatst online: 23:08
Ik zou heel erg oppassen met extending zoals de poster hierboven aangeeft. Maak bij voorkeur gebruik van composition waaraan je je extra logica toevoegt:

Java:
1
2
3
4
5
6
7
8
9
10
11
public class Registry implements Map {
  private Map map;
  public Registry(Map map) {
     this.map = map;
  }

  public V put(K key, V value) {
    return this.map.put(key,value);
  }

  // enz voor alle methods in interface Map


Bij extension kunnen namelijk gekke bugs optreden als de class niet is ontworpen voor extension, zelfs na het uitbrengen van een nieuwe versie van de extended class. Zie het volgende voorbeeld (bron: effective java, Addison Wesley):

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class InstrumentedHashSet<E> extends HashSet<E> {
  // The number of attempted element insertions
  private int addCount = 0;
  public InstrumentedHashSet() {
  }
  public InstrumentedHashSet(int initCap, float loadFactor) {
    super(initCap, loadFactor);
  }
  @Override public boolean add(E e) {
    addCount++;
    return super.add(e);
  }
  @Override public boolean addAll(Collection<? extends E> c) {
    addCount += c.size();
    return super.addAll(c);
  }
  public int getAddCount() {
    return addCount;
  }
}


Stel we roepen dit aan met de volgende code:
Java:
1
2
3
InstrumentedHashSet<String> s =
new InstrumentedHashSet<String>();
s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));


Je zal verwachten dat de methode getAddAccount 3 zal teruggeven, echter zal dit 6 zijn. Dit is te verklaren door de implementatie van addAll van de superclass (in dit geval HashSet), die namelijk voor elk element in de meegegeven collection de eigen add-methode aanroept.

Pas daarom enorm goed op wanneer je wel en wanneer je niet gebruik maakt van extension. In dit geval zal ik voor composition kiezen.

Acties:
  • 0 Henk 'm!

  • Macros
  • Registratie: Februari 2000
  • Laatst online: 15-05 16:29

Macros

I'm watching...

Als je de Map interface wilt implementeren kan je wel AbstractMap implementeren, dan hoef je nog maar 3 methodes te implementeren en je hele Map is geimplementeerd.

"Beauty is the ultimate defence against complexity." David Gelernter


Acties:
  • 0 Henk 'm!

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
Bovendien zou ik mijn eerste voorbeeld niet kunnen implementeren als Registry een Map zou zijn. De get()-methode voldoet niet aan de beschrijving in die interface.

Acties:
  • 0 Henk 'm!

  • Robtimus
  • Registratie: November 2002
  • Laatst online: 19:03

Robtimus

me Robtimus no like you

2playgames schreef op woensdag 28 januari 2009 @ 13:46:
[...]


Dat klopt, maar dat is in het geval van de cast in het tweede voorbeeld ook. Tja, ik kan moeilijk niet casten, dan kan ik niks met zo'n object. :/
Maar in het tweede voorbeeld kun je controleren of het ook werkelijk een Car is, dmv instanceof. Dat lukt je niet met T.

More than meets the eye
There is no I in TEAM... but there is ME
system specs


Acties:
  • 0 Henk 'm!

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
Ik ga deze code alleen gebruiken op plekken waar ik zeker weet welk type ik uit het registry haal. Als er ergens een cast verkeerd gaat zal dat dus een bug zijn. Het zal normaal niet voorkomen.

Als ik wel instanceof zou kunnen gebruiken, zou ik in het geval van een fout type alsnog een Exception gooien. Zelfde resultaat dus.

[ Voor 26% gewijzigd door 2playgames op 28-01-2009 23:22 ]


Acties:
  • 0 Henk 'm!

  • Sihaya
  • Registratie: Juni 2001
  • Niet online

Sihaya

Pasfoto:

Het leek mij dat een oplossing met een static parameterized class een mogelijke oplossing zou zijn, bijvoorbeeld:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Map;

final public class RegistryInstance<T>
{
    private static RegistryInstance<T> instance = new RegistryInstance<T>();
    private static Map<String, T> map;

    public static T get(String auto)
    {
        return map.get(auto);
    }
}


Helaas genereert dit een foutmelding over het gebruik van non-static template parameter T in een static context. In C++ mag het overigens wel:

C++:
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
32
33
#include <map>
#include <string>

using namespace std;

template <class T> class Registry
{
  public:
    static Registry<T> instance;

    static T get(string object)
    {
      return mapping[object];
    }


  private:
    static map<string, T> mapping;
};

template <class T>
map<string, T> Registry<T>::mapping;

class Car
{
  public:
    void drive() { }
};

int main(void)
{
  Registry<Car>::get("auto").drive();
}


Dit alles losstaand van de vraag waarom je deze constructie wilt gebruiken. Het doet mij denken aan een soort "service locator" maar dan niet type safe. Lijkt mij dat zo'n constructie vroeg of laat misbruikt gaat worden in het ontwikkelproces.

signature has expired


Acties:
  • 0 Henk 'm!

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
Ik wil het gebruiken in een game-engine (http://victoryengine.org). Het idee is dat een spel aan het begin verschillende objecten maakt/aanroept, bijvoorbeeld:
- Een display, voor het renderen van plaatjes
- Een inputmanager, voor het uitlezen van toetsenbord- en muisinput
- Een entitylist, die alle spelobjecten bijhoudt
- Een resourcemanager, voor het laden en opvragen van plaatjes, geluiden, etc.

Deze worden dan allemaal in het Registry gestopt, en deze wordt doorgegeven waar nodig. Zo hoef ik de bovenstaande klassen niet allemaal Singleton of iets dergelijks te maken. Dat heeft twee voordelen:
- Het geheel wordt flexibeler en beter te testen. Als je een andere inputmanager zou gebruiken hoef je dit niet overal te veranderen, alleen daar waar hij gemaakt wordt.
- Ieder object kan in het register gestopt worden, ook als ze van buiten de engine komen en dus niet volledig onder controle zijn.

Als iemand een betere oplossing voor deze situatie heeft, ik hoor het graag.

Acties:
  • 0 Henk 'm!

  • Kettrick
  • Registratie: Augustus 2000
  • Laatst online: 23:58

Kettrick

Rantmeister!

Ik ken die specifieke engine niet, maar wat je aan het doen bent lijkt mij prima op te lossen met bijvoorbeeld spring ?

Acties:
  • 0 Henk 'm!

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
Dat lijkt me een beetje overkill. Bovendien ga je in een spel waarschijnlijk honderden objecten per seconden maken. Om die allemaal door een IoC-container met reflection en XML-bestandjes hun dependencies te geven lijkt me niet al te best voor de performance.

Hoe zou ik Spring hier dan goed toe kunnen passen?

Acties:
  • 0 Henk 'm!

  • Kettrick
  • Registratie: Augustus 2000
  • Laatst online: 23:58

Kettrick

Rantmeister!

2playgames schreef op zaterdag 31 januari 2009 @ 14:29:
Dat lijkt me een beetje overkill. Bovendien ga je in een spel waarschijnlijk honderden objecten per seconden maken. Om die allemaal door een IoC-container met reflection en XML-bestandjes hun dependencies te geven lijkt me niet al te best voor de performance.
Je wil inderdaad niet alles door spring halen, en dat hoeft gelukkig ook niet :). als je bepaalde onderdelen die je normaal in je registry gooit ( die vier die je eerder noemde ) spring managed maakt heeft de rest daar geen last van. Spring is dan je registry, niets meer dan dat.
Hoe zou ik Spring hier dan goed toe kunnen passen?
Geen idee, ik ken die engine verder niet ;) , maar als dit een java leer/aankloot project is zou ik gewoon eens naar spring kijken, sowieso handig om die kennis te hebben :)

Acties:
  • 0 Henk 'm!

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
Spring is dan je registry, niets meer dan dat.
Wat is dan het voordeel van Spring als registry tegenover het zelf maken?
maar als dit een java leer/aankloot project is zou ik gewoon eens naar spring kijken, sowieso handig om die kennis te hebben
Ik heb al eens met Spring gewerkt, maar dan voor een netwerkservice. Dit is toch net iets anders.

Acties:
  • 0 Henk 'm!

  • Kettrick
  • Registratie: Augustus 2000
  • Laatst online: 23:58

Kettrick

Rantmeister!

2playgames schreef op zaterdag 31 januari 2009 @ 14:37:
[...]

Wat is dan het voordeel van Spring als registry tegenover het zelf maken?
Vooral dat je het zelf niet hoeft te maken ;), waarom gebruik je een game engine als je ook alles zelf kan bouwen ? :)
[...]
Ik heb al eens met Spring gewerkt, maar dan voor een netwerkservice. Dit is toch net iets anders.
Spring is redelijk veelzijdig, maar als je gewoon wat IoC wil doen is daar genoeg over te vinden.

Acties:
  • 0 Henk 'm!

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
waarom gebruik je een game engine als je ook alles zelf kan bouwen ?
Ik gebruik geen game engine (of ja, uiteindelijk wel), ik bouw hem :p
Maar goed, het "waarom zelf bouwen"-argument gaat natuurlijk altijd op. Ik denk echter dat het in iets simpels als dit minder waarde heeft.

Acties:
  • 0 Henk 'm!

  • SPee
  • Registratie: Oktober 2001
  • Laatst online: 23-09 15:51
Waar jouw voobeeld fout op gaat, is dat je met een String een Object terug wilt.
Je kunt nooit weten wat het object is dat je erin hebt gestopt.

Wat je zou kunnen doen is een registry maken dat per type een eigen map heeft.
En dan voor die verschillende types een eigen getXXX() maken. Niet erg netjes.

Of je geeft mee welke class je verwacht.
Java:
1
2
3
4
5
6
7
public <T> T get(String name, Class<T> type)
{
  if( map.get(name).getClass().equals(type) )
    return (T)map.get(name);
  else
    return null; // or throw Exception
}

let the past be the past.


Acties:
  • 0 Henk 'm!

  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

SPee schreef op zaterdag 31 januari 2009 @ 14:58:
Of je geeft mee welke class je verwacht.
Java:
1
2
3
4
5
6
7
public <T> T get(String name, Class<T> type)
{
  if( map.get(name).getClass().equals(type) )
    return (T)map.get(name);
  else
    return null; // or throw Exception
}
Als je willekeurige objecten, waarvan je het type ten tijde van get() en put() wel weet, in dezelfde map op wilt kunnen slaan, dan kan je niet anders dan zo'n soort oplossing kiezen. Door in de put() zo'n soort constructie te gebruiken, hoef je deze test in de get() niet meer te doen. Dat zal meestal performanter zijn, omdat je veel meer get() dan put() doet. Als je slechts 1 object van ieder type hebt, kan je ook Class<?> als key in je onderliggende HashMap gebruiken.

[ Voor 7% gewijzigd door Confusion op 31-01-2009 20:41 ]

Wie trösten wir uns, die Mörder aller Mörder?


Acties:
  • 0 Henk 'm!

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
Wat je zou kunnen doen is een registry maken dat per type een eigen map heeft.
En dan voor die verschillende types een eigen getXXX() maken. Niet erg netjes.
Van de meeste typen (allemaal?) zal er maar één object zijn. Het zijn eigenlijk Singletons, alleen wil ik niet de beperkingen van het Singleton-patroon hebben.
Of je geeft mee welke class je verwacht.
Java:
1
2
3
4
5
6
7
public <T> T get(String name, Class<T> type)
{
  if( map.get(name).getClass().equals(type) )
    return (T)map.get(name);
  else
    return null; // or throw Exception
}
Dat doe ik nu ook. Het enige verschil is dat die ClassCastException "automatisch" wordt gegooid, niet door mezelf. ;)
Null teruggeven zou ik niet doen. Deze Registry wordt op een manier gebruikt waarbij dat altijd een programmeerfout is, en dus een fatale Exceptie op moet leveren.

Ik vind het zelf geen enkel probleem om ervan uit te gaan dat je code gewoon goed gaat en eventuele Exceptions ook te vangen. Dat is juist het idee van het exceptiemodel. Dat je niet meer de returnwaarde van elke aanroep hoeft te gaan controleren (zoals in C) maar dat er een exceptie gegooid wordt als er een uitzonderingsgeval (exceptioneel geval) voorkomt. :)
Bijvoorbeeld het lezen van een bestand. Daar open je ook het bestand, neem je aan dat dit wel goed gaat en leest er je gegevens uit. Om dat hele blok zet je dan een try/catch(IOException).

[ Voor 14% gewijzigd door 2playgames op 31-01-2009 20:57 ]

Pagina: 1