Verschiedene Objekte in Abhängigkeit eines Typs instanziieren.

Bober

Mitglied
Hallo,


ich hab folgendes Konstrukt geschirben:
-Interface Calculation
-Abstrakte Klasse AbstractCalculation implements Calculation
- SubKlassen Calculation1, Calculation2,...

Nun habe ich in einer Methode ein Objekt vom Typ "Calculation", wobei ich in Abhängigkeit einer Int-Variable "typ" die Konstruktoren der SubKlassen aufrufen möchte.

Ist bisher über eine switch-Anweiseung gelöst:

Java:
		switch (typ) {

		case 1:
			calculation = new Calculation1();
			break;
		case 2:
			calculation = new Calculation2();
			break;
		default:
			System.err.println("now model or calculation with type number "
					+ typ + " was found");
			return;
}

Geht das auch einfacher bzw. schöner ohne Beschränkung der Anzahl durch die switch Anweisung. (muss immer erweitert werden, wenn eine neue Berechnung dazu kommt.)

Gruß,

Bober
 
Könntest du nähere infos zu den Klassen / interfaces geben? Ich finde das Konstrukt Abstrakte Klasse Implements interface iwie komisch?! Weil entwerder nutzt man eigentlich ein Interface oder eine abstrakte Klasse?!
 
Zuletzt bearbeitet:
Auf Basis des bisher gesagten, der Brot-und-Butter-OO-Vorschlag...
Java:
interface CalculationFactory
{
    Calculation create();
}

class CalculationFactoryOne implements CalculationFactory 
{
    @Override
    public Calculation create() { return new Calculation1(); }
}

void yourCode(CalculationFactory insteadOfSwitch)
{
    Calculation c = insteadOfSwitch.create();
}
 
und wie wird die Factory ausgewählt? 😉

wenn man nur einen Typ hat, ist die Alternative zu einem switch das Nachschauen in einer Map
map.get(typ).create()
wenn z.B. die zuvor genannte Factory jeweils eingetragen ist, das können die einzelnen Klassen machen,
falls sie dran kommen und Zugriff auf die Map haben
 
Ja, war nur ein first shot. Vermutlich wird jemand parallel zu diesem Beitrag den ich gerad schreiben auch vorschlagen, ein Dependency Injection Framework zu verwenden 😀
 
Also hier einige weitere Infos:

Ich möchte mit dem Konstrukt ermöglichen, dass weitere Calculations entweder durch erben der Abstrakten Klasse oder durch implementieren des Interfaces hinzugefügt werden können.


Ich will also in einer freie Sprache formuliert folgendes realisieren:

Calculation c;

c = new Calculation*();

Wobei für * der wert der variable typ eingesetzt werden soll, also den Klassennamen ändert.
 
Wäre so z.B. möglich:
[c]Class.forName("mein.pakat.Calculation" + meineVariable).newInstance()[/c]

Wobei ich mir eher eine Factory bauen würde.
 
Keine Ahung ob ich den TS richtig verstehe, aber ohne DI würde ich das wohl mit dem Strategy Patttern als Enum umsetzen:
Java:
public enum Calculate{
	ADD {
		public double calculate(double first, double second) {
			return first + second;
		}
	},
	MULTIPLY {
		public double calculate(double first, double second) {
			return first + second;
		}		
	},
..	
	public abstract double calculate(double first, double second);
}

...

Calculate calc = Calculate.ADD;
double result = calc.calculate(1,2);
 
Ok, das mit der Factory habe ich wohl verstanden. jedoch lagert das doch bei mir lediglich das Problem in eine andere Klasse aus, bei der ich dann wiederum mittels switch oder if anweisungen die einzelnen Klassennamen einem Typ zuweisen muss, oder habe ich da was übersehen?
 
Du könntest es in komination mit marcos und slaters vorschlag machen (was ähnlichkeiten zu meinem Class.forName hat):
Java:
public class CalculatorFactory {
     private static Map<String,Calculator> operations=new HashMap<>();

     public static Calculator getInstance(String identifier) {
           return operations.get(identifier);
     }

     public static boolean registerOperation(String identifier, Calculator calc) {
          if(operations.containsKey(identifier)) return false; // Überschreiben verhindern
          operations.put(identifier,calc);
          return true;
     }
}

Fehler im Code sind möglich, da ichs gerade im Forum runtergetippt habe und nicht in einer IDE. Aber so in etwa hast du eine flexible Factory.

Ach ja:
Abrufen könntest du es dann so:
Java:
Calculator addition=CalculatorFactory.getInstance("add");
 
Zuletzt bearbeitet von einem Moderator:
Ich verstehe nicht soganz warum du den Klassennamen dynamisch haben willst?! Anscheinend machen die Objekte alle das gleiche?! Wäre ist nich einfacher ein Calcualionobjekt zu machen demm du ne Option übergibst was du berechnen willst?!
 
Die Objekte machen alle komplett unterschiedliche Berechnungen, sie bekommen lediglich alle die Parameter als String Array und setzen die Ergebnisse in einem String Array. Berechnungen werden durch die Methode "calculate()", die das Interface vorschreibt, durchgeführt.


Zurück zum Thema:


Was ist eigentlich an der "Class.forName("mein.pakat.Calculation" + meineVariable).newInstance()"
Methode schlecht, bzw was an der Map bzw der Factory besser?
 
Was ist eigentlich an der "Class.forName("mein.pakat.Calculation" + meineVariable).newInstance()"
Methode schlecht, bzw was an der Map bzw der Factory besser?
Die genannte Factory-Lösung bringt schon einige Vorteile:
  1. Typsicherheit: Die Factory gibt dir bereits Calculator-Instanzen zurück. Class.newInstance() hingegen nur ein Object, welches ein Calculator oder aber mit etwas Pech auch was ganz anderes sein kann.
  2. Fehlerhandling: Die Behandlung und eventuelle Weitergabe/Generierung von Fehlern lässt sich in der Factory schön festlegen, während du bei Class.newInstance() das jedes mal im aufrufenden Code machen musst, und du an der Art und Weise, wie sich Class.newInstance() verhält, auch nicht mehr viel ändern kannst.
  3. Durchsetzung der Singleton-Semantik: Die Factory gibt dir für eine ID jedes mal die gleiche Instanz zurück. Mit Class.newInstance() hingegen erzeugst du jedes mal eine neue Instanz, obwohl das bei solchen zustandslosen Objekten gar nicht notwendig ist.
  4. Erweiterbare Registrierung: Die Factory dient auch als Registry für Calculator-Instanzen. Das heißt, dass du sie z.B. einfach fragen könntest, welche Calculator überhaupt existieren.
  5. Frei wählbare IDs: Class.forName() übergibst du einen vollqualifizierten Klassennamen. Damit bist du sehr abhängig von Implementierungsdetails wie Packages und Klassennamen. Bei der Factory-Lösung können die IDs hingegen frei wählbar sein. Sie lassen sich somit viel besser menschenlesbar und merkbar gestalten und verlieren nicht gleich beim nächsten Refactoring ihre Gültigkeit.
All das macht die Factory um einiges einfacher und angenehmer zu benutzen.
 
Zuletzt bearbeitet von einem Moderator:
@Tomate_Salat (und teilweise dhalsim) : Ich finde, durch diese Map wird da einiges "kauptt" gemacht. Es ist dann keine Factory mehr, sondern einfach eine statisch-globale Map, und das ist IMHO erstmal unschön. Vielleicht ist "sowas" hier passend, dazu müßte man das Problem genauer kennen, aber ... man sollte das NICHT mehr "Factory" nennen :noe:
 
Danke für den Hinweis, du hast natürlich recht. Wenn man sich das Beispiel noch mal in Ruhe anschaut, ist diese vermeintliche Factory von außen auch nichts weiter als eine Map. Man braucht dazu bloß die Methodennamen ersetzen
  • getInstance(id) durch get(id)
  • registerOperation(id, calculator) durch put(id, calculator)
und schon sieht man das deutlich. Ich würde das Ganze daher eher als "Registry" bezeichnen.

Was man allerdings machen kann ist das Ganze wiefolgt zu ändern (zur Vereinfachung mal nicht-statisch als Interface):
Java:
interface CalculatorRegistry {

    void register(String id, Class<? extends Calculator> clazz);

    Calculator getCalculator(String id);

}
Dann hätte das tatsächlich einen Factory-Anteil, da sich auch um die Erzeugung gekümmert wird.
 
Hmja, und wenn man das noch ein bißchen weiter ausbaut, ist man wirklich bei einem Dependency-Injection-Framework 😉 Irgendwo muss die Entscheidung getroffen werden: Beim ersten dort, wo der int 'typ' einen Wert erhielt, bei der Factor dort, wo die Factory instantiiert wird, bei den Registry-Varianten dort, wo die passende Value zum passenden Key reingelegt wird, und bei DI auf noch höherer oder abstrakterer Ebene - aber zumindest sieht man, dass es viele Möglichkeiten gibt, das nach 1980 aussehende unflexible "switch" wegzubekommen 😉
 

Neue Themen


Zurück
Oben