Zum Inhalt springen
  • 0

UML Klassediagramm - Verständnisproblem


Tician

Frage

Hallöööchen mit 3 öchen,

wir schreiben nächste Woche eine Klassenarbeit und ich schaue mir gerade UML nochmal an, aber das Internet und meine Unterlagen sind mir nicht so hilfreich wie ich es gerne hätte.

Es geht um Klassendiagramme: Vererbung, Komposition, Aggregation

Vererbung ist mir klar, im Code ist es leicht zu erkennen:

//Pseudocode
class Leuchte : Panel

und lässt sich durch einen ungefüllten Pfeil von 'Tochter' zu 'Mutter' darstellen.

Komposition stelle ich mir als "Erstellen von Objekten einer anderen Klasse" vor. Also so:

//Pseudocode
class Ampel : Panel
  Leuchte[3] leuchte = new Leuchte()

lässt sich mit einer gefüllten Raute an der erstellenden Klasse darstellen + Anzahl der Objekte die erstellt wurden (an der erstellten Klasse).

 

Jetzt die Frage: Was ist Aggregation und wie kann ich es anhand von Code erkennen bzw im Code darstellen? Ich komme mit den Beziehungen die überall erklärt werden nicht klar, deshalb versuche ich es auf diese Weise zu verstehen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

25 Antworten auf diese Frage

Empfohlene Beiträge

  • 0

Puhh,

mit Code tue ich mich ehrlich schwer, dass zu erklären.

Komposition und Aggregation sind beides "Teil von" Beziehungen.

Das heißt Teil B ist ein Teil von A.

Unterschied der beiden:

Aggregation:

Teil B kann auch ohne Teil A existieren. Beispiel: Ein Haus hat eine Garage, diese steht nebenan. Die Garage ist Teil des Hauses. Reisse ich das Haus ab, so kann die Garage immernoch für sich alleine stehen.

Komposition:

Teil B kann nicht ohne Teil A existieren. Beispiel: Ein Haus hat ein Schlafzimmer. Das Schlafzimmer ist Teil des Hauses. Reisse ich das Haus ab, so ist auch das Schlafzimmer weg.

 

Trivial lässt sich das abhängig von der Programmiersprache nicht darstellen.

Nebenbei: Ich merke mir das so: Aggregation und Komposition sind beide "Teil von" Beziehungen. bei der Komposition geht es nur komplett oder gar nicht.

Bearbeitet von Chiyoko
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Um bei dem Beispiel zu bleiben.

Nemen wir mal an wir haben einen Raum:
 

public class Room
{
    public int WindowCount { get; set; }
    public string Name { get; set; }

    public Room(int windowCount)
    {
        WindowCount = windowCount;
    }

    public override string ToString()
    {
        return string.IsNullOrEmpty(Name) 
            ? $"This room has { WindowCount } windows." 
            : $"The {Name} room has { WindowCount } windows.";
    }
}

Dieser Raum hat Fenster, und ggf. einen Namen.

Ein Haus hat viele Räume und eine Garage.

public class House : IDisposable
{
    public List<Room> Rooms { get; set; }

    public Garage Garage { get; set;}
    public House(List<Room> rooms)
    {
        Rooms = rooms;
    }

    public override string ToString()
    {
        var sb = new StringBuilder();
        sb.AppendLine($"This house has {Rooms.Count} rooms");
        Rooms.ForEach(x => sb.AppendLine(x.ToString()));

        if (Garage != null)
            sb.AppendLine(Garage.ToString());

        return sb.ToString();
    }

    public void Dispose()
    {
        // free resources ...
    }
}

Eine Garage hat Platz für n Autos

 

public class Garage
{
    public int NumberOfCars { get; set; } = 1;
    
    public Garage(int numberOfCars)
    {
        NumberOfCars = numberOfCars;
    }

    public override string ToString()
    {
        return $"This garage has enought space for { NumberOfCars} cars. ";
    }
}

Wenn ich jetzt Objekte erzeuge,
 

static void Main(string[] args)
{
    var garage = new Garage(numberOfCars: 1);

    using (var house = new House(
            new List<Room>
            {
                new Room(windowCount: 1),
                new Room(windowCount: 3)
                {
                    Name = "dining"
                }
            }))
    {
        house.Garage = garage;
        Console.WriteLine("house in a using statement");
        Console.WriteLine(house.ToString());
        Console.WriteLine("leaving using statement");
    }

    Console.WriteLine("garage still there?");
    Console.WriteLine(garage.ToString());
    Console.ReadKey();
}

kann man sehen, das man zunächst eine Garage erzeugen kann, um diese dann einem Haus zuzuordnen.
Das using Statement sorgt dafür das das Haus Objekt nur innerhalb des Statements gültig ist.
Lässt man sich die Garage dannach anzeigen, ist diese immer noch vorhanden.

Output:
 

house in a using statement
This house has 2 rooms
This room has 1 windows.
The dining room has 3 windows.
This garage has enought space for 1 cars.

leaving using statement
garage still there?
This garage has enought space for 1 cars.

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

@Chiyoko Ich verstehe was du meinst und so habe ich es auch verstanden, beides wird also mit dieser Raute dargestellt (entweder gefüllt oder nicht) und wie in meinem Beispiel als Objekte aufgerufen. Rein vom logischen her könnte ich auch sagen "das gibt es ohne das nicht".

Aber wenn ich mir dann unsere Unterlagen ansehen bin ich wieder völlig verwirrt, weil unser einziges Beispiel in dem beides vorkommt so überhaupt nicht passt.

@GooosePseudocode wäre schöner gewesen, ich tu mich sehr schwer dem Code zu folgen und ich versteh den Zusammenhang mit Klassen nicht. Ich denke ich muss mal das Beispiel aus unserem Unterricht ging in dem es um ein Sudoku geht

Bearbeitet von Tician
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0
vor einer Stunde schrieb Gooose:

Das using Statement sorgt dafür das das Haus Objekt nur innerhalb des Statements gültig ist.
Lässt man sich die Garage dannach anzeigen, ist diese immer noch vorhanden.

Was zum Geier? :blink:
Das using hat hier überhaupt nichts zu suchen ...
Das using ist für das IDisposable-Interface und dieses Interface dient fürs Aufräumen vom Speicher von unmanaged Code.

Wenn du das Haus nur innerhalb eines Blockes haben willst, dann setze es doch ein ein Block:

{
    var house = new House(
                    new List<Room>
                    {
                        new Room(windowCount: 1),
                        new Room(windowCount: 3)
                        {
                            Name = "dining"
                        }
                    });

    house.Garage = garage;
    Console.WriteLine("house in a using statement");
    Console.WriteLine(house.ToString());
    Console.WriteLine("leaving using statement");
}

@ Topic:

Eine Aggregation assoziiert "ein Besitz".
z.B. ein Forum besitzt Mitglieder. Das Forum kann aber auch ohne Mitglieder bestehen.
Oder eine Garage besitzt ein Fahrzeug. Die Garage besteht aber auch, wenn kein Fahrzeug drinnensteht.

Eine Komposition assoziert "ein Teil".
z.B. das Herz ist ein Teil des Menschens. Ohne ein Herz kann ein Mensch nicht leben.
Oder ein Raum ist ein Teil eines Gebäudes. Ohne ein Raum existiert auch kein Gebäude.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0
vor 8 Minuten schrieb Whiz-zarD:

Das using hat hier überhaupt nichts zu suchen ...
Das using ist für das IDisposable-Interface und dieses Interface dient fürs Aufräumen vom Speicher von unmanaged Code.

Da gebe ich dir recht. Ein einfacher Block wie in deiner Anmerkung hätte es auch getan.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0
vor 56 Minuten schrieb Whiz-zarD:

Eine Aggregation assoziiert "ein Besitz".
z.B. ein Forum besitzt Mitglieder. Das Forum kann aber auch ohne Mitglieder bestehen.
Oder eine Garage besitzt ein Fahrzeug. Die Garage besteht aber auch, wenn kein Fahrzeug drinnensteht.

Da muss ich mal nachhaken. Eventuell habe ich das in der ganzen Zeit falsch verstanden. Aber es liest sich so, als wären die Mitglieder mit einer Aggregation an das Forum gebunden. Interessant ist dann aber nicht, ob das große Ganze ohne Teile exisitieren kann, sondern das Teil ohne das große Ganze. Oder täusche ich mich hier?

Dementsprechend müsste es doch heißen: Die Mitglieder können auch ohne das Forum exisitieren.

Aus z.B. wikipedia lese ich das so heraus, das beides (Aggregation und Komposition) keine "Besitz", sondern "Teil von" Beziehungen ausdrücken. [https://de.wikipedia.org/wiki/Assoziation_(UML]

In dieser Quelle [http://www.informatik.uni-leipzig.de/~stjaenicke/uml-11.pdf] sieht man allerdings, dass Aggregation doch eher "Besitz" ist. Leider wird nicht die Frage geklärt, welcher Teil ohne den anderen kann.

Hier wiederum [http://stefan.szalowski.de/hs-harz/html/archiv/SoSe06/ooad/pdf/2_UML_Einstieg.pdf] wird alles als "Besitz" betitelt, aber bei der Komposition extra gesgat, dass die Teile nicht ohne das Ganze exisiteren können.

Oder: Ist mit Komposition dann nur "gleiche Lebenszeit" gemeint (siehe zweite Quelle). Das würde aber bedeuten, weder Mensch noch Herz können ohne den anderen existieren. Das steht aber wieder im Gegensatz zu "ist Teil von" und dem, was ich bisher gelernt habe.

Das Ganze scheint wohl doch nicht so eindeutig, wie ich damals gelernt habe :P

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0
vor 1 Stunde schrieb Chiyoko:

Oder: Ist mit Komposition dann nur "gleiche Lebenszeit" gemeint (siehe zweite Quelle). Das würde aber bedeuten, weder Mensch noch Herz können ohne den anderen existieren. Das steht aber wieder im Gegensatz zu "ist Teil von" und dem, was ich bisher gelernt habe.

Ich mag UML zwar überhaupt nicht und habe mich schon seit Jahren nicht mehr damit befasst aber ich denke, dass die Komposition will eher darauf hinaus, wie die Objektorientierung tatsächlich mal gedacht war und nicht, wie sie gelebt wird.

Oft sieht man ja folgendes Konstrukt:

public Class A
{
    public void Foo(int i)
    {
        int x = i * 3;
        B b = new B();
        b.Bar(x);
    }
}

Es sind aber drei Dinge, die in der Methode Foo() stattfinden:

  1. Berechnung von x
  2. Instanzierung von B
  3. der Aufruf von Bar()

Dies verstößt aber eigentlich gegen die Definition der Objektorientierung von Alan Kay. Aus meiner Sicht kommt dann hier die Komposition ins Spiel. Die Komposition würde die einzelnen Logikschritte teilen und sie stattdessen in eine Komposition zusammenführen:

public class Komposition
{
    private A a;
    private B b;

    ...

    public void DoSomething(int i)
    {
        b.Bar(a.Foo(i));
    }
}

public class A
{
    public int Foo(int i)
    {
        return i * 3;
    }
}

public class B
{
    public void Bar(int x)
    {
        ...
    }
}

Bei der Analogie des Menschens könnte die Komposition so aussehen:

public class Mensch
{
    private Herz herz;
    private Aorta aorta;
    ...
      
    public PumpeBlutInDieAorta()
    {
        this.aorta.Blut = this.Herz.PumpeBlut();
    }
}

Somit wird deutlich, dass der Mensch ein Herz braucht. Ohne die Aorta oder ohne das Herz gäbe es eine NullReferenceException. Der Mensch wäre also nicht lebensfähig. Das Herz oder die Aorta lassen sich aber einzeln instanziieren und gezielt testen. So kann man ja auch im wirklichen Leben einem Menschen das Herz entfernen und per elektrische Impulse zum Schlagen bringen. Übertragen auf die Softwareentwicklung wären die elektrischen Impulse der Unittest. Wir schicken ein Signal ins Herz rein und erwarten eine Antwort. 

Wenn man diese Art- und Weise der Entwicklung durchzieht, braucht man fürs Testen nicht mal Mock-Objekte oder Dependency Injection, weil man die Anwendung in einen Integralen-, und Logik-Teil trennen würde. Den Logik-Teil kann man dann sehr leicht mit Unittests testen und der Integrale-Teil wäre ein Teil des Integrationstests. So ist also denkbar, dass das Herz auch nur eine Komposition ist und aus weiteren Teilen besteht, die dann wiederrum einzeln getestet werden können.

Die Aggregation war für mich immer ein optionaler Teil, der auch fehlen kann. Bei der Analogie eines Menschens würde mir da spontan nur die Haare einfallen, die wachsen:

public class Mensch
{
    IList<Haar> haare;
    ...
      
    public void LasseHaareWachsen()
    {
        foreach(Haar haar in this.haare)
          haar.Wachse();
    }
}

Somit kann der Mensch keine Haare haben aber der Mensch würde trotzdem leben.

Bearbeitet von Whiz-zarD
Korrektur des Codes
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Für die Komposition habe ich irgendwo einmal eine noch krassere Definition in Code-Form gesehen: innere Klassen. Die können definitiv nicht ohne die umschließende Klasse existieren. Beispiel:
 

class Mensch {
    class Herz { ... }
    private Herz herz;
    ...
}

Aber in der Praxis (und der Prüfung) würde ich von dieser simplen Definition ausgehen:

  • Assoziation: die Klassen "kennen" sich einfach nur (rufen Methoden auf oder nutzen Attribute voneinander)
  • Aggregation: eine Klasse ist Teil der anderen (Teil/Ganzes-Beziehung), kann aber für sich allein existieren (Reifen -> Auto)
  • Komposition: eine Klasse ist Teil der anderen und kann nicht ohne diese existieren (Raum -> Haus)
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Hach, die schöne Schulzeit :D

Aggregation und Komposition sind zwei Konzepte, die Beziehungen von Objekten untereinander darstellen.

Ein Objekt, dessen Lebenszyklus innerhalb eines anderen startet wird durch die "Komposition" beschrieben.

Typischerweise werden diese dann in Instanzvariablen referenziert. Sinnvollerweise sind es Objekte, die einen sehr eingeschränkten Funktionsbereich haben. In der Praxis sehe ich keinen Grund etwas anderes als Containerobjekte durch Komposition in einem Objekt zu nutzen. Containerobjekte sind Datenstrukturen wie Listen, Tupel, Dictionaries, etc. 

Deren Lebenszeit beginnt im Objekt und endet dort.

Andere Objekte, die bspw. gebraucht werden, um die Funktionalität des sie beherbergenden Objekts zu erweitern, werden durch Aggregation "injeziert". 

Beispiel: Motor. Motoren werden natürlich in Autos eingebaut - Autos gebären keine Motoren unter der Motorhaube. Und mancher Motor überlebt auch das Auto, in welches er eingebaut worden ist. 

Entsprechend sinnvoll ist es, innerhalb der Klasse Auto nicht "new Motor()" aufzurufen, sondern man übergibt den Motor bspw. im Konstruktor (Faustregel: Notwendige Objekte im Konstruktor übergeben und optionale per "Setter" reinreichen).

Das Tolle an der Aggregation ist die Austauschbarkeit der Komponenten, die benötigt werden. Wenn man bspw. den Blinker am Auto testen will, kann man einfach auch einen "Pseudomotor" einbauen. Und wenn jemand versehentlich Gas gibt, passiert halt nix - man will ja in aller Ruhe den Blinker testen.

Umgekehrt muss man davon ausgehen, dass man an alle Objekte, deren Lebenszyklus in anderen beginnt (und endet) in der Regel nicht rankommt; geschweige denn, kann man sie austauschen. Deshalb will man möglichst Abstand halten von der Komposition. 

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0
vor 36 Minuten schrieb lilith2k3:

Entsprechend sinnvoll ist es, innerhalb der Klasse Auto nicht "new Motor()" aufzurufen, sondern man übergibt den Motor bspw. im Konstruktor (Faustregel: Notwendige Objekte im Konstruktor übergeben und optionale per "Setter" reinreichen).

Das ist das sog. Inversion of Control bzw. das Dependecy Injection. Beides sind aber im Grunde nur Patterns, um Klassen zu strukturieren. Allerdings birgt dieses Pattern weitere Probleme, weil man Mock-Objekte benötigt, um solche Klassen testen zu können, da eine Klasse die Funktionalität einer anderen Klasse aufruft. Inzwischen bin ich bei einem Punkt angelangt,  wo ich von Dependecy Injection wieder weggehe und den Code in einen funktionalen- und integralen-Teil zerlege und die Integration die Funktionen zusammenstöpselt. Ich bin da zwar noch dabei Erfahrungen zu sammeln, weil das echt ein Umdenken erfordert aber so fühlt sich Objektorientierung irgendwie richtiger an. Man braucht einfach keine großen Dependency Injection- und Mocking-Frameworks, um überleben zu können, sondern einfach nur die Sprachfeatures.

vor 56 Minuten schrieb lilith2k3:

Das Tolle an der Aggregation ist die Austauschbarkeit der Komponenten, die benötigt werden. Wenn man bspw. den Blinker am Auto testen will, kann man einfach auch einen "Pseudomotor" einbauen. Und wenn jemand versehentlich Gas gibt, passiert halt nix - man will ja in aller Ruhe den Blinker testen.

Wenn ich einen Blinker testen will, wozu brauche ich da ein Pseudomotor?
Baust du auch ein Motor aus Pappmaché, um einen Blinker zu testen?
Nein. Du testest die Glühlampe und dann das Blinker-Relais. 

Und genau das ist doch das Problem vom Dependecy Injection: Teile, die ich gar nicht testen will, muss ich erst mal nachbauen, weil trotzdem die Funktionalität benötigt wird. 

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0
Zitat

 Beides sind aber im Grunde nur Patterns, um Klassen zu strukturieren

Ja. So wie UML ein graphisches Verfahren ist, Software zu modellieren. 

Zitat

Allerdings birgt dieses Pattern weitere Probleme, weil man Mock-Objekte benötigt, um solche Klassen testen zu können, da eine Klasse die Funktionalität einer anderen Klasse aufruft.

Ich verstehe nicht, worin der Nachteil bestehen sollte.

Das Zusammenspiel von Objekten, die durch Komposition zusammengesteckt werden kann ich schlechter testen als durch Aggregation.  Die von Dir Angeführten Mocks bieten Dir Möglichkeiten, die du ohne nicht hast: bspw. sicherstellen, dass gewissen Methode keinmal, einmal, mindestens einmal aufgerufen worden sind. 

Möchte ich das nicht testen, benötige ich auch keine Mocks. 

Das heißt, Du hast im Vergleich zur Komposition keine Nachteile. Da sind die Objekte genauso opak in der Benutzung.

Seine Software in der Art zu strukturieren bietet Dir die Möglichkeit, testgesteuert (ich vermeide explizit »testdriven«) Deine Software zu entwickeln. 

Man kann sein Smartphone nutzen, um ulkige Katzenvideos zu gucken - aber es ist noch mehr drin.

Es ist natürlich von den Ansprüchen, die man an sein Produkt hat, abhängig.

vor 13 Stunden schrieb Whiz-zarD:

 Inzwischen bin ich bei einem Punkt angelangt,  wo ich von Dependecy Injection wieder weggehe und den Code in einen funktionalen- und integralen-Teil zerlege und die Integration die Funktionen zusammenstöpselt. Ich bin da zwar noch dabei Erfahrungen zu sammeln, weil das echt ein Umdenken erfordert aber so fühlt sich Objektorientierung irgendwie richtiger an. Man braucht einfach keine großen Dependency Injection- und Mocking-Frameworks, um überleben zu können, sondern einfach nur die Sprachfeatures.

Das tolle an der Softwarentwicklung ist ja, dass Du als Entwickler bestimmen kannst, mit welchen Wegen Du zum Ziel kommst. Ich bin aus dem Alter raus, wo ich den Leuten erzählen will, was sich wie richtiger anfühlt.

Es zählt, dass mit der Software Geld verdient werden kann. Ob das objektorientiert, funktional, prozedural, strukturiert passiert interessiert keinen Kunden/Arbeitgeber. Am Ende des Budgets muss das Problem gelöst sein, sonst taugt der Ansatz nicht.

 

Bearbeitet von lilith2k3
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0
vor 41 Minuten schrieb lilith2k3:

Das Zusammenspiel von Objekten, die durch Komposition zusammengesteckt werden kann ich schlechter testen als durch Aggregation.  Die von Dir Angeführten Mocks bieten Dir Möglichkeiten, die du ohne nicht hast: bspw. sicherstellen, dass gewissen Methode keinmal, einmal, mindestens einmal aufgerufen worden sind.

Noch mal: Wozu braucht man Mocks?
Mocks sind nichts weiter als ein Hilfskonstrukt und ein eher Zeichen, dass mit der Softwarearchitektur irgendwas nicht stimmt.

Wenn man Mocks benötigt, hat man Abhängigkeiten. Sei es auch nur Abhängigkeiten zu Interfaces. Die Abhängigkeit ist aber da. Ohne ein Objekt, was ein bestimmtes Interface implementiert, kann ein anderes Objekt nicht leben. Die Frage ich aber, sind diese Abhängigkeiten überhaupt notwendig? Die reale Welt zeigt es doch deutlich: Nein.

Als Beispiel hast doch der Blinker genannt. Wozu brauche ich dann nun ein Pseudo-Auto mit einem Pseudo-Motor, wenn ich nur den Blinker testen will? Stell dir eine Werkbank vor und auf dieser Werkbank liegt ein Blinker. Aus welchen Bauteilen besteht ein Blinker? Aus einem Blink-Relais, aus Kabeln und einem Leuchtmittel. Ich kann den Blinker also in seine Einzelteile zerlegen und einzeln testen. Ans Blink-Relais lege ich eine Spannung an und prüfe, ob das Relais auf und zu geht. Dem Leuchtmittel lege ich auch eine Spannung an und prüfe, ob sie leuchtet. Dann verbinde ich beide Teile mit einem Kabel (unsere Komposition) und lege wieder eine Spannung ans Relais an und siehe: Das Leuchtmittel blinkt. Wunderbar! Blinker getestet. Nächstes Bauteil...

Das mache ich solange, bis ich alle Bauteile eines Autos getestet habe und kann nun die Teile ins Auto stecken und das Auto testen (wieder eine Komposition). Man sieht also, dass ein Mock-Objekt gar nicht von Nöten ist. Das Testen von Kompositionen wären dann die Integrationstests und das Testen einzelner, elementarer Bauteile wären unsere Unittests.

Und hier noch mal zum Thema Inversion of Control und Dependecy Injection:
Wann wird das mal wirklich benötigt? Beispiel Auto: Wenn ich sowieso nur ein Auto baue und das immer mit den selben Komponenten, wozu brauche ich dann Dependency Injection? Dependency Injection wird doch häufig nur des SOLID wegens implementiert aber oftmals wird das gar nicht benötigt. Man bläht also seine Anwendung nur unnötig auf. Dependecy Injection wird doch häufig nur verwendet, um tatsächlich Mock-Objekte in sein zu testendes Objekt stecken zu können aber überträgt man dies in die reale Welt, kommt doch was völlig absurdes raus:

vor 15 Stunden schrieb lilith2k3:

Das Tolle an der Aggregation ist die Austauschbarkeit der Komponenten, die benötigt werden. Wenn man bspw. den Blinker am Auto testen will, kann man einfach auch einen "Pseudomotor" einbauen. Und wenn jemand versehentlich Gas gibt, passiert halt nix - man will ja in aller Ruhe den Blinker testen.

Man spricht immer davon, dass Softwareentwicklung ein Handwerk sei also wieso seinen Code nicht so entwickeln und testen, wie ein Handwerker seine Arbeit verrichtet? Ein Kfz-Mechatroniker braucht doch auch kein Motor aus Pappmaché, wenn er den Blinker testen will. Die Austauschbarkeit wird also nur häufig nur des Testen-Willens vorgeschoben. Im Produktionscode ist aber die Austauschbarkeit gar nicht gefordert. Vielleicht hat man nur eine Art von Blinkern. Man baut also in den Produktionscode ein Hilfskonstrukt nur für den Test ein. Für mich persönlich klingt das einfach völlig absurd.

Dependency Injection bzw. Inversion of Control lässt sich auch durch Kompositionen realiseren, indem ich ein Interface und eine abstrakte Klasse für die Komposition erschaffe und für meine unterschiedlichen Konfigurationen in Klassen auslagere. Beispiel:

public interface IKomposition
{
	void TuEtwas();
}

public abstract class KompositionBase : IKomposition
{
	protected IAbhängigkeitA AbhängigkeitA { get; private set; }
	protected IAbhängigkeitB AbhängigkeitB  { get; private set; }

	public KompositionBase(IAbhängigkeitA abhängigkeitA, IAbhängigkeitB abhängigkeitB)
	{
		this.AbhängigkeitA = abhängigkeitA;
		this.AbhängigkeitB = AbhängigkeitB;
	}

	public abstract void TuEtwas();
}

public class KompositionA : KompositionBase
{
	public KompositionA() : base(new AbhängigkeitA1Impl(), new AbhängigkeitB1Impl())
	{ }

	public override void TuEtwas()
	{
		/// Code
	}
}

public class KompositionB : KompositionBase
{
	public KompositionB() : base(new AbhängigkeitA2Impl(), new AbhängigkeitB2Impl())
	{ }

	public override void TuEtwas()
	{
		/// Code
	}
}

Also kann ich doch die Klassen AbhängigkeitA1Impl, AbhängigkeitA2Impl, AbhängigkeitB1Impl und AbhängigkeitB2Impl einzeln testen und dann im Integrationstest KompositionA und KompositionB. Und siehe da, ich brauche nicht mal ein DI-Framework oder Mock-Objekte. Ein DI-Framework macht wirklich nur dann Sinn, wenn ich Kompositionen(!) über eine Konfiguration zusammenstecken möchte. Beispielsweise ich habe mehrere Kunden und Kunde A möchte, dass die Logs in die Datenbank geschrieben werden und Kunde B Logs in eine Datei oder Kunde A verwendet MSSQL und Kunde B OracleDB und ich möchte nicht für den Kunden ein eigenes Kompilat ausliefern. Diese Art von Konfiguration wird aber in vielen Fällen im Code gar nicht benötigt. Wer z.B. eine App ausliefert, die bei allen Kunden sowieso gleich funktioniert, braucht so etwas eigentlich gar nicht.

vor 53 Minuten schrieb lilith2k3:

Das tolle an der Softwarentwicklung ist ja, dass Du als Entwickler bestimmen kannst, mit welchen Wegen Du zum Ziel kommst. Ich bin aus dem Alter raus, wo ich den Leuten erzählen will, was sich wie richtiger anfühlt.

Dann gehörst du leider zu den Personen der Ignoranten, die nicht mal neue Wege ausprobieren und ihre Erfahrungen teilen. Sorry. Leider besteht die Softwarentwicklung hauptsächlich nur aus Versuch-und-Irrtum und es geht darum, immer wieder neue Wege zu finden, wie man sich die Arbeit erleichtern kann. Gäbe es z.B. Robert C. Martin und andere, die das Clean Code voranbringen, nicht, würde man heute immer noch seitenlangen Spaghetti-Code in Excel und Access schreiben und lehren. Auch ein Softwareentwickler sollte sich weiterbilden und schauen, ob seine es inzwischen nicht andere Wege gibt, wie man Software entwickelt. Solche Sturköpfe habe ich auch in der Firma, wo ich arbeite und was produzieren sie? Unwartbaren und unleserlichen Spaghetti-Code, der in Regions und Kommentar-Blöcken geclustert wird und dann steht man dort vor einer 15.000 zeiligen Klasse, wo ReSharper schon schlapp macht, diese zu analysieren.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Äh... ja ich verstehe zu 90% nur Bahnhof >.<

@lilith2k3 Deine Beschreibung passte bisher am ehesten zu dem was uns unser Lehrer beigebracht hat (auch wenn ich es immer noch nicht ganz verstehe).

Es war etwas das beim einen die Objekte neu erzeugt werden und beim anderen sind sie schon vorhanden.

Unser Beispiel-UML besteht aus 3 Klassen: Sudoku, NeunerGruppe und TextBox (das Projekt an sich hat natürlich mehr, aber diese 3 haben wir in einem UML dargestellt)

Sorry wenn ich jetzt nicht mit Fachbegriffen sondern mit Laiensprache um mich schmeiße.

Es gibt nur 3 'Verbindungen':

Die Klasse NeunerGruppe besteht aus 9 TextBoxen. Aufgezeichnet haben wir eine Aggregation 9/1

Die Klasse Sudoku besteht aus 9 mal 9, also 81 TextBoxen. Aufgezeichnet ist eine Komposition 81/1

Die Klasse Sudoku besteht aber auch aus 27 NeunerGruppen (9 waagerecht, 9 senkrecht, 9 in 3x3 Felder angeordnet, wer schonmal Sudoku gemacht hat weiß was ich meine). Hier auch wieder eine Komposition 27/1

 

Aber außer das "Teile sind vorhanden" bei einer Aggregation habe ich überhaupt keinen anderen Anhaltspunkt wie das zustande kommt, entweder ich habe einen falschen Code ausgedruckt oder die Objekte werden einfach absolut identisch jeweils im Konstruktor erstellt. Ich war mir ziemlich sicher das man es amhand vom Code unterscheiden konnte ohne auf die Logik einzugehen ob ein Raum jetzt nun ohne Haus existieren kann oder nicht oder andersrum oder wie auch immer.

 

 

Bearbeitet von Tician
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Hier mal ein Zitat aus Wikipedia (https://de.wikipedia.org/wiki/Aggregation_(Informatik)):

Zitat

Aggregation: Eine Ehe besteht aus zwei Ehepartnern, die auch nach einer Scheidung der Ehe als eigenständige Personen fortbestehen.

Komposition: Im Unterschied dazu besteht ein Gebäude aus Stockwerken, die nach dessen Abriss nicht eigenständig fortbestehen.

Bei einer Aggregation können die Objekte auch ohne die Aggregation leben. Dies wäre z.B. beim "Dependency Injection" der Fall, wenn wir die Abhängigkeiten in eine Klasse reinreichen:

public class Ehe
{
	Person Mann;
	Person Frau;

	public Ehe(Person mann, Person frau)
	{
		this.Mann = mann;
		this.Frau = frau;
	}
}

Es müssen also zuerst die Personen erzeugt werden, bevor wir die Ehe erzeugen können. Die Personen können also ohne die Ehe leben.

Bei einer Komposition hingegen könnte ein Objekt nicht alleine leben, was ein anderes Objekt erzeugt hat. Es ist dann also so, dass in einem Objekt (z.B. Haus) ein weiteres Objekt (z.B. Stockwerk) erzeugt wird und wenn das Haus-Objekt löscht wird, werden auch die Stockwerk-Objekte gelöscht. Beispiel
 

public class Haus
{
	private Stockwerk erdgeschoss;
  
	public Haus()
	{
		this.erdgeschoss = new Stockwerk();
	}
}

 

Bearbeitet von Whiz-zarD
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

@Tician

Tja. Du wirst lachen, aber ich hatte in der Berufsschule genau das gleiche Problem wie Du.

Das liegt meist daran, dass die Schulbeispiele herrlich konstruiert sind XD

Ich glaube, dass gerade Dein Beispiel ein gutes Beispiel dafür ist, wie man es den Schülern unnötig schwer macht, zu verstehen, was man eigentlich will, weil man irgendwas auf teufelkommraus OOP-artig präsentieren will. 

Ich versuch das mal irgendwie so umzustricken, dass es halbwegs sinn macht XD

Also:

Stell Dir vor, Du hast eine Game-Engine-Klasse für Sudoku. Diese Game-Engine enthält das Regelset dafür, welche Felder wie ausgefüllt sein dürfen etc. 

Diese Game-Engine könntest Du theoretisch so programmieren, dass man sie wie ein Programm auf der Konsole aufrufen könnte. Du könntest quasi eine Eingaberoutine schreiben, die ein CSV bekommt und Dir ausspuckt, ob das ein valides Sudoko Ergebnis ist. 

In der Zwischenzeit hat sich jemand die Mühe gemacht, einen graphischen Client für Deine Regel-Engine zu basteln.

Er bastelt quasi eine Klasse, die als Vermittler zwischen der Anwendung und Deiner Game-Engine steht. Übergabeparameter im Konstruktor ist eben ein Objekt vom Typus Deiner Gameengine.

Aus dem Zusammenhang wird klar, dass es sich um eine Aggregation handeln muss. Denn wie wir gesehen haben, funktioniert Deine Engine auch prima, ohne dass man die GUI dazu braucht. 

Und was noch viel spannender ist: Du könntest - wenn Du schön brav threadsafe code geschrieben hast - diese eine Instanz Deiner Engine auch für einen Multiuser-Client zur Verfügung stellen: Jeder User erhält zwar eine eigene Instanz der GUI; aber Deine Engine existiert noch brav weiter, wenn der Client geschlossen wird. 

Man könnte den Client auch so umschreiben, dass sich jede Instanz des Clients eine eigene Instanz Deiner Engine mit new () selbst baut. Diese Instanz wäre dann an die Lebenszeit des Clients gebunden. Das wäre dann eine Komposition.

vor 3 Stunden schrieb Tician:

Die Klasse NeunerGruppe besteht aus 9 TextBoxen. Aufgezeichnet haben wir eine Aggregation 9/1

Die Klasse Sudoku besteht aus 9 mal 9, also 81 TextBoxen. Aufgezeichnet ist eine Komposition 81/1

Die Klasse Sudoku besteht aber auch aus 27 NeunerGruppen (9 waagerecht, 9 senkrecht, 9 in 3x3 Felder angeordnet, wer schonmal Sudoku gemacht hat weiß was ich meine). Hier auch wieder eine Komposition 27/1

Das ist kompliziert.

Wenn man das Beispiel »Sudoku« rein realweltlich interpretieren würde, kommt man zu dem Schluss:

Ein Sudoku-Spiel besteht aus 81 individuellen Textboxen. Die sind allesamt Teil des Sudoku-Spiels.

Zwar kann ein Schlaumeier behaupten: »naja, wenn ich mir das Sudoku wegdenke, kann ich die Kästchen ja immer noch zum rechnen benuzten« - aber das trifft es nicht. 

Und es würde auch niemand behaupten: »Hey, ich habe gerade aus meinem Matheheft 81 Kästchen ausgeschnitten, die kann ich jetzt zum Sudokuspielen nutzen. Oder wenn ich keine Lust mehr habe, klebe ich sie wieder in mein Matheheft«

Die 81 Kästchen verdanken ihr Sudoku-Kästchen-Sein einzig dem Spiel als ganzem. Und wenn Du den Zettel zerknüllst, sind die Kästchen auch im A*****. Also leben die Teile streng genommen nur solange, wie das Spiel als solches lebt. Und ihren Sinn haben die Kästchen nur durch das Spiel.

Also sind das 81 Kästchen in Komposition vereint. 

Andererseits könnte ein kluger Philosoph hingehen und sagen:

»Hey, ich male ein Kästchen. Oopsi: hier, noch eins. Und plötzlich waren es 80. Damit könnte ich Rechenaufgaben lösen. Nein, warte: hey! Boah! Ich könnte noch eins malen und habe ein Sudokuraster« 

In dem Fall waren die Kästchen vor dem Spiel da und hätten das Spiel auch überlebt. 

Wie gesagt: Das tolle an so Geschichten ist, dass Sie konstruiert sind, um Schülern etwas beizubringen. Letztlich legt man sich, wenn man Pech hat einundrölfzig Fallstricke aus, über die man ständig fällt; und man verwirrt die Schüler mehr als ihnen zu helfen.

Aber IMHO kannst Du bei dem realen Sudoku es drehen wie Du willst, Du hast immer Komposition, weil es keinen Sinn macht, von einer Gruppe auszugehen, die Außerhalb des Sudokus existiert. Aber das sagt Dir nur ein alter Mann aus dem Internet :D

 Implementieren kannst Du das natürlich wie Du willst: Du könntest Objekte Bauen, die Du der Sudokugruppe "leihst" - analog zur Game-Engine, die Du wiederverwenden kannst - und nach Beendigung des Sudokus hättest Du die Objekte immer noch. Allerdings stellt sich für mich die Frage nach frei flottierenden Textboxen *LOL*

Schön wäre es, wenn Dir das Game-Engine-Beispiel helfen kann, das Ganze besser zu verstehen :]

Bearbeitet von lilith2k3
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0
vor 19 Minuten schrieb lilith2k3:

Ich glaube, dass gerade Dein Beispiel ein gutes Beispiel dafür ist, wie man es den Schülern unnötig schwer macht, zu verstehen, was man eigentlich will, weil man irgendwas auf teufelkommraus OOP-artig präsentieren will. 

Eher, dass auf Teufelkommraus unbedingt alles eine grafische Oberfläche haben muss und in der sich alles abspielt und den Azubis OOP gar nicht beibringt. 

Bearbeitet von Whiz-zarD
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Blödes Beispiel zur Aggregation/Komposition:

Gugelhupf - Das Loch in der Mitte ist nur solange da, wie der Kuchen drumherum ist (Komposition)

Nur ein Softwareentwickler würde behaupten, er habe ein Loch genommen und einen Kuchen drumherumgebaut und würde das Loch anschließend, nachdem der Kuchen gegessen ist, wiederverwenden (Aggregation).

Bearbeitet von lilith2k3
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Mir ist bewusst, dass es sich in diesem Thread um schulischen bzw prüfungsrelvanten Inhalt handelt, aber gerade an dieser Diskussion kann man die Praxistauglichkeit von UML gut erkennen. Man sieht hier den Grund dafür, dass teilweise UML in der Praxis entweder gar nicht verwendet wird oder meistens jeder sein eigenes FreeStyle-UML verwendet (es mag sicher Ausnahmen geben).

Ich finde es trotzdem immer wieder interessant, wie oft man trotzdem immernoch in Stellenausschreibungen lesen kann, dass UML-Kenntnisse erwünscht sind.

@Whiz-zarD: Ohne diesen Thread jetzt sprengen zu wollen würde mich deine neue Herangehensweise interessieren, die von dir erwähnten Nachteile von Dependency-Injection kann ich Nachvollziehen, habe allerdings noch nicht ganz verstanden, wie du das Problem jetzt gelöst hast.

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

1. Aufgabe: Vorgefertigt gab es ein funktionierendes Programm mit einer Ampel die wir anhand eines Statechart mit switch/case um eine Fußgängerampel  (2 Panels) erweitern sollten -> lief sehr gut

2. Aufgabe: Aus einem bestehenden Programm ein UML zeichnen -> es gab nur 2 Vererbungen und 2 Kompositionen, ich hab erst alles richtig gehabt, dann aber aus Unsicherheit die Kompositions-Rauten durchgestrichen und am jeweils anderen Ende des "Pfeils" angebracht und somit falsch gemacht (wie ich hinterher gemerkt habe). :unsure:

3. Anhand eines Textes ein Zustandsdiagramm (State Chart) über eine Heizung gezeichnet die sich je nach Temperatur aus/ein-schalten soll -> lief sehr gut.

Würde auf die Note 1-2 tippen

 

Ich hab den Lehrer auch nochmal um den Unterschied zwischen Komposition und Aggregation gebeten. Komposition ist wenn ein Objekt neu erzeugt wird, Aggregation (sagt er) ist wenn ein Objekt erzeugt wird und dabei etwas bestehendes über die Klammern danach übergeben wird.

Z.B. Haus haus = new Haus(garage);

Da Garage schon vorhanden ist und woanders erstellt wurde ist das hier wohl eine Aggregation. Wie gesagt so hat es der Lehrer nun erklärt und so wird es auch verlangt.

Bearbeitet von Tician
Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Diese Frage beantworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...