Sinnvolles Software Design bei Eigenschaftsänderungen von Objekten

AndiMb

Mitglied
Hallo,

ich habe eine eher allgemeine Software Design Frage zum Eventhandling, hoffe aber trotzdem, dass mir jemand helfen kann.

Ich versuche mal das Problem allgemeinverständlich an einem Auto zu beschreiben. In meinen Fall wäre das Auto eine einfache Klasse, die mehrere Objekte der Klasse Bauteil enthält (Karosserie, Motor, usw.), welches wiederum mindestens ein Objekt der Klasse Material enthält. Für jede der drei Klassen kann der Preis bestimmt werden. Beim Material ist dieser einfach durch den Nutzer gesetzt, bei den anderen beiden Klassen (Auto, Bauteil) muss er aus den enthaltenen Objekten berechnet werden. Da der jeweilige Preis in einer GUI angezeigt werden soll, werden entsprechende Events bei Veränderungen verwendet und ausgelöst. Wenn man nun den Preis des Materials ändert, werden alle Bauteile darüber informert und berechnen den Preis neu. Dies wird an das Auto weitergereicht, was bedeutet, dass auch für das Auto der Preis neu berechnet wird. Allerdings wird das Auto mehrfach darüber informiert (sooft, wie es Bauteile aus dem veränderten Material enthält). Wenn nun wie in meinem Fall die Berechnung des Preises sehr aufwendig ist, wäre es wünschenswert, dass das Autoobjekt dies nur ganz am Ende macht, da die Zwischenwerte auch keinerlei Relevanz besitzen

Hat einer eine Idee wie man sowas sinnvoll realisieren kann.

Viele Grüße und vielen Dank im Voraus
Andreas
 
Du wickelst das alles von hinten ab.
Also du informierst das Auto über die Veränderung,
dann fordert das Auto seine Teile auf ihren Preis neu zu
berechnen und ihn dann zurückzugeben.
 
Hmja, schwierig. Ein spontaner Gedanken (von

... Hier habe ich nochmal runtergescrollt: Ich wollte genau das schreiben, was kaetzacoatl geschrieben hat 😀 Aber wollte nachsehen, ob es vielleicht notwendig ist, dass die einzelnen Elemente auch Events werfen, weil sie quasi "unabhängig vom Auto" in GUI-Components angezeigt werden... Ist das der Fall? Vielleicht könnte man das trotzdem auf die von kaetzacoatl beschriebene Art machen, aber müßte sich dann überlegen, ob die Elemente vielleicht Events werfen, auf die zwar die GUI-Components hören, aber das Auto nicht...
 
Erstmal danke für die schnelle Antwort.

An sowas hatte ich auch schon gedacht. Nur wie erfährt dann das Bauteil, dass es den Preis wirklich neu berechnen muss. Ich möchte das natürlich nicht machen, wenn sich bei einem Bauteil mit einem anderen Material der Preis gar nicht geändert hat, denn auch für das Bauteil ist die Berechnung aufwendig. Zudem besteht das ursprüngliche Problem aus mehr als drei Hierarchieebenen, wobei die Anzahl eventuell sogar unerschiedlich sein kann.

In meinem Kopf sieht das Konzept so aus, dass man einmal die Hierarchie von unten her (Material) durchwandern muss, wobei jedes nur Objekt sich nur merkt, dass sich bei ihm was geändert hat. Anschließend muss dann die Hierarchie nochmal von oben her durchwandert werden. Aber wie ich das "von oben" nochmal auslöse ist mir noch nicht klar. Für eine statische Hierarchie mag das gehen. Aber es ist möglich, dass das Material dann wiederum aus verschiebenen Legierungselementen besteht und die Bauteile erst zu Baugruppen zusammengefasst werden, bevor sie an das Auto kommen.

Viele Grüße
Andreas
 
Warum sollten Bauteile Preise berechnen?
Wurde sowas nicht mischen.

Zum traversieren von Bäumen kann man zb. in Post Order vorgehen.
 
Ich dachte an sowas:
Java:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

public class Auto {
	
	static Auto auto = new Auto();
	ArrayList<Teil> teile = new ArrayList<Teil>();
	
	public void update(Material m){
		for(int i = 0;i < teile.size();i++){
			if(teile.get(i).needUpdate(m))teile.get(i).update(m);
		}
	}

}
class Material implements ActionListener {

	@Override
	public void actionPerformed(ActionEvent arg0) {
		Auto.auto.update(this);		
	}	
	
}
interface Teil{

	boolean needUpdate(Material m);

	void update(Material m);
	
}
class BSP implements Teil{
	
	ArrayList<Teil> teile = new ArrayList<Teil>();

	@Override
	public boolean needUpdate(Material m) {
		for(int i = 0;i < teile.size();i++){
			if(teile.get(i).needUpdate(m))return true;
		}
		return false;
	}

	@Override
	public void update(Material m) {
		for(int i = 0;i < teile.size();i++){
			if(teile.get(i).needUpdate(m))teile.get(i).update(m);
		}		
	}
	
}
 
Geht alles mit dem Observerpattern auf ein Klasse "Preis" angewendet, wobei in diesem Fall nur Materialien ihren Preis haben. Problem dabei: verschiedene Materialien haben verschiedene Preise, Materialien gleichen Typs haben gleiche Preise. Für die Produktion von Materialien eignet sich also eine Factory (wer 'hätts gedacht? XD).
Dann geht man wie folgt vor:
Ändert man den Preis eines Materials, sollten sich auch gleichzeitig die Preise der Materialien gleichen Typs ändern. Materialien gleichen Typs erhalten also ein und dieselbe Instanz der Klasse Preis (per Factory geht das, fragt mal in der Kneipe 😳 Wirtschaft nach XD). Wird ein Material einem Bauteil hinzugefügt, registriert sich das Bauteil beim Preis des Materials als Observer und addiert den Wert zu seiner eigenen Preisinstanz hinzu (dieser würde dann logischerweise sofort auch übergeordnete Bauteile über sene Änderung informieren), beim Entfernen des Materials meldet sich das Bauteil bei dem Preis wieder ab und zieht den Wert von der eigenen Instanz entsprechend ab (übergeordnete Bauteile werden auch darüber informiert).
Wie aber publiziert man nun Preisänderungen? Recht einfach. Eine Änderung hat immer etwas mit einer Vorher- Nachherdifferenz zu tun. Für die Klasse Preis würde das bedeuten, dass sie ihre registrierten Bauteile auch genau nur darüber informiert. Kein grosses gerechne in den übergeordneten Objekten und alles in allem extrem performant. Wenn ich heut noch Zeit hab' setz' ich mich mal an ein KSKB.
 
Also für die Factory hat die Zeit jetzt nicht gereicht, das wird recht aufwändig. Dabei sagt man der Factory eigentlich nur, was benötigt wird und bekommt dann eine Instanz, die ihren Preis aus einer Map bekommt, das hat also weniger mit der Problematik des Themas zu tun.
Für den Rest hab' ich das mal gebaut:
Java:
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

interface Observer {
	void update(double diff);
}

class Price {
	private final Collection<Observer> observer = new HashSet<>();
	private double value;

	public Price() {
	}

	public Price(Price price) {
		value = price.value;
	}

	public void setValue(double newValue) {
		double diff = this.value - newValue;
		this.value = newValue;
		for(Observer obs : observer) {
			obs.update(diff);
		}
	}

	public double getValue() {
		return value;
	}

	public void add(double value) {
		this.value += value;
		for(Observer obs : observer) {
			obs.update(value);
		}
	}

	public void addObserver(Observer obs) {
		for(Observer o : observer) {
			if(o == obs) {
				return;
			}
		}
		observer.add(obs);
		obs.update(value);
	}

	public void removeObserver(Observer obs) {
		observer.remove(obs);
		obs.update(-value);
	}
}

class Part {
	protected final Price price;

	protected Part(Price price) {
		this.price = price;
	}

	public Price getPrice() {
		return price;
	}
}

class AssemblyGroup extends Part {
	protected final List<Part> parts = new ArrayList<>();
	protected final Observer observer;

	protected AssemblyGroup() {
		super(new Price());
		observer = new Observer() {
			@Override
			public void update(double diff) {
				price.add(diff);
			}
		};
	}

	public final void addPart(Part part) {
		Price p = part.getPrice();
		p.addObserver(observer);
		parts.add(part);
	}

	public final void removePart(Part part) {
		Price p = part.getPrice();
		p.removeObserver(observer);
		parts.remove(part);
	}

	@Override
	public final Price getPrice() {
		return new Price(price);
	}
}

class Product extends AssemblyGroup {
	protected Product() {
		super();
	}

	// man kann hier noch explizit Methoden zum
	// adden und removen von AssemblyGroups
	// implementieren, muss man aber nicht.
}
 

Zurück
Oben