Aggregation und Delegation sinnvoll nutzen für Objektübergreifenden Datenaustausch

Klassenübergreifende Kommunikation zwischen Objekten zweckmäßig organisieren, Dependency Injaction

Oft besteht die Aufgabe, dass Instanzen nicht verwandter Klassen kommunizieren müssen. Sei es dass ein Request-Objekt eine Header-Instanz benötigt oder ein Data Access Layer angesprochen werden muss. Damit das funktioniert, müssen selbstverständlich die Schnittstellen einer Klasse bzw. Instanz nach draußen klar definiert sein. Unschön und in Multiprozessumgebungen möglicherweise fehlerhaft ist es, wenn Instanzen nicht verwandter Klassen global definiert und beim Funktionsaufruf übergeben werden. Da sowas auch schlecht zu debuggen ist, beschreibt der folgende Artikel ein effizienteres Verfahren anhand eines konkreten Beispiels.

Wesentlich ist, dass Methoden nicht verwandter Klassen über eigene Methoden gekapselt werden.

Aggregation

Aggregation heißt, dass bereits beim Erstellen der Instanz die Kommunikation berücksichtigt wird. Die Kommunikation selbst wird stets über eine Methode der eigenen Instanz (hier eine Instanz der Klasse Fahrzeug) abgewickelt, wobei Instanzen nicht verwandter Klassen bereits im eigenen Konstruktor als Attribute aggregiert werden:

# erstelle gleich mal 2 Instanzen
# Fahrzeugführer ID wird durchgereicht
   $pkw = Fahrzeug->new(ffid => 1);
   $suv = Fahrzeug->new(ffid => 2);

# Wenn der Fahrer gecheckt werden soll, ruft die Fahrzeug-Instanz eine eigene Methode
   $pkw->check;
   $suv->check;


# Anwendungsklasse
package Fahrzeug;

# Argumente für die Konstruktoren nicht verwandter Klassen werden durchgereicht
# für Debugzwecke wird die gesamte Konfiguration in der eigenen Instanz mitgeschleift
sub new{
   my $class = shift;
   my %cfg = (
      ffid => '', # Fahrzeugführer ID
      # ggf. weitere Defaults
   @_);
   my $self = bless{
      Fahrer   => Fahrer->new( $cfg{ffid} ),
      Polizist => Polizei->new,
      CFG      => \%cfg,
   ), $class;
}

# Delegation in eine eigene Methode
# mit eigener Fehlerbehandlung
sub check{
   my $self = shift;

   my $fuehrerschein = $self->{Fahrer}->fuehrerschein();
   $self->{Polizist}->check( $fuehrerschein) ||
      $self->fehlerbehandlung();
}

Delegation

Wesentlich ist also, dass innerhalb der eigenen Anwendung keine Instanzen nicht verwandter Klassen erstellt werden, weil das bereits im Konstruktor der eigenen Instanz erfolgt. Somit werden auch keine Instanzen anderer Klassen übergeben sondern nur die relevanten Daten, im Beispiel die ID des Fahrzeugführers womit das Fahrzeugführerobjekt in der Lage ist, sämtliche Daten des Führerscheins zu liefern. Letzere werden dann an das Polizeiobjekt übergeben. Die gesamte Kommunikation findet also in einer eigenen Methode bzw. in Methoden der eigenen Anwendungsklasse statt. In dieser eigenen Methode also, und nur ebenda, greift die eigene Instanz auf die Schnittstellen der anderen Instanzen zu.

Lazy Delegation

Heißt lediglich, dass die Instanzen der nicht verwandten Klassen erst in einer eigenen Methode hinzugefügt werden. Z.B. so:

sub check{
   my $self = shift;
   my $ffid = shift || $self->{CFG}{ffid};

   $self->{Fahrer}   = Fahrer->new( $ffid ) unless $self->{Fahrer};
   $self->{Polizist} = Polizei->new unless $self->{Polizist};


   my $fuehrerschein = $self->{Fahrer}->fuehrerschein();
   $self->{Polizist}->check( $fuehrerschein) ||
      $self->fehlerbehandlung();
}

Somit verbirgt sich hinter Lazy Delegation eine Lazy Aggregation. Über Perls AUTOLOAD lässt es sich weiterhin einrichten, dass der hier zugehörige Code erst dann compiliert wird, wenn es erforderlich ist.

Anmerkungen zu Dependency Injection

Untenstehender Code zeigt, wie eine Instanz außerhalb der eigenen Klasse erstellt und in den Konstruktor der eigenen Klasse übergeben wird. Dadurch geht die Kapselung verloren:

package Cookie; # nicht verwandte Klasse

sub new{
    my $class = shift;
    return bless{}, $class;
}


package HTTP;   # Eigene Klasse

sub new{
    my $class = shift;
    my $cookie = shift; # Objekt wird eingebaut
    return bless{COOKIE => $cookie}, $class;
}

# das aggregierte Objekt wird verändert
sub request{
    my $self = shift;
    $self->{COOKIE}{text} = "Huch, hab mir einen Cookie eingefangen";
}

package main;

my $cookie = Cookie->new;
my $http = HTTP->new($cookie);

$http->request;

print Dumper $cookie;
__END__

Der Dump zeigt das veränderte Objekt, die Daten sind somit von außen
zugänglich
$VAR1 = bless( {
                 'text' => \'Huch, hab mir einen Cookie eingefangen'
               }, 'Cookie' );

Anbieter: nmq​rstx-18­@yahoo.de, die Seite verwendet funktionsbedingt einen Session-Cookie und ist Bestandteil meines nach modernen Aspekten in Perl entwickelten Frameworks.