Discussion:
Falsches Pattern?
(zu alt für eine Antwort)
Achim Peters
2008-05-16 11:02:00 UTC
Permalink
Hi,

ich bin nach ein paar Jahren verhaltener Pause wieder massiv beim OO (in
Java) aber OO-design-technisch etwas aus der Übung, und da hab' ein gern
emal ein Problem(chen), was in einer Compile-Fehlermeldung endet.

Die Meldung ist mir klar, ich weiß, warum sie kommen muß, und ich weiß,
wie ich sie umgehe. Ich möchte sie aber _elegant_ wegbekommen, und sei
es, dass ich dafür ein anderes Klassen-Design wählen muss. Da es somit
eine Designfrage ist, wähle ich ein beispielhaftes, leichter
darzustellendes Problem, das m. E. die konkreten Abhängigkeiten
hinreichend widerspiegelt.

Es gibt Fahrzeuge F, die manchmal repariert werden müssen, und
spezialisierte Reparatur-Werkstätten W pro Fahrzeughersteller. Wer eine
Reparatur in Auftrag geben will, hat ein konkretes Fahrzeug an der Hand,
er weiß aber nicht, welcher Reparatur-Firmen-Typ zuständig ist (eine
Mercedes-Werkstatt für einen 500SL und A-Klasse, Opel für Manta und
Vectra, etc.) Das ist aber rauszubekommen (z. B. weiß das ein Guru).

Jedefalls erhält der Fzg-Besitzer eine Instanz einer von W abgeleiteten
Klasse und die möchte er gerne mit repariere(meinAuto) aufrufen.

Die Klasse W habe ich jetzt als abstrakte Oberklasse aller
Reparaturwerkstättentypen definiert (weil der Guru einfach eine
zuständige "Werkstatt" liefert) und die Klasse F als abstrakte
Oberklasse aller Fahrzeugmarken definiert (weil eine Werkstatt allgemein
ein "F(ahrzeug)" repariert, Z. B.
public abstract class F ...
public abstract class OpelAuto extends F ...
public abstract class MercedesAuto extends F ...
public Manta extends OpelAuto ...

public abstract class W ...
public class OpelWerkstatt extends W ...
public class MercedesWerkstatt extends W ...

Nun hat die abstrakte Klasse W eine abstrakte Methode
repariere(F fahrzeug). Der Fzg.besitzer kann also auf die vom Guru
erhaltene, von W abgeleitete Instanz einfach repariere(meinAuto)
aufrufen, egal welches konkrete Auto er hat und egal, welche konkrete
Werkstatt er vom Guru erhalten hat.

Jetzt kommt's:

Damit einer Opelwerkstatt auch nur Opelfahrzeuge zur Reparatur in
Auftrag gegeben werden, wollte ich die repariere-Methode dort so
implementieren:
public class OpelWerkstatt extends W ...
repariere(OpelAuto o)

Da meckert der Compiler natürlich, dass OpelWerkstatt doch bitte
repariere(F) implementieren möge.

Logisch, OpelWerkstatt ist eine W(erkstatt) und die kann nach Definition
Fahrzeuge reparieren, nicht nur Opels.

Lösung a)
Ich implementiere die allgemeinere Methode repariere(F) und prüfe zur
Laufzeit (explizit per instanceof oder implizit per casten) auf den Typ
OpelAuto
public class OpelWerkstatt extends W ...
repariere(F fahrzeug)
if (! fahrzeug instanceof OpelAuto)
throw new UngueltigeFahrzeugMarke()

Solche Typüberprüfungen zur Laufzeit finde ich aber unschön.

Gibt's da auch was besseres?

TIA

Bye
Achim
Frank
2008-05-16 11:23:01 UTC
Permalink
Achim Peters schrieb:

Generics ist Lsg. um die Typprüfung zur Compilezeit vorzunehmen ...
Post by Achim Peters
public abstract class W ...
public class OpelWerkstatt extends W ...
public class MercedesWerkstatt extends W ...
public abstract class W<T extends F> {

... repariereAuto(T fahrzeug);
}

public class OpelWerkstatt extends W<OpelAuto> {

... repariereAuto(OpelAuto fahrzeug);
}

...
Achim Peters
2008-05-16 12:01:41 UTC
Permalink
Post by Frank
Generics ist Lsg. um die Typprüfung zur Compilezeit vorzunehmen ...
Mift! Ich habe vor lauter Begeisterung vergessen zu erwähnen, dass Java
1.4 vorgegeben ist (ist in so einer Rational Software Development
Platform mit integriertem Eclipse, Websphere usw., also nicht
verhandelbar) ;-)
Post by Frank
public abstract class W<T extends F> {
... repariereAuto(T fahrzeug);
}
public class OpelWerkstatt extends W<OpelAuto> {
... repariereAuto(OpelAuto fahrzeug);
}
Danke! Dabei sehr ich jetzt aber, dass das Problem mit diesem
W-F-Klassendesign zur Compilezeit wohl prinzipiell nicht lösbar ist,
weil der Fahrzeugbesitzer zur Compilezeit nur irgendein W bekommen kann.
Da kann der /Compiler/ doch gar nicht sicher stellen _können_, dass der
W-Lieferer eine zum Fahrzeug passende Unterklasse liefert, oder?

public class Guru
public W<???> getWerkstatt(F fahrzeug)
if (f instanceof OpelAuto)
return new OpelWerkstatt();
else if (f instanceof Mercedes)
return new MercedesWerkstatt();
...

public class Besitzer
private F meinAuto = new OpelManta();

guruInstance.getWerkstatt(meinAuto).repariere(meinAuto);

Auf diese Art _kann_ sich die OpelWerkstatt offenbar gar nicht zur
Compilezeit gegen eine falsche Guru-Entscheidung wehren können?

Bye
Achim
Patrick Roemer
2008-05-17 13:07:44 UTC
Permalink
Post by Achim Peters
Danke! Dabei sehr ich jetzt aber, dass das Problem mit diesem
W-F-Klassendesign zur Compilezeit wohl prinzipiell nicht lösbar ist,
weil der Fahrzeugbesitzer zur Compilezeit nur irgendein W bekommen kann.
Da kann der /Compiler/ doch gar nicht sicher stellen _können_, dass der
W-Lieferer eine zum Fahrzeug passende Unterklasse liefert, oder?
public class Guru
public W<???> getWerkstatt(F fahrzeug)
if (f instanceof OpelAuto)
return new OpelWerkstatt();
else if (f instanceof Mercedes)
return new MercedesWerkstatt();
...
"Guru" ist ja wohl eine benamsungstechnische Bankrotterklaerung. ;)

Das ginge ja noch...

public class WerkstattVerzeichnis {
public <F extends Fahrzeug> Werkstatt<F> werkstattFuer(F fahrzeug) {
// ...
}
}
Post by Achim Peters
private F meinAuto = new OpelManta();
guruInstance.getWerkstatt(meinAuto).repariere(meinAuto);
...aber eben hier ist das Problem. Die statische Ueberpruefung
orientiert sich halt nur an den Variablentypen. Du willst aber Fahrzeug
wohl polymorph verwenden.

OpelFahrzeug fahrzeug = new OpelFahrzeug();
Werkstatt<OpelFahrzeug> werkstatt = verzeichnis.werkstattFuer(fahrzeug);
werkstatt.repariere(new MercedesFahrzeug()); // compiler error

Fahrzeug fahrzeug = new OpelFahrzeug();
Werkstatt<Fahrzeug> werkstatt = verzeichnis.werkstattFuer(fahrzeug);
werkstatt.repariere(new MercedesFahrzeug()); // class cast exception

Du muesstest also die Generifizierung strikt durch den gesamten Code
durchziehen...

void <F extends Fahrzeug> void doRepair(F fahrzeug) {
Werkstatt<F> werkstatt = this.verzeichnis.werkstattFuer(fahrzeug);
werkstatt.repariere(fahrzeug);
}

Viele Gruesse,
Patrick
Günter Vollmer
2008-05-17 09:17:11 UTC
Permalink
Post by Frank
Generics ist Lsg. um die Typprüfung zur Compilezeit vorzunehmen ...
Post by Achim Peters
public abstract class W ...
public class OpelWerkstatt extends W ...
public class MercedesWerkstatt extends W ...
public abstract class W<T extends F> {
... repariereAuto(T fahrzeug);
}
public class OpelWerkstatt extends W<OpelAuto> {
... repariereAuto(OpelAuto fahrzeug);
}
Hätte ich auch gesagt, hat aber den Nachteil, daß es mitunter unabhängige
Werkstätten gibt, die alle Typen reparieren.


Vielleicht bietet die abstracte Klasse W eine Methode, um zu erfragen, ob
die Werkstatt sich berufen fühlt, ein Auto eines bestimmten Typs zu
reparieren, etwa:
public boolean werkstattRepariert(Auto auto);

die wird von jeder konkreten Klasse entsprechend implementiert und eine
unabhängige Werkstatt sagt einfach zu allem "ja und Amen".

Der Guru könnte dann alle ihm bekannten Werkstätten abklappern und eine
Liste erstellen, welche W in Frage kommen.


Auto könnte übrigens auch Methoden implementieren in der Art:
public boolean isOpel() {return false;}
public boolean isMercedes() {return false;}

und AutoOpel die entsprechende überschreiben mit
public boolean isOpel() {return true;}

Peng, schon muß die WerkstattOpel nur noch implementieren:
public boolean werkstattRepariert(Auto auto){return auto.isOpel();}


bye
GV
Achim Peters
2008-05-17 12:36:21 UTC
Permalink
Post by Günter Vollmer
Post by Frank
Generics ist Lsg. um die Typprüfung zur Compilezeit vorzunehmen ...
Post by Achim Peters
public abstract class W ...
public class OpelWerkstatt extends W ...
public class MercedesWerkstatt extends W ...
public abstract class W<T extends F> {
... repariereAuto(T fahrzeug);
}
public class OpelWerkstatt extends W<OpelAuto> {
... repariereAuto(OpelAuto fahrzeug);
}
Hätte ich auch gesagt, hat aber den Nachteil, daß es mitunter unabhängige
Werkstätten gibt, die alle Typen reparieren.
Bei mir nicht. ;-)
Post by Günter Vollmer
Vielleicht bietet die abstracte Klasse W eine Methode, um zu erfragen, ob
die Werkstatt sich berufen fühlt, ein Auto eines bestimmten Typs zu
public boolean werkstattRepariert(Auto auto);
Der Guru liefert doch schon eine passenden Werkstatt. Wenn ich die
Opelwerkstatt davor schützen will, dass der Aufrufer die Antwort vom
Guru falsch benutzt, nützt es auch nichts, wenn ich dem Aufrufer die
Möglichkeit biete, vorher nochmal nachzufragen, ob die Werkstatt auch
wirklich zuständig ist. Er könnte das Nachfragen einfach weglassen oder
vergessen, oder auch diese Antwort falsch auswerten.

Zum Verständnis: Den Guru, die Werkstätten und die Autos implementiere
ich, benutzt werden sie von anderen. Und ich wollte diesen Benutzern
meiner Klassen falls möglich schon zur Compilezeit mitteilen, wenn sie
die Klassen falsch benutzen.

Bye
Achim
Christoph Dahlen
2008-05-16 11:20:52 UTC
Permalink
Post by Achim Peters
Gibt's da auch was besseres?
Besser ist relativ, aber sowas geht seit Java 5 sehr schön mit
Templates:

public abstract class Fahrzeug {}

public class Opel extends Fahrzeug {}

public abstract class Werkstatt<T extends Fahrzeug> {
public abstract void repariere(T fahrzeug);
}

public class OpelWerkstatt extends Werkstatt<Opel> {
@Override
public void repariere(Opel fahrzeug) {
}
}

Gruß,

Christoph
Achim Peters
2008-05-16 12:12:45 UTC
Permalink
Post by Christoph Dahlen
Post by Achim Peters
Gibt's da auch was besseres?
Besser ist relativ, aber sowas geht seit Java 5 sehr schön mit
Ja, sorry, Java 1.4 ist vorgegeben. Ich sähe aber auch hier nicht, dass
damit zur Compilezeit eine falsche Laufzeitentscheidung des Gurus
verhindert werden könnte, siehe Antwort auf Franks Posting. Mit dem
Template sparte ich mir aber zumindest das explizite Casten (so oder so
mit einer möglichen ClassCastExcpetion) oder noch mehr?

Bye
Achim
Patrick Roemer
2008-05-16 11:47:16 UTC
Permalink
Post by Achim Peters
public abstract class F ...
public abstract class OpelAuto extends F ...
public abstract class MercedesAuto extends F ...
public Manta extends OpelAuto ...
public abstract class W ...
public class OpelWerkstatt extends W ...
public class MercedesWerkstatt extends W ...
Mir kommt die Verwendung von Vererbung an sich hier schon sehr fishy vor.
Post by Achim Peters
Damit einer Opelwerkstatt auch nur Opelfahrzeuge zur Reparatur in
Auftrag gegeben werden, wollte ich die repariere-Methode dort so
public class OpelWerkstatt extends W ...
repariere(OpelAuto o)
Da meckert der Compiler natürlich, dass OpelWerkstatt doch bitte
repariere(F) implementieren möge.
Wenn Du eine Referenz auf ein allgemeines F erst mal in ein OpelAuto
casten musst, um es in einer OpelWerkstatt abgeben zu koennen, hast Du
doch eh nichts gewonnen, oder?

Viele Gruesse,
Patrick
Achim Peters
2008-05-16 12:23:50 UTC
Permalink
Post by Patrick Roemer
Post by Achim Peters
public abstract class F ...
public abstract class OpelAuto extends F ...
public abstract class MercedesAuto extends F ...
public Manta extends OpelAuto ...
public abstract class W ...
public class OpelWerkstatt extends W ...
public class MercedesWerkstatt extends W ...
Mir kommt die Verwendung von Vererbung an sich hier schon sehr fishy vor.
Deswegen stellte ich ja auch das Klassendesign an sich zur Debatte. Wie
würdest Du das anlegen? Die Opelwerkstatt weiß bei jedem einzelnen
Opelauto, wie es zu reparieren ist, aber bei keinem Mercedes. Ich wollte
mir in der Opelwerkstatt das Handling eines versehentlich reingekommenen
Mercedes per Exception sparen, wenn sich schon das Reinkommen elegant
verhindern lässt.
Post by Patrick Roemer
Post by Achim Peters
Damit einer Opelwerkstatt auch nur Opelfahrzeuge zur Reparatur in
Auftrag gegeben werden, wollte ich die repariere-Methode dort so
public class OpelWerkstatt extends W ...
repariere(OpelAuto o)
Da meckert der Compiler natürlich, dass OpelWerkstatt doch bitte
repariere(F) implementieren möge.
Wenn Du eine Referenz auf ein allgemeines F erst mal in ein OpelAuto
casten musst, um es in einer OpelWerkstatt abgeben zu koennen, hast Du
doch eh nichts gewonnen, oder?
Ich brauche mich in der Opelwerkstatt nicht um den Fall kümmern, dass
ein Mercedes abgegeben wurde.

Bye
Achim
Volker Glave
2008-05-16 13:37:39 UTC
Permalink
Post by Achim Peters
Post by Patrick Roemer
Post by Achim Peters
public abstract class F ...
public abstract class OpelAuto extends F ...
public abstract class MercedesAuto extends F ...
Mir kommt die Verwendung von Vererbung an sich hier schon sehr fishy vor.
Deswegen stellte ich ja auch das Klassendesign an sich zur Debatte. Wie
würdest Du das anlegen? Die Opelwerkstatt weiß bei jedem einzelnen
Opelauto, wie es zu reparieren ist, aber bei keinem Mercedes.
Lassen wir den Werkstatt-Kram erstmal beiseite.
Worin unterscheiden sich die beiden Klassen OpelAuto und
MercedesAuto? Gib ein Beispiel einer Methode, die es in OpelAuto
gibt, und in MercedesAuto nicht, oder umgekehrt.

Gruß
Volker
Achim Peters
2008-05-16 14:25:01 UTC
Permalink
Post by Volker Glave
Post by Achim Peters
Post by Patrick Roemer
Post by Achim Peters
public abstract class F ...
public abstract class OpelAuto extends F ...
public abstract class MercedesAuto extends F ...
Mir kommt die Verwendung von Vererbung an sich hier schon sehr fishy vor.
Deswegen stellte ich ja auch das Klassendesign an sich zur Debatte.
Worin unterscheiden sich die beiden Klassen OpelAuto und
MercedesAuto? Gib ein Beispiel einer Methode, die es in OpelAuto
gibt, und in MercedesAuto nicht, oder umgekehrt.
Für MercedesAuto gibt's noch keine konkrete Spezifikation.

Ein OpelAuto kann sich komplett in ein XML-Format wandeln.
String getXMLForm();
Aufgerufen von der Werkstatt.

Ein MercedesAuto verpackt sich z. B. häppchenweise in ein Binärformat.
byte[] getNextNBytes(64);

Alle Unterklassen von OpelAuto, bzw. MercedesAuto haben aber jeweils die
gemeinsame Eigenschaft, dass sie von einer Werkstatt entsprechenden Typs
verarbeitet werden können. Eine OpelWerkstatt kennt/kann alle OpelAutos
und nur die.

Die Werkstatt-Typen sind Clients zu über IP ansprechbaren Server-Typen.
Jeder Server-Typ kennt einen Satz Requests, den er bearbeiten kann. Die
Autos sind die Requests.

Das "repariere" ist "wende Dich an einen zuständigen Server und führe
den Request aus."

Alle "OpelWerkstatt-Server" werden per SOAP angesprochen und der erste
gefundene Server bleibt zuständig, bis er ausfällt. Da gibt es also in
der Opel-Werkstatt-Klasse eine Methode bildeSoapHeader() oder sowas, die
in der Mitte getXMLForm() der Opel-Auto-Requests aufruft.

Alle "MercedesWerkstatt-Server" werden z. B. per UDP und Round-Robin
angesprochen, die Daten müssen noch verschlüsselt werden und die
Mercedes-Auto-Requests dürfen nicht am Stück, sondern nur häppchenweise
transferiert werden. Die Mercedes-Werkstatt holt sich also immer die
nächste Portion von 64 Bytes per getNextNBytes(64).

Bye
Achim
Volker Glave
2008-05-16 15:54:42 UTC
Permalink
Post by Achim Peters
Post by Volker Glave
Post by Achim Peters
Post by Patrick Roemer
Post by Achim Peters
public abstract class F ...
public abstract class OpelAuto extends F ...
public abstract class MercedesAuto extends F ...
Mir kommt die Verwendung von Vererbung an sich hier schon sehr fishy vor.
Deswegen stellte ich ja auch das Klassendesign an sich zur Debatte.
Worin unterscheiden sich die beiden Klassen OpelAuto und
MercedesAuto? Gib ein Beispiel einer Methode, die es in OpelAuto
gibt, und in MercedesAuto nicht, oder umgekehrt.
Für MercedesAuto gibt's noch keine konkrete Spezifikation.
Ein OpelAuto kann sich komplett in ein XML-Format wandeln.
String getXMLForm();
Aufgerufen von der Werkstatt.
Packe diese Methodendeklaration in F.
Lasse Fahrzeuge defaultmäßig null oder einen irgendwie brauchbaren
(etwa als Klasse oder besser Interface gekapselten) Nullobjekt-XML-
String
liefern.
Gegebenenfalls können abgeleitete Klassen Genaueres liefern,
z. B. OpelAuto.
Post by Achim Peters
Ein MercedesAuto verpackt sich z. B. häppchenweise in ein Binärformat.
byte[] getNextNBytes(64);
Packe diese Methodendeklaration in F.
Lasse Fahrzeuge defaultmäßig null oder ein byte[0]-Array oder
ein irgendwie brauchbares (etwa als Klasse oder besser Interface
gekapseltes) Nullobjekt-byte[]-Array liefern.
Gegebenenfalls können abgeleitete Klassen Genaueres liefern,
z. B. MercedesAuto.
Post by Achim Peters
Alle Unterklassen von OpelAuto, bzw. MercedesAuto haben aber jeweils die
gemeinsame Eigenschaft, dass sie von einer Werkstatt entsprechenden Typs
verarbeitet werden können. Eine OpelWerkstatt kennt/kann alle OpelAutos
und nur die.
Die Werkstatt-Typen sind Clients zu über IP ansprechbaren Server-Typen.
Jeder Server-Typ kennt einen Satz Requests, den er bearbeiten kann. Die
Autos sind die Requests.
Das "repariere" ist "wende Dich an einen zuständigen Server und führe
den Request aus."
Alle "OpelWerkstatt-Server" werden per SOAP angesprochen und der erste
gefundene Server bleibt zuständig, bis er ausfällt. Da gibt es also in
der Opel-Werkstatt-Klasse eine Methode bildeSoapHeader() oder sowas, die
in der Mitte getXMLForm() der Opel-Auto-Requests aufruft.
Wenn das an OpelWerkstatt.repariere() übergebene Fahrzeug
bei getXMLForm() ein Nullobjekt-XML-String liefert, ignoriere es.
("Ignorieren" ist eigtl. falsch gesagt, denn es wird ja das
Passende getan - nichts.)
Würde man hier richtig mit Nullobjekt-Pattern arbeiten, entfiele
die Fallunterscheidung.
http://de.wikipedia.org/wiki/Nullobjekt_%28Entwurfsmuster%29
Post by Achim Peters
Alle "MercedesWerkstatt-Server" werden z. B. per UDP und Round-Robin
angesprochen, die Daten müssen noch verschlüsselt werden und die
Mercedes-Auto-Requests dürfen nicht am Stück, sondern nur häppchenweise
transferiert werden. Die Mercedes-Werkstatt holt sich also immer die
nächste Portion von 64 Bytes per getNextNBytes(64).
Wenn das an MercedesWerkstatt.repariere() übergebene Fahrzeug
bei getNextNBytes() ein Nullobjekt-byte[]-Array liefern, ignoriere es.
("Ignorieren" ist eigtl. falsch gesagt, denn es wird ja das
Passende getan - nichts.)
Würde man hier richtig mit Nullobjekt-Pattern arbeiten, entfiele
die Fallunterscheidung.
http://de.wikipedia.org/wiki/Nullobjekt_%28Entwurfsmuster%29

Gruß
Volker
Patrick Roemer
2008-05-17 14:40:59 UTC
Permalink
Post by Volker Glave
Post by Achim Peters
Ein OpelAuto kann sich komplett in ein XML-Format wandeln.
String getXMLForm();
Aufgerufen von der Werkstatt.
Packe diese Methodendeklaration in F.
Lasse Fahrzeuge defaultmäßig null oder einen irgendwie brauchbaren
(etwa als Klasse oder besser Interface gekapselten) Nullobjekt-XML-
String
liefern.
class OpelFahrzeug extends Fahrzeug {
@override
public OpelXml getOpelXml() {
// ...
}

@override
public MercedesBinary getMercedesBinary() {
return MercedesBinary.NULL;
}

@override
public VWBlob getVWBlob() {
return VWBlob.NULL;
}

// ...
}

Nicht ernsthaft, oder?
Post by Volker Glave
Wenn das an OpelWerkstatt.repariere() übergebene Fahrzeug
bei getXMLForm() ein Nullobjekt-XML-String liefert, ignoriere es.
("Ignorieren" ist eigtl. falsch gesagt, denn es wird ja das
Passende getan - nichts.)
Das ist nicht ganz, was ich von einer Werkstatt erwarte, der ich eine
#repariere-Botschaft schicke. Irgendwie wird die sie zurueckmelden
muessen, dass sie dieses Modell nicht verarzten kann. Und genau das will
der OP wohl vermeiden. Ob diese Pruefung und Signalisierung anhand eines
instanceof plus Exception geschieht, oder in einem NullObject gekapselt
ist, ist da IMO eher nebensaechlich.

Viele Gruesse,
Patrick
Volker Glave
2008-05-19 06:49:40 UTC
Permalink
Post by Patrick Roemer
Post by Volker Glave
Post by Achim Peters
Ein OpelAuto kann sich komplett in ein XML-Format wandeln.
String getXMLForm();
Aufgerufen von der Werkstatt.
Packe diese Methodendeklaration in F.
Lasse Fahrzeuge defaultmäßig null oder einen irgendwie brauchbaren
(etwa als Klasse oder besser Interface gekapselten) Nullobjekt-XML-
String
liefern.
class OpelFahrzeug extends Fahrzeug {
@override
public OpelXml getOpelXml() {
// ...
}
@override
public MercedesBinary getMercedesBinary() {
return MercedesBinary.NULL;
}
@override
public VWBlob getVWBlob() {
return VWBlob.NULL;
}
// ...
}
Nicht ernsthaft, oder?
Da es eine funktionierende Variante ist, selbstverständlich
ernsthaft.
Post by Patrick Roemer
Post by Volker Glave
Wenn das an OpelWerkstatt.repariere() übergebene Fahrzeug
bei getXMLForm() ein Nullobjekt-XML-String liefert, ignoriere es.
("Ignorieren" ist eigtl. falsch gesagt, denn es wird ja das
Passende getan - nichts.)
Das ist nicht ganz, was ich von einer Werkstatt erwarte, der ich eine
#repariere-Botschaft schicke. Irgendwie wird die sie zurueckmelden
muessen, dass sie dieses Modell nicht verarzten kann.
Im Gegenteil. Ein Zurückmelden des Nicht-Verarzten-Könnens wäre
bei einer reparierenAnfrage() angebracht. Führe ich für ein
nicht ganz passendes Fahrzeug dessenungeachtet repariere()
aus, ist das Fahrzeug anschließend eben teilrepariert oder (als
Grenzfall) unrepariert.
Post by Patrick Roemer
Und genau das will
der OP wohl vermeiden. Ob diese Pruefung und Signalisierung anhand eines
instanceof plus Exception geschieht, oder in einem NullObject gekapselt
ist, ist da IMO eher nebensaechlich.
Ein nicht ganz passendes Fahrzeug in eine Werkstatt zur Reparatur
zu geben, ist nichts Ungewöhnliches und erst recht keine Ausnahme.
Das Fahrzeug ist anschließend teilrepariert (möglicherweise auch
unrepariert, möglicherweise auch ganz repariert).

Gruß
Volker
Achim Peters
2008-05-19 09:01:46 UTC
Permalink
Post by Volker Glave
Post by Patrick Roemer
Post by Volker Glave
Post by Achim Peters
Ein OpelAuto kann sich komplett in ein XML-Format wandeln.
String getXMLForm();
Aufgerufen von der Werkstatt.
Packe diese Methodendeklaration in F.
Lasse Fahrzeuge defaultmäßig null oder einen irgendwie brauchbaren
(etwa als Klasse oder besser Interface gekapselten) Nullobjekt-XML-
String
liefern.
Wenn das an OpelWerkstatt.repariere() übergebene Fahrzeug
bei getXMLForm() ein Nullobjekt-XML-String liefert, ignoriere es.
("Ignorieren" ist eigtl. falsch gesagt, denn es wird ja das
Passende getan - nichts.)
Das ist nicht ganz, was ich von einer Werkstatt erwarte, der ich eine
#repariere-Botschaft schicke. Irgendwie wird die sie zurueckmelden
muessen, dass sie dieses Modell nicht verarzten kann.
Im Gegenteil. Ein Zurückmelden des Nicht-Verarzten-Könnens wäre
bei einer reparierenAnfrage() angebracht. Führe ich für ein
nicht ganz passendes Fahrzeug dessenungeachtet repariere()
aus, ist das Fahrzeug anschließend eben teilrepariert oder (als
Grenzfall) unrepariert.
Post by Patrick Roemer
Und genau das will
der OP wohl vermeiden. Ob diese Pruefung und Signalisierung anhand eines
instanceof plus Exception geschieht, oder in einem NullObject gekapselt
ist, ist da IMO eher nebensaechlich.
Ein nicht ganz passendes Fahrzeug in eine Werkstatt zur Reparatur
zu geben, ist nichts Ungewöhnliches und erst recht keine Ausnahme.
Bei mir schon. Das ganze spiegelte ja nur soweit die tatsächlichen
Gegebenheiten wider, wie ich sie darstellte. Interpolationen darüber
hinaus können dann von meinem Problem abweichen und das tun sie hier.
Ich suchte eine Möglichkeit, die Abgabe eines "nicht ganz passenden"
Fahrzeugs sogar unmöglich zu machen.

Aber danke für Deinen Hinweis auf das Null-Objekt. Ist interessant,
kannte ich in der Form noch nicht, und kann man sicher später noch mal
verwenden.

Bye
Achim
Volker Glave
2008-05-19 10:55:12 UTC
Permalink
Post by Achim Peters
Post by Volker Glave
Post by Patrick Roemer
Post by Volker Glave
Post by Achim Peters
Ein OpelAuto kann sich komplett in ein XML-Format wandeln.
String getXMLForm();
Aufgerufen von der Werkstatt.
Packe diese Methodendeklaration in F.
Lasse Fahrzeuge defaultmäßig null oder einen irgendwie brauchbaren
(etwa als Klasse oder besser Interface gekapselten) Nullobjekt-XML-
String
liefern.
Wenn das an OpelWerkstatt.repariere() übergebene Fahrzeug
bei getXMLForm() ein Nullobjekt-XML-String liefert, ignoriere es.
("Ignorieren" ist eigtl. falsch gesagt, denn es wird ja das
Passende getan - nichts.)
Das ist nicht ganz, was ich von einer Werkstatt erwarte, der ich eine
#repariere-Botschaft schicke. Irgendwie wird die sie zurueckmelden
muessen, dass sie dieses Modell nicht verarzten kann.
Im Gegenteil. Ein Zurückmelden des Nicht-Verarzten-Könnens wäre
bei einer reparierenAnfrage() angebracht. Führe ich für ein
nicht ganz passendes Fahrzeug dessenungeachtet repariere()
aus, ist das Fahrzeug anschließend eben teilrepariert oder (als
Grenzfall) unrepariert.
Post by Patrick Roemer
Und genau das will
der OP wohl vermeiden. Ob diese Pruefung und Signalisierung anhand eines
instanceof plus Exception geschieht, oder in einem NullObject gekapselt
ist, ist da IMO eher nebensaechlich.
Ein nicht ganz passendes Fahrzeug in eine Werkstatt zur Reparatur
zu geben, ist nichts Ungewöhnliches und erst recht keine Ausnahme.
Bei mir schon. Das ganze spiegelte ja nur soweit die tatsächlichen
Gegebenheiten wider, wie ich sie darstellte. Interpolationen darüber
hinaus können dann von meinem Problem abweichen und das tun sie hier.
Ich suchte eine Möglichkeit, die Abgabe eines "nicht ganz passenden"
Fahrzeugs sogar unmöglich zu machen.
Das ist nicht empfehlenswert.
Eine Y-Werkstatt kann und wird überlichweise einen Großteil
der Defekte eines X-Fahrzeug reparieren können.
Und selbst wenn nicht - dann verbleibt das Fahrzeug eben
unverändert unrepariert.
Eine Ausnahmesituation ist weit und breit nicht zu sehen.

Gruß
Volker
Achim Peters
2008-05-19 11:56:04 UTC
Permalink
Post by Volker Glave
Post by Achim Peters
Post by Volker Glave
Post by Patrick Roemer
Post by Volker Glave
Post by Achim Peters
Ein OpelAuto kann sich komplett in ein XML-Format wandeln.
String getXMLForm();
Aufgerufen von der Werkstatt.
Packe diese Methodendeklaration in F.
Lasse Fahrzeuge defaultmäßig null oder einen irgendwie brauchbaren
(etwa als Klasse oder besser Interface gekapselten) Nullobjekt-XML-
String
liefern.
Wenn das an OpelWerkstatt.repariere() übergebene Fahrzeug
bei getXMLForm() ein Nullobjekt-XML-String liefert, ignoriere es.
("Ignorieren" ist eigtl. falsch gesagt, denn es wird ja das
Passende getan - nichts.)
Das ist nicht ganz, was ich von einer Werkstatt erwarte, der ich eine
#repariere-Botschaft schicke. Irgendwie wird die sie zurueckmelden
muessen, dass sie dieses Modell nicht verarzten kann.
Im Gegenteil. Ein Zurückmelden des Nicht-Verarzten-Könnens wäre
bei einer reparierenAnfrage() angebracht. Führe ich für ein
nicht ganz passendes Fahrzeug dessenungeachtet repariere()
aus, ist das Fahrzeug anschließend eben teilrepariert oder (als
Grenzfall) unrepariert.
Post by Patrick Roemer
Und genau das will
der OP wohl vermeiden. Ob diese Pruefung und Signalisierung anhand eines
instanceof plus Exception geschieht, oder in einem NullObject gekapselt
ist, ist da IMO eher nebensaechlich.
Ein nicht ganz passendes Fahrzeug in eine Werkstatt zur Reparatur
zu geben, ist nichts Ungewöhnliches und erst recht keine Ausnahme.
Bei mir schon. Das ganze spiegelte ja nur soweit die tatsächlichen
Gegebenheiten wider, wie ich sie darstellte. Interpolationen darüber
hinaus können dann von meinem Problem abweichen und das tun sie hier.
Ich suchte eine Möglichkeit, die Abgabe eines "nicht ganz passenden"
Fahrzeugs sogar unmöglich zu machen.
Das ist nicht empfehlenswert.
Eine Y-Werkstatt kann und wird überlichweise einen Großteil
der Defekte eines X-Fahrzeug reparieren können.
Und selbst wenn nicht - dann verbleibt das Fahrzeug eben
unverändert unrepariert.
Ich habe aber keine Werkstätten. Die waren nur ein Beispiel und zwar bis
zu dem Übereinstimmungsgrad, den ich beschrieb.

Bye
Achim
Volker Glave
2008-05-19 13:55:12 UTC
Permalink
Post by Achim Peters
Post by Volker Glave
Das ist nicht empfehlenswert.
Eine Y-Werkstatt kann und wird überlichweise einen Großteil
der Defekte eines X-Fahrzeug reparieren können.
Und selbst wenn nicht - dann verbleibt das Fahrzeug eben
unverändert unrepariert.
Ich habe aber keine Werkstätten. Die waren nur ein Beispiel und zwar bis
zu dem Übereinstimmungsgrad, den ich beschrieb.
Ok, für das eigentliche Problem, das du hast, war das von
dir verwendete Fahrzeug/Werkstatt-Beispiel also ebenso
anschaulich wie ungeschickt gewählt gewesen, verstehe.

Gruß
Volker
Achim Peters
2008-05-19 14:48:10 UTC
Permalink
Post by Volker Glave
Post by Achim Peters
Post by Volker Glave
Das ist nicht empfehlenswert.
Eine Y-Werkstatt kann und wird überlichweise einen Großteil
der Defekte eines X-Fahrzeug reparieren können.
Und selbst wenn nicht - dann verbleibt das Fahrzeug eben
unverändert unrepariert.
Ich habe aber keine Werkstätten. Die waren nur ein Beispiel und zwar bis
zu dem Übereinstimmungsgrad, den ich beschrieb.
Ok, für das eigentliche Problem, das du hast, war das von
dir verwendete Fahrzeug/Werkstatt-Beispiel also ebenso
anschaulich wie ungeschickt gewählt gewesen, verstehe.
Sehe ich nicht so, es kamen ja brauchbare Vorschläge. Und wenn, täte es
mir leid, aber das lässt sich bei einer in Newsgroups notwendigen
Abstraktion wohl nicht immer vermeiden, sorry. Danke für Deine Mithilfe.

Bye
Achim
Volker Glave
2008-05-19 16:25:55 UTC
Permalink
Post by Achim Peters
Post by Volker Glave
Post by Achim Peters
Post by Volker Glave
Das ist nicht empfehlenswert.
Eine Y-Werkstatt kann und wird überlichweise einen Großteil
der Defekte eines X-Fahrzeug reparieren können.
Und selbst wenn nicht - dann verbleibt das Fahrzeug eben
unverändert unrepariert.
Ich habe aber keine Werkstätten. Die waren nur ein Beispiel und zwar bis
zu dem Übereinstimmungsgrad, den ich beschrieb.
Ok, für das eigentliche Problem, das du hast, war das von
dir verwendete Fahrzeug/Werkstatt-Beispiel also ebenso
anschaulich wie ungeschickt gewählt gewesen, verstehe.
Sehe ich nicht so, es kamen ja brauchbare Vorschläge.
Stimmt auch wieder. (Andere mögliche Antwort: "Sehe ich auch
so, aber es kamen ja dennoch brauchbare Vorschläge.".)
Post by Achim Peters
Und wenn, täte es
mir leid, aber das lässt sich bei einer in Newsgroups notwendigen
Abstraktion wohl nicht immer vermeiden, sorry.
Macht gar nichts, die ursprüngliche Frage konnte auf eine Weise
verstanden werden, die den heisigen Zweig nicht unbedingt obsolet
macht.

Gruß
Volker
Patrick Roemer
2008-05-19 12:37:50 UTC
Permalink
Post by Volker Glave
Post by Patrick Roemer
Post by Volker Glave
Post by Achim Peters
Ein OpelAuto kann sich komplett in ein XML-Format wandeln.
String getXMLForm();
Aufgerufen von der Werkstatt.
Packe diese Methodendeklaration in F.
Lasse Fahrzeuge defaultmäßig null oder einen irgendwie brauchbaren
(etwa als Klasse oder besser Interface gekapselten) Nullobjekt-XML-
String
liefern.
[...]
Post by Volker Glave
Post by Patrick Roemer
Nicht ernsthaft, oder?
Da es eine funktionierende Variante ist, selbstverständlich
ernsthaft.
Die Variante mag "funktionieren", aber sie muellt die abstrakte API mit
herstellerspezifischer Funktionalitaet zu. Das ist unintuitiv (warum
soll eine Opel-Karre eine Methode haben, um ein Mercedes-spezifisches
Datenformat auszugeben?), erfordert Boilerplate-Code, und fuehrt jede
Menge unnoetiger Ueberkreuzabhaengigkeiten zwischen den
herstellerspezifischen Modulen ein, somit die besten Zutaten fuer einen
Wartungsalptraum.
Post by Volker Glave
Ein nicht ganz passendes Fahrzeug in eine Werkstatt zur Reparatur
zu geben, ist nichts Ungewöhnliches und erst recht keine Ausnahme.
Das Fahrzeug ist anschließend teilrepariert (möglicherweise auch
unrepariert, möglicherweise auch ganz repariert).
Im Szenario des OP scheint das durchaus ungewoehnlich und eine Ausnahme
zu sein - es scheitert ja schon daran, dass der Reparaturauftrag nicht
erstellt werden kann, weil das benoetigte Datenformat gar nicht sinnvoll
erzeugt werden kann. Die Benennung der beteiligten Klassen ist
allerdings nicht deckungsgleich mit den Prozessen, die modelliert werden
sollen, und laedt zu Missverstaendnissen ein.

Viele Gruesse,
Patrick
Volker Glave
2008-05-19 13:49:31 UTC
Permalink
Post by Patrick Roemer
Post by Volker Glave
Post by Patrick Roemer
Post by Volker Glave
Post by Achim Peters
Ein OpelAuto kann sich komplett in ein XML-Format wandeln.
String getXMLForm();
Aufgerufen von der Werkstatt.
Packe diese Methodendeklaration in F.
Lasse Fahrzeuge defaultmäßig null oder einen irgendwie brauchbaren
(etwa als Klasse oder besser Interface gekapselten) Nullobjekt-XML-
String
liefern.
[...]
Post by Volker Glave
Post by Patrick Roemer
Nicht ernsthaft, oder?
Da es eine funktionierende Variante ist, selbstverständlich
ernsthaft.
Die Variante mag "funktionieren",
Sie funktioniert.
Post by Patrick Roemer
aber sie muellt die abstrakte API mit
herstellerspezifischer Funktionalitaet zu.
Nicht zwingend.
Um ebenfalls mit "mögen" zu argumentieren: Die vermeintliche
Herstellerspezifik mag wegzuabstrahieren sein.
Post by Patrick Roemer
Das ist unintuitiv (warum
soll eine Opel-Karre eine Methode haben, um ein Mercedes-spezifisches
Datenformat auszugeben?),
Beispielsweise um den (möglicherweise geschickten, möglicherweise
abstrusen, egal) Wunsch des OP zu erfüllen.
Post by Patrick Roemer
erfordert Boilerplate-Code,
Nicht zwingend. (Beispiel: Das mag eine Basisklasse schon liefern.)
Oder: Und wenn schon.
Oder: Dann lass es halt bleiben. War ja nur ein (funktionierender)
Vorschlag.
Post by Patrick Roemer
und fuehrt jede
Menge unnoetiger Ueberkreuzabhaengigkeiten zwischen den
herstellerspezifischen Modulen ein, somit die besten Zutaten fuer einen
Wartungsalptraum.
Again: Die vermeintliche Herstellerspezifik mag wegzuabstrahieren
sein.
Post by Patrick Roemer
Post by Volker Glave
Ein nicht ganz passendes Fahrzeug in eine Werkstatt zur Reparatur
zu geben, ist nichts Ungewöhnliches und erst recht keine Ausnahme.
Das Fahrzeug ist anschließend teilrepariert (möglicherweise auch
unrepariert, möglicherweise auch ganz repariert).
Im Szenario des OP scheint das durchaus ungewoehnlich und eine Ausnahme
zu sein - es scheitert ja schon daran, dass der Reparaturauftrag nicht
erstellt werden kann, weil das benoetigte Datenformat gar nicht sinnvoll
erzeugt werden kann. Die Benennung der beteiligten Klassen ist
allerdings nicht deckungsgleich mit den Prozessen, die modelliert werden
sollen, und laedt zu Missverstaendnissen ein.
Es verbleibt als eine mögliche Lösung, die genannt werden konnte.

Gruß
Volker
Alfred
2008-05-23 04:47:43 UTC
Permalink
Post by Volker Glave
Post by Patrick Roemer
class OpelFahrzeug extends Fahrzeug {
@override
public OpelXml getOpelXml() {
// ...
}
@override
public MercedesBinary getMercedesBinary() {
return MercedesBinary.NULL;
}
@override
public VWBlob getVWBlob() {
return VWBlob.NULL;
}
// ...
}
Nicht ernsthaft, oder?
Da es eine funktionierende Variante ist, selbstverständlich
ernsthaft.
...
Was geht es einen Opel an, wie ein Mercedes darzustellen ist?
Solche Funktionalität gehört überhaupt nicht an das Auto
sondern an einen Service, der dieses Wissen bei sich kapselt.

Alfred
Patrick Roemer
2008-05-17 14:13:07 UTC
Permalink
Post by Achim Peters
Post by Patrick Roemer
Mir kommt die Verwendung von Vererbung an sich hier schon sehr fishy vor.
Deswegen stellte ich ja auch das Klassendesign an sich zur Debatte. Wie
würdest Du das anlegen?
Hmmm... Wenn die von Dir beschriebenen XML-/Binaerformate sich nicht
irgendwie extern ueber Informationen aus der Fahrzeug-API
zusammenbasteln lassen, wird's tatsaechlich schwierig, die Vererbung
loszuwerden.

Wenn die uebertragenen Informationen an sich gleichartig sind, und nur
das Marshalling unterschiedlich ist, wuerde ich vielleicht versuchen,
das in ein ReparaturProtokoll auszulagern.

BTW, insgesamt finde ich die Benennungen etwas verwirrend. Hier geht's
doch nicht um die Reparatur von Autos, wie werkstatt.repariere(fahrzeug)
es nahelegt, sondern um die Erstellung und Uebermittlung von
Reparaturauftraegen.
Post by Achim Peters
Die Opelwerkstatt weiß bei jedem einzelnen
Opelauto, wie es zu reparieren ist, aber bei keinem Mercedes. Ich wollte
mir in der Opelwerkstatt das Handling eines versehentlich reingekommenen
Mercedes per Exception sparen, wenn sich schon das Reinkommen elegant
verhindern lässt.
[...]
Post by Achim Peters
Post by Patrick Roemer
Wenn Du eine Referenz auf ein allgemeines F erst mal in ein OpelAuto
casten musst, um es in einer OpelWerkstatt abgeben zu koennen, hast Du
doch eh nichts gewonnen, oder?
Ich brauche mich in der Opelwerkstatt nicht um den Fall kümmern, dass
ein Mercedes abgegeben wurde.
Dann brauchst Du aber im Prinzip auch keine polymorphe Methode in der
Oberklasse, oder? Waere es eine Option, einen ReparaturAuftrag
einzufuehren, der anstelle einer Werkstatt vom "Guru" herausgegeben
wird, und der im Prinzip ein fest auf eine Marke getyptes (F,W)-Tupel
darstellt, das die spezifische API der Werkstatt kennt? Ganz naiv:

abstract class Werkstatt {
// *keine* abstrakte #repariere(Fahrzeug)
}

class OpelWerkstatt extends Werkstatt {
public void repariere(OpelFahrzeug fahrzeug) {
// ...
}
}

interface ReparaturAuftrag {
void repariere();
}

class OpelReparaturAuftrag extends ReparaturAuftrag {
private OpelFahrzeug fahrzeug;
private OpelWerkstatt werkstatt;

public void repariere() {
werkstatt.repariere(fahrzeug);
}
}

class WerkstattVerzeichnis {
public ReparaturAuftrag auftrag(Fahrzeug fahrzeug) {
// ...
}
}

Viele Gruesse,
Patrick
Achim Peters
2008-05-18 17:53:53 UTC
Permalink
Post by Patrick Roemer
BTW, insgesamt finde ich die Benennungen etwas verwirrend. Hier geht's
doch nicht um die Reparatur von Autos, wie werkstatt.repariere(fahrzeug)
es nahelegt, sondern um die Erstellung und Uebermittlung von
Reparaturauftraegen.
Na ja, dem Aufrufer dürfte es egal sein, ob die bearbeitende Klasse das
noch per TCP/IP delegiert (Auftrag) oder selber macht (repariere). Man
könnte das ganze auch dingHandler.handle(ding) nennen. ;-) Ist ja nur
die beispielhafte Darstellung, sowas hinkt immer irgendwo.
Post by Patrick Roemer
Post by Achim Peters
Die Opelwerkstatt weiß bei jedem einzelnen
Opelauto, wie es zu reparieren ist, aber bei keinem Mercedes. Ich wollte
mir in der Opelwerkstatt das Handling eines versehentlich reingekommenen
Mercedes per Exception sparen, wenn sich schon das Reinkommen elegant
verhindern lässt.
[...]
Post by Achim Peters
Post by Patrick Roemer
Wenn Du eine Referenz auf ein allgemeines F erst mal in ein OpelAuto
casten musst, um es in einer OpelWerkstatt abgeben zu koennen, hast Du
doch eh nichts gewonnen, oder?
Ich brauche mich in der Opelwerkstatt nicht um den Fall kümmern, dass
ein Mercedes abgegeben wurde.
Dann brauchst Du aber im Prinzip auch keine polymorphe Methode in der
Oberklasse, oder?
Um dem (zukünftigen) Entwickler einer MercedesWerkstatt zu zeigen,
welches Interface er bedienen können soll?
Post by Patrick Roemer
Waere es eine Option, einen ReparaturAuftrag
einzufuehren, der anstelle einer Werkstatt vom "Guru" herausgegeben
wird, und der im Prinzip ein fest auf eine Marke getyptes (F,W)-Tupel
darstellt, das die spezifische API der Werkstatt kennt?
Ja, das löst mein Problem. Danke! Jetzt muss ich nur noch schauen, ob
das so ins (nicht beschriebene) Gesamtkonzept passt.
Post by Patrick Roemer
class OpelReparaturAuftrag extends ReparaturAuftrag {
private OpelFahrzeug fahrzeug;
private OpelWerkstatt werkstatt;
public void repariere() {
werkstatt.repariere(fahrzeug);
}
}
Schick. So wird der Zusammenhang OpelWerkstatt<->OpelFahrzeug gekapselt.
Hätt' ich auch selbst drauf kommen können, wenn es mir nur eingefallen
wäre. ;-)

Bye
Achim
Bernd Eckenfels
2008-05-18 18:08:17 UTC
Permalink
Post by Achim Peters
Schick. So wird der Zusammenhang OpelWerkstatt<->OpelFahrzeug gekapselt.
Hätt' ich auch selbst drauf kommen können, wenn es mir nur eingefallen
wäre. ;-)
Aber gleich mal nebenbeibemerkt. In der Praxis ist es eher selten dass man
Fachobjekte in der Art modelliert. Dadurch wird das Softwaresystem einfach
zu statisch. Metadaten getriebene Systeme sind da einfach besser. Solche
Typischeren Aufrufe bewegen sich eher auf internen Datenstrukturen als auf
Fachkonzepten.

(Ja es gibt Ausnahmen, speziell wenn man MDA einsetzt, aber die Systeme
merken dann erst nach einiger Zeit was das für Probleme macht).

Gruss
Bernd
Achim Peters
2008-05-19 11:57:51 UTC
Permalink
Post by Bernd Eckenfels
Post by Achim Peters
Schick. So wird der Zusammenhang OpelWerkstatt<->OpelFahrzeug gekapselt.
Hätt' ich auch selbst drauf kommen können, wenn es mir nur eingefallen
wäre. ;-)
Aber gleich mal nebenbeibemerkt. In der Praxis ist es eher selten dass man
Fachobjekte in der Art modelliert. Dadurch wird das Softwaresystem einfach
zu statisch. Metadaten getriebene Systeme sind da einfach besser. Solche
Typischeren Aufrufe bewegen sich eher auf internen Datenstrukturen als auf
Fachkonzepten.
Das sind in dem Sinne "interne Datenstrukturen". Meine Aufrufer arbeiten
im selben Projekt. Der Kunde mit seinem Fachkonzept bekommt davon nichts
mit.

Bye
Achim
Jürgen Wille
2008-05-16 14:43:48 UTC
Permalink
Post by Achim Peters
Es gibt Fahrzeuge F, die manchmal repariert werden müssen, und
spezialisierte Reparatur-Werkstätten W pro Fahrzeughersteller. Wer eine
Reparatur in Auftrag geben will, hat ein konkretes Fahrzeug an der Hand,
er weiß aber nicht, welcher Reparatur-Firmen-Typ zuständig ist
Jedefalls erhält der Fzg-Besitzer eine Instanz einer von W abgeleiteten
Klasse und die möchte er gerne mit repariere(meinAuto) aufrufen.
[..]
Post by Achim Peters
Nun hat die abstrakte Klasse W eine abstrakte Methode repariere(F
fahrzeug). Der Fzg.besitzer kann also auf die vom Guru erhaltene, von W
abgeleitete Instanz einfach repariere(meinAuto) aufrufen, egal welches
konkrete Auto er hat und egal, welche konkrete Werkstatt er vom Guru
erhalten hat.
Welches Verhalten willst Du haben?

Möchtest Du, dass Du bei gegebenem Fahrzeug (und somit bekanntem
Hersteller) vom Guru definitiv eine Werkstatt zugewiesen bekommst, die
das Fahrzeug reparieren kann? Dann muss der Guru die Prüfung machen -
anhand des Herstellers des Fahrzeugs und anhand der "Selbstauskunft" der
Werkstätten, welche Fahrzeuge sie reparieren.

Andernfalls wird das immer eine Prüfung zur Laufzeit werden, egal wie die
implementiert ist.

Gruß Jürgen
Achim Peters
2008-05-16 15:12:49 UTC
Permalink
Post by Jürgen Wille
Post by Achim Peters
Es gibt Fahrzeuge F, die manchmal repariert werden müssen, und
spezialisierte Reparatur-Werkstätten W pro Fahrzeughersteller. Wer eine
Reparatur in Auftrag geben will, hat ein konkretes Fahrzeug an der Hand,
er weiß aber nicht, welcher Reparatur-Firmen-Typ zuständig ist
Jedefalls erhält der Fzg-Besitzer eine Instanz einer von W abgeleiteten
Klasse und die möchte er gerne mit repariere(meinAuto) aufrufen.
[..]
Post by Achim Peters
Nun hat die abstrakte Klasse W eine abstrakte Methode repariere(F
fahrzeug). Der Fzg.besitzer kann also auf die vom Guru erhaltene, von W
abgeleitete Instanz einfach repariere(meinAuto) aufrufen, egal welches
konkrete Auto er hat und egal, welche konkrete Werkstatt er vom Guru
erhalten hat.
Welches Verhalten willst Du haben?
Ich wollte die Opel-Werkstatt davor schützen, mit unbekannten Autos
aufgerufen zu werden.
Post by Jürgen Wille
Andernfalls wird das immer eine Prüfung zur Laufzeit werden, egal wie die
implementiert ist.
... oder das Klassendesign ist noch nicht optimal.

Man könnte ja den Guru ins Auto packen und statt

guruInstance.getWerkstatt(meinAuto).repariere(meinAuto);

einfach

meinAuto.lassDichReparieren()

aufrufen und bereits in den entsprechenden Zwischen-Klassen

public abstract class OpelAuto extends F
public final void lassDichReparieren() {
new OpelWerkstatt().repariere(this);
}

public abstract class MercedesAuto extends F
public final void lassDichReparieren() {
new MercedesWerkstatt().repariere(this);
}

implementieren. Vielleicht ist das ja schon der bessere Schutz, weil man
als Implementierer von Autos und Werkstätten "sieht", dass die richtige
Werkstatt aufgerufen wird?

Während das vorige

guruInstance.getWerkstatt(meinAuto).repariere(meinAuto);

von Dritten aufgerufen wird und ich keinen direkten Einfluss auf falsche
Aufrufe à la

guruInstance.getWerkstatt(meinErstesAuto).repariere(meinAnderesAuto);

habe.

Bye
Achim
Jürgen Wille
2008-05-16 16:02:03 UTC
Permalink
Post by Achim Peters
Ich wollte die Opel-Werkstatt davor schützen, mit unbekannten Autos
aufgerufen zu werden.
[..]
Post by Achim Peters
Man könnte ja den Guru ins Auto packen und statt
guruInstance.getWerkstatt(meinAuto).repariere(meinAuto);
einfach
meinAuto.lassDichReparieren()
public abstract class OpelAuto extends F
public final void lassDichReparieren() {
new OpelWerkstatt().repariere(this);
}
public abstract class MercedesAuto extends F
public final void lassDichReparieren() {
new MercedesWerkstatt().repariere(this);
}
Wenn der Guru ansonsten nichts können soll, geht das. Aber evtl. soll er
ja aus den Werkstätten auswählen und z.B. die nächste freie, billigste,
etc. liefern. Dann müsstest Du den Guru als an die lassDichReparieren-
Methode des Autos übergeben. Es ist m.E. sinnvoller, das Wissen über die
Werkstätten beim Guru zu lassen und nicht in den einzelnen Autoklassen zu
verstreuen. Damit hältst Du Dir nämlich die Möglichkeit offen, die
Werkstätten zu ändern, ohne die Autos anfassen zu müssen.
Dann kannst Du den Guru allerdings auch direkt um Hilfe bitten. Der sieht
sich Dein Auto an, steckt es in die richtige Werkstatt und gibts Dir
zurück (möglichst repariert - kann aber auch entscheiden, dass es kaputt
bleibt, weil keine Werkstatt frei ist oder die Werkstatt sagt das es sich
nicht mehr lohnt..).

Gruß Jürgen
Tor-Einar Jarnbjo
2008-05-16 17:56:31 UTC
Permalink
Post by Achim Peters
Nun hat die abstrakte Klasse W eine abstrakte Methode
repariere(F fahrzeug). Der Fzg.besitzer kann also auf die vom Guru
Meiner Meinung nach ist dein Fehler hier. Entweder sollte jede Werkstatt
jedes Fahrzeug reparieren können, oder die abstrakte Oberklasse sollte
ein definiertes Verhalten nahelegen, falls die konkrete Werkstatt ein
bestimmtes Fahrzeug nicht reparieren kann. Eine Möglichkeit wäre eine
IllegalArgumentException zu werfen, oder falls du die Prüfung "enger"
machen willst, könntest du ja auch eine eigene checked Exception wie
UnbekanntesFahrzeugException einführen.

Die Lösung mit Generics geht in die richtige Richtung, macht es aber
nicht möglich, dass "AudiWerkstatt extends Werkstatt<AudiFahrzeug>" auch
vielleicht VolkswagenFahrzeug hätte reparieren können.

Gruß, Tor
Christoph Herrmann
2008-05-16 18:22:57 UTC
Permalink
Post by Tor-Einar Jarnbjo
Die Lösung mit Generics geht in die richtige Richtung, macht es aber
nicht möglich, dass "AudiWerkstatt extends Werkstatt<AudiFahrzeug>" auch
vielleicht VolkswagenFahrzeug hätte reparieren können.
Doch, wenn VolkswagenFahrzeug eine Unterklasse von AudiFahrzeug ist
*ironisch gemeint*. Die Hersteller würden sich über diese Art von
Beziehung freuen. :)

Mir ist nicht ganz klar, warum die Automarke eine spezialisierung eines
Autos darstellt. Ist diese nicht eher eine Eigenschaft und somit die
Sache mit der Vererbung unangebracht? Wäre es eine Eigenschaft, könnte
man die Werkstätten anhand bestimmter Kriterien abfragen (Marke
reparierbar, niedrige geographische Entfernung, geringer Preis etc.) und
somit durch eine Suchfunktion die passendste heraus suchen.

Oder hat die Vererbung bei dir hier ganz klar einen Sinn?
--
Mit freundlichen Grüßen,
Christoph Herrmann

http://dragonprojects.de/
Wanja Gayk
2008-05-17 17:54:12 UTC
Permalink
Achim Peters said...

Hallo,
Post by Achim Peters
Die Klasse W habe ich jetzt als abstrakte Oberklasse aller
Reparaturwerkstättentypen definiert (weil der Guru einfach eine
zuständige "Werkstatt" liefert) und die Klasse F als abstrakte
Oberklasse aller Fahrzeugmarken definiert (weil eine Werkstatt allgemein
ein "F(ahrzeug)" repariert, Z. B.
public abstract class F ...
public abstract class OpelAuto extends F ...
public abstract class MercedesAuto extends F ...
public Manta extends OpelAuto ...
public abstract class W ...
public class OpelWerkstatt extends W ...
public class MercedesWerkstatt extends W ...
Ich finde Vererbung hier nicht besonders angebracht, solange es keine
wirklich wichtigfenUnterschiede zwischen den Typen gibt.

Wie wäre es mit etwas in dieser Art?
(Java 1.4 ohne enums oder Generics, sagtest du ja bereits):

//in Java 5 wäre hier eine enum
public final class Hersteller{

public static final Hersteller OPEL = new Hersteller("Opel");
public static final Hersteller CHEVROLET = new Hersteller("Chevrolet");
public static final Hersteller FORD = new Hersteller("Ford");
public static final Hersteller MERCEDES = new Hersteller("Mercedes");
public static final Hersteller HARLEY=new Hersteller("HarleyDavidson");


private String name;
private Hersteller(final String name){
this.name=name;
}
public String toString(){return name;}
}

public class Fahrzeug{
private final Hersteller hersteller;
private final String name;
public Fahrzeug(final Hersteller hersteller, final String name){
if(hersteller==null){throw new IllegalArumenmtException("null");}
this.hersteller=hersteller;
}
public boolean herstellerGehoertZu(final Collection hersteller){
return hersteller.contains(this.hersteller);
}
public String getName(){return name;}
public String toString(){return hersteller+" "+name;}
}

public class Werkstatt{
private final Set eigeneHersteller = new HashSet();
public Werkstatt(final Collection hersteller){
eigeneHersteller.addAll(hersteller);
}
public boolean akzeptiert(Fahrzeug fahrzeug){
return fahrzeug.herstellerGehoertZu(eigeneHersteller);
}

public void repariere(final Fahrzeug fahrzeug){
if(!akzeptiert(fahrzeug)){
throw new ServiceException("Falscher Hersteller",
fahrzeug, werkstatt);
}
//... reparatur ...
}
}

pubic class ServiceException extends Exception{
private final Werkstatt werkstatt;
private final Fahrzeug fahrzeug;
public HerstellerException (final String nachricht;
final Fahrzeug fahrzeug,
final Werkstatt werkstatt){
super(nachricht); //wer sagt, dass Code nicht ironisch sein kann?
this.fahrzeug=fahrzeug;
this.werkstatt=werkstatt;
}
public Fahrzeug getFahrzeug(){
return fahrzeug;
}
public Werkstatt getWerkstatt(){
return werkstatt;
}
}


Zu benutzen wäre es etwa so:

final Fahrzeug fahrzeug = new Fahrzeug(Hersteller.OPEL, "Vectra");

final Werkstatt harleyWerkstatt = new Werkstatt(
Arrays.asList(
new Hersteller[]{Hersteller.HARLEY}
)
);
final Werkstatt opelWerkstatt = new Werkstatt(
Arrays.asList(
new Hersteller[]{Hersteller.OPEL, Hersteller.CHEVROLET}
)
);


//....
try{
harleyWerkstatt.repariere(fahrzeug);
catch(ServiceException e){
anrufKunde("Fahr deine Karre vom Hof, wir reparieren nur Harleys!!");
}
try{
opelWerkstatt.repariere(fahrzeug);
catch(ServiceException e){
anrufKunde("Alder, das isn Opel Club und deine Mudder zieht LKW auf
DSF! Fahr mit deine Karre woanders hin, du Experde!");
}


Wäre das ein Design, welches dir passen könnte?

Gruß,
-Wanja-
--
Klingon function calls do not have 'parameters', they have 'arguments' -
and they always win them.
[Nele Abels in dsg]
Achim Peters
2008-05-17 18:28:39 UTC
Permalink
Post by Wanja Gayk
Achim Peters said...
Post by Achim Peters
Die Klasse W habe ich jetzt als abstrakte Oberklasse aller
Reparaturwerkstättentypen definiert (weil der Guru einfach eine
zuständige "Werkstatt" liefert) und die Klasse F als abstrakte
Oberklasse aller Fahrzeugmarken definiert (weil eine Werkstatt allgemein
ein "F(ahrzeug)" repariert, Z. B.
Wie wäre es mit etwas in dieser Art?
public class Werkstatt{
public void repariere(final Fahrzeug fahrzeug){
if(!akzeptiert(fahrzeug)){
throw new ServiceException("Falscher Hersteller",
fahrzeug, werkstatt);
}
Die Abfrage habe ich auch und die wollte ich mir sparen. Ich wollte,
falls möglich, dass der Aufrufer gar keine falsche Werkstatt aufrufen
*kann*.
Post by Wanja Gayk
super(nachricht); //wer sagt, dass Code nicht ironisch sein kann?
super(witz); ;-))
Post by Wanja Gayk
Wäre das ein Design, welches dir passen könnte?
Leider nicht.

Trotzdem Danke!

Bye
Achim
Wanja Gayk
2008-05-17 20:35:25 UTC
Permalink
Achim Peters said...
Post by Achim Peters
Post by Wanja Gayk
public class Werkstatt{
public void repariere(final Fahrzeug fahrzeug){
if(!akzeptiert(fahrzeug)){
throw new ServiceException("Falscher Hersteller",
fahrzeug, werkstatt);
}
Die Abfrage habe ich auch und die wollte ich mir sparen. Ich wollte,
falls möglich, dass der Aufrufer gar keine falsche Werkstatt aufrufen
*kann*.
Ich halte es für keine gute Lösung, wenn der Compiler das checkt, weil
dieser Check statisch ist. Nimm z.B. den Fall, dass eine Werkstatt ihr
Angebot erweitert oder verändert - sowas möchte man doch im günstigsten
Fall dynamisch haben, ohne das Programm anfassen zu müssen, oder?

Nunja, um die die Abfrage oben zu sparen, gäbe es da noch eine
Möglichkeit einen dynamischen Dispatch per Reflection zu machen, das
sähe dann etwa so aus:

//deine Werkstatt:
public abstract class AbstractNumberManager{
public void manage(final Number n){
try{
final Method m=getClass().getMethod("manage", n.getClass());
m.invoke(this, n);
}catch(SecurityException e){
e.printStackTrace();
}catch(IllegalAccessException e){
e.printStackTrace();
}catch(InvocationTargetException e){
e.printStackTrace();
}catch(NoSuchMethodException e){
e.printStackTrace();
}
}
}

//die Fachwerkstätten
public class DoubleManager extends AbstractNumberManager{
public void manage(final Double n){
System.out.println("Double!");
}
}

public class IntegerManager extends AbstractNumberManager{
public void manage(final Integer n){
System.out.println("Integer!");
}
}

public class FallbackManager extends AbstractNumberManager{
public void manage(final Number n){
System.out.println("Fallback!");
}
}

//das Werkstattverzeichnis
public class ManagerFactory{
private final Map managers = new HashMap();

public ManagerFactory(){
managers.put(Double.class, new DoubleManager());
managers.put(Integer.class, new IntegerManager());
managers.put(FallbackManager.class, new IntegerManager());
}

public AbstractNumberManager managerfor(final Number n){
final AbstractNumberManager manager
=(AbstractNumberManager)managers.get(n.getClass());
if(manager==null){
manager=(AbstractNumberManager)managers.get(FallbackManager.class);
}
return manager;
}
}


//der Benutzer:
public class Test{

public static void main(String[] args){
Number number = new Double(10.0);
new ManagerFactory.managerFor(number).manage(number);
number = new Integer(5);
new ManagerFactory.managerFor(number).manage(number);
number = new Byte((byte)2);
new ManagerFactory.managerFor(number).manage(number);
}
}

Im Prinzip sieht es recht elegant aus.

Das Schreiben der speziellen Unterklassen erinnert ein wenig an das
Schreiben von JUnit-Tests - der Compiler kann die Namenskonvention, bzw.
die Methodensignatur-Konvention nicht prüfen und so wird die
Implementation einer Methode für einen speziellen Typen nicht vom
Compiler erzwungen, sondern in dem Fall, dass man es vergeigt, kommt die
Exception erst zur Laufzeit.
Deine Factory, also das Werkstattverzeichnis, kann man gff auch
dynamisieren, so dass du mit dieser Lösung schon eine halbe Plugin-
Architektur hast.

Nur so, um eine weitere Idee in den Raum zu werfen.
Vielleicht gefällt dir das ja besser als mein erster vorschlag.
Post by Achim Peters
Trotzdem Danke!
Gerne.

Gruß,
-Wanja-
--
Klingon function calls do not have 'parameters', they have 'arguments' -
and they always win them.
[Nele Abels in dsg]
Patrick Roemer
2008-05-18 00:12:26 UTC
Permalink
Post by Wanja Gayk
Post by Achim Peters
Post by Wanja Gayk
public void repariere(final Fahrzeug fahrzeug){
if(!akzeptiert(fahrzeug)){
throw new ServiceException("Falscher Hersteller",
fahrzeug, werkstatt);
}
Die Abfrage habe ich auch und die wollte ich mir sparen. Ich wollte,
falls möglich, dass der Aufrufer gar keine falsche Werkstatt aufrufen
*kann*.
Ich halte es für keine gute Lösung, wenn der Compiler das checkt, weil
dieser Check statisch ist. Nimm z.B. den Fall, dass eine Werkstatt ihr
Angebot erweitert oder verändert - sowas möchte man doch im günstigsten
Fall dynamisch haben, ohne das Programm anfassen zu müssen, oder?
Nach den bisherigen Beschreibungen wuerde ich erwarten, dass eine
hypothetische Werkstatt, die Mercedes und Opel akzeptiert, auch zwei
unterschiedliche Wege anbietet, die Formate dieser Hersteller
einzuliefern, aber mit allen anderen Fabrikaten und Formaten nichts
anfangen kann. Die Verantwortlichkeit wuerde sich dann nur von der
Werkstatt in eine andere, mit der Werkstatt 1..N verbundene, Abstraktion
verschieben, die Problematik bliebe dieselbe.

Viele Gruesse,
Patrick
Achim Peters
2008-05-19 12:06:53 UTC
Permalink
Post by Wanja Gayk
Achim Peters said...
Post by Achim Peters
Post by Wanja Gayk
public class Werkstatt{
public void repariere(final Fahrzeug fahrzeug){
if(!akzeptiert(fahrzeug)){
throw new ServiceException("Falscher Hersteller",
fahrzeug, werkstatt);
}
Die Abfrage habe ich auch und die wollte ich mir sparen. Ich wollte,
falls möglich, dass der Aufrufer gar keine falsche Werkstatt aufrufen
*kann*.
Ich halte es für keine gute Lösung, wenn der Compiler das checkt, weil
dieser Check statisch ist. Nimm z.B. den Fall, dass eine Werkstatt ihr
Angebot erweitert oder verändert - sowas möchte man doch im günstigsten
Fall dynamisch haben, ohne das Programm anfassen zu müssen, oder?
Mein Unterschied zwischen "Mercedes" und "Opel" ist größer als zwei
Automarken es vermuten lassen.
Post by Wanja Gayk
Nunja, um die die Abfrage oben zu sparen, gäbe es da noch eine
Möglichkeit einen dynamischen Dispatch per Reflection zu machen,
Hallo? Ich schrieb "elegant" ... ;-))) Wenn sich eine Idee nur per
Reflection machen lässt, halte ich im ersten Ansatz immer die Idee für
schlecht und das Design (meins) für überarbeitungswürdig. ;-)

Bye
Achim
Wanja Gayk
2008-05-22 10:06:34 UTC
Permalink
Achim Peters said...
Post by Achim Peters
Post by Wanja Gayk
Nunja, um die die Abfrage oben zu sparen, gäbe es da noch eine
Möglichkeit einen dynamischen Dispatch per Reflection zu machen,
Hallo? Ich schrieb "elegant" ... ;-))) Wenn sich eine Idee nur per
Reflection machen lässt, halte ich im ersten Ansatz immer die Idee für
schlecht und das Design (meins) für überarbeitungswürdig. ;-)
Die Reflection-Lösung finde ich nicht unelegant!
Die ist schon fast sowas wie ein Plugin-Mechanismus, ziemlich mächtig
und einfach gleichermaßen. Sie krankt nur daran, dass der Compiler
deinen Fehler nicht checken kann. Das ist allerdings nicht wirklich
schlimm, wenn deine Testsuite das checkt.

Das Problem liegt im Prinzip deiner Anforderung: Die Factory
generalisiert den Typ und die verlorene Information kannst du nicht
einfach wieder zurück gewinnen.

Noch ein paar Ansätze:

public abstract class AbstractNumberManager{
private static final String ERR = "Typ nicht unterstützt.";

public void manage(final Number number){
if(number instanceof Double){
manage((Double)number);
}else if(number instanceof Integer){
manage((Integer)number);
}
}

public void manage(Integer i){throw new SupportException(ERR);}
public void manage(Double d){throw new SupportException(ERR);}

}

public class DoubleManager extends AbstractNumberManager{
protected void manage(final Double d){
System.out.println("Double!");
//...
}
}


public class IntegerManager extends AbstractNumberManager{
protected void manageIntern(final Integer i){
System.out.println("Integer!");
//...
}
}

public class FallbackManager extends AbstractNumberManager{
public void manage(final Number n){
System.out.println("Fallback!");
}
}


public class ManagerFactory{
private final Map managers = new HashMap();

public ManagerFactory(){
managers.put(Double.class, new DoubleManager());
managers.put(Integer.class, new IntegerManager());
managers.put(FallbackManager.class, new IntegerManager());
}

public AbstractNumberManager managerfor(final Number n){
final AbstractNumberManager manager
=(AbstractNumberManager)managers.get(n.getClass());
if(manager==null){
manager=(AbstractNumberManager)managers.get(FallbackManager.class);
}
return manager;
}
}

public class Test{

public static void main(String[] args){
Number number = new Double(10.0);
new ManagerFactory.managerFor(number).manage(number);
number = new Integer(5);
new ManagerFactory.managerFor(number).manage(number);
number = new Byte((byte)2);
new ManagerFactory.managerFor(number).manage(number);
}
}


Instanceof-Kaskaden: sehen nicht nur scheiße aus, sie binden die Klasse,
die die Kaskade enthält, an alle Klassen auf die geprüft wird (hohe-fan-
out-complexity) und bringen dir den check erst zur Laufzeit. In diesem
Fall hast du also zwei, statt nur eine Baustelle: Die Factory und den
AbstractNumberManager, die beide sämtliche klassenkenen müssen, die du
jemals behandeln können willst. Dieses Problem hat die Reflection-Lösung
nicht!
Ich denek wenndu einen neuen typen unterstützen willst, ist das, was du
willst, an so wenigen stellen wie möglich eine Änderung vorzunehmen.
Ich finde also die Reflection-Lösung ist von dem Standpunkt aus auf
jeden Fall schöner.


Noch ein Ansatz, um die instanceof-Kaskade zu eliminieren:

public abstract class AbstractNumberManager{
private final Class targetClazz;
public AbstractNumberManager(final Class targetClazz){
if(targetClazz== null){
throw new IllegalArgumentException("argument clazz is null")};
}
this.targetClazz=targetClazz;
}

public final void manage(final Number number){
if(targetClazz.isInstance(number)){
}
}

protected abstract manageIntern(final Number n);
}

public class DoubleManager extends AbstractNumberManager{
public DoubleManager(){
super(Double.class);
}
protected void manageIntern(final Number n){
final Double d = (Double) n;
System.out.println("Double!");
//...
}
}


public class IntegerManager extends AbstractNumberManager{
public DoubleManager(){
super(Integer.class);
}
protected void manageIntern(final Number n){
final Integer i = (Integer) n;
System.out.println("Integer!");
//...
}
}

public class FallbackManager extends AbstractNumberManager{
public DoubleManager(){
super(Number.class);
}
public void manage(final Number n){
System.out.println("Fallback!");
}
protected void manageIntern(Number n){}
}


public class ManagerFactory{
private final Map managers = new HashMap();

public ManagerFactory(){
managers.put(Double.class, new DoubleManager());
managers.put(Integer.class, new IntegerManager());
managers.put(FallbackManager.class, new IntegerManager());
}

public AbstractNumberManager managerfor(final Number n){
final AbstractNumberManager manager
=(AbstractNumberManager)managers.get(n.getClass());
if(manager==null){
manager=(AbstractNumberManager)managers.get(FallbackManager.class);
}
return manager;
}
}

public class Test{

public static void main(String[] args){
Number number = new Double(10.0);
new ManagerFactory.managerFor(number).manage(number);
number = new Integer(5);
new ManagerFactory.managerFor(number).manage(number);
number = new Byte((byte)2);
new ManagerFactory.managerFor(number).manage(number);
}
}

Diese Lösung ist praktisch die Gleiche wie mit Reflection, nur dass der
methodenparameter immer gecastet werden muss und du einen explizten
Aufruf des Superkonstruktors brauchst, den Typ casten musst, und den
Check eben wieder nur zur Laufzeit hast.
Natürlich hast du hier die selbe Abfrage des "bin ich zuständig?", die
du ja nicht haben wolltest (hir ist sie in der der Oberklasse). Von
diesem Standpunkt aus hast du also gar nichts gewonnen und die
Reflection-Lösung ist nach wie vor eleganter.

Es gibt noch eine Lösung:

public SupportException extends RuntimeException{
public SupportException(String message){super(message);}
}

public abstract class NumberManager{
private static final String ERR = "Typ nicht unterstützt.";
public void manage(Number n){throw new SupportException(ERR);}
public void manage(Integer i){throw new SupportException(ERR);}
public void manage(Double d){throw new SupportException(ERR);}
}

public class DoubleManager extends AbstractNumberManager{
public void manage(final Double d){
System.out.println("Double!");
//...
}
}

public class IntegerManager extends AbstractNumberManager{
public void manage(final Integer d){
System.out.println("Double!");
//...
}
}

public class FallbackManager extends AbstractNumberManager{
public void manage(final Number n){
System.out.println("Fallback!");
}
}

public class ManagerFactory{
private final Map managers = new HashMap();

public ManagerFactory(){
managers.put(Double.class, new DoubleManager());
managers.put(Integer.class, new IntegerManager());
managers.put(FallbackManager.class, new IntegerManager());
}

public AbstractNumberManager managerfor(final Number n){
final AbstractNumberManager manager
=(AbstractNumberManager)managers.get(n.getClass());
if(manager==null){
manager=(AbstractNumberManager)managers.get(FallbackManager.class);
}
return manager;
}
}

public class Test{

public static void main(String[] args){
Double d = new Double(10.0);
new ManagerFactory.managerFor(d).manage(i);
Double i = new Integet(10.0);
new ManagerFactory.managerFor(number).manage(i);
Byte b = new Byte((byte)2);
new ManagerFactory.managerFor(number).manage(b);
}
}

Auf den ersten Blick recht sexy, oder?
Aber auch das hat Probleme:
1. Der AbstractNumberManager muss jeden Typen kennen, der jemals
verwendet werden könnte. Das muss deine Factory auch, du hast also zwei
Baustellen mit hoher fan-out-complexity, statt nur einer.
2. Wie du hier am Test unschwer erkennen kannst, darfst du, im Gegensatz
zu den früheren Lösungen, die Typinformation der Zahl nicht verlieren,
sonst rennst du immer in eine Exception.
Auch das ist nicht schön und auch hier würde ich die Reflection-Lösung
vorziehen.

Ich befürchte ohne Generics wirst du nicht an so einem Mechanismus
vorbei kommen, der entweder checkt "bin ich zuständig?" oder der
irgendeine Namenskonvention fordert, oder an einer hohen fan-out-
complexity krankt. Eben deswegen, weil einerseits deine Factory den Typ
für den Compiler auslöscht, andererseits entweder eine Methode über die
du einheitlich zugreifen willst, oder der typ den du der Methode
übergibst.

Generics sind im Prinzip wie die Lösung, die eine einheitliche Signatur
hat und stumpfe Typcasts macht, nur dass dir die Generics das Casten
abnehmen und der Compiler ein wenig mehr (aber nicht alles) an
typinformation checken kann - und auch die Generics schützen dich nicht
vor einer ClassCastException, wenn du in der Factory Unsinn baust.

Gruß,
-Wanja-
--
Klingon function calls do not have 'parameters', they have 'arguments' -
and they always win them.
[Nele Abels in dsg]
Wanja Gayk
2008-05-22 18:51:39 UTC
Permalink
Wanja Gayk said...
Post by Wanja Gayk
public abstract class AbstractNumberManager{
private final Class targetClazz;
public AbstractNumberManager(final Class targetClazz){
if(targetClazz== null){
throw new IllegalArgumentException("argument clazz is null")};
}
this.targetClazz=targetClazz;
}
public final void manage(final Number number){
if(targetClazz.isInstance(number)){
manageIntern(number); <------- sollte da schon stehen ;-)
Post by Wanja Gayk
}
}
protected abstract manageIntern(final Number n);
}
Gruß,
-Wanja-
--
Klingon function calls do not have 'parameters', they have 'arguments' -
and they always win them.
[Nele Abels in dsg]
Achim Peters
2008-05-24 11:26:19 UTC
Permalink
Post by Wanja Gayk
Das Problem liegt im Prinzip deiner Anforderung: Die Factory
generalisiert den Typ und die verlorene Information kannst du nicht
einfach wieder zurück gewinnen.
Danke für die viele Mühe!
Post by Wanja Gayk
Ich befürchte ohne Generics wirst du nicht an so einem Mechanismus
vorbei kommen, der entweder checkt "bin ich zuständig?" oder der
irgendeine Namenskonvention fordert, oder an einer hohen fan-out-
complexity krankt. Eben deswegen, weil einerseits deine Factory den Typ
für den Compiler auslöscht, andererseits entweder eine Methode über die
du einheitlich zugreifen willst, oder der typ den du der Methode
übergibst.
Generics sind im Prinzip wie die Lösung, die eine einheitliche Signatur
hat und stumpfe Typcasts macht, nur dass dir die Generics das Casten
abnehmen und der Compiler ein wenig mehr (aber nicht alles) an
typinformation checken kann - und auch die Generics schützen dich nicht
vor einer ClassCastException, wenn du in der Factory Unsinn baust.
Yep.

Bye
Achim
Ivan Dolvich
2008-05-19 22:37:36 UTC
Permalink
Hier brauchst IMHO du die abstrakte Fabrik (Abstract Factory). Deine
Familien von Objekten unterscheinden sich anhand der Marke - die
OpelFabrik gibt dir OpelFahrzeuge und OpelWerkstatt, die AudiFabrik gibt
dir AudiFahrzeug und AudiWerkstatt. Du als Klient kennst aber die Fabrik
nur unter ihr Interface FahrzeugFabrik, von dem die anderen konkreten
Fabriken ableiten.

Viele Grüße
Achim Peters
2008-05-20 05:46:28 UTC
Permalink
Post by Ivan Dolvich
Hier brauchst IMHO du die abstrakte Fabrik (Abstract Factory).
Auch ein schöner Ansatz. Danke! Passt aber nicht ganz, weil mein Klient
genau ein konkretes Auto bereits hat, aber die Marke nicht kennt.

Bye
Achim
Ivan Dolvich
2008-05-21 23:13:47 UTC
Permalink
Post by Achim Peters
Auch ein schöner Ansatz. Danke! Passt aber nicht ganz, weil mein Klient
genau ein konkretes Auto bereits hat, aber die Marke nicht kennt.
Die konkrete Fabrik kennt doch die Marke ... der Klient sollte diese
Fabrik benutzen um die Auto-Instanzen und danach die richtigen
Werkstatt-Instanzen zu erhalten.

PS: die Marke könnte eine zusätzliche Klasse sein, dadurch kannst du die
Auto-Varianten durch Aggregation statt durch Vererbung realisieren.

Grüße, Ivan
Achim Peters
2008-05-24 11:27:24 UTC
Permalink
Post by Ivan Dolvich
PS: die Marke könnte eine zusätzliche Klasse sein, dadurch kannst du die
Auto-Varianten durch Aggregation statt durch Vererbung realisieren.
Ok, Danke!

Bye
Achim

Lesen Sie weiter auf narkive:
Loading...