T-Rex: Softwarepattern für Kumulierung aus einer Liste gesucht

Moin,

Ich hab da so ein Objekt (nennen wir es Zahlen-Objekt). Das speichert und berechnet verschiedene Inhalte. Es gibt mehrere Methoden, welche eine Zahl zurück geben. Das sieht vereinfacht dann ungefähr so aus:

public function getZahl1() { return $this->zahl1; }

public function getZahl2() { return $this->zahl2; }

public function getZahl3() { return $this->zahl3; }

In Wirklichkeit gibt es in den Methoden noch Berechnungen, was für meine Frage irrelevant ist. Wichtig ist, dass am Ende eine Zahl zurück geliefert wird.

Es kann mehrere dieser Objekte geben. Diese werden in einem anderen Objekt (nennen wir es Listen-Objekt) gespeichert. In diesem Listen-Objekt ist ein Array, was die Zahlen-Objekte speichert.

Jetzt gibt es mehrere fast identische Methode, welche die getZahlX Methoden kumulieren und die Summe zurück geben. Die sehen vereinfacht gesagt so aus:

public function getZahl1()
{
	$return = 0;
	foreach( $this->arZahlObjekte as $objekt )
	{
		$return += $objekt->getZahl1();
	}
	return round( $return, 2);
}

public function getZahl2()
{
	$return = 0;
	foreach( $this->arZahlObjekte as $objekt )
	{
		$return += $objekt->getZahl2();
	}
	return round( $return, 2);
}

Wie man sieht gibt es nur zwei Unterschiede und das sind die Methodennamen. Witzigerweise heißen sie auch noch gleich, also im Zahlen-Objekt und im Listen-Objekt, was eventuell der Vereinfachung der Schreibweise hilft.

Da ich ca. 10 solcher Methoden habe (und sowas immer mal wieder vorkommt), ist meine Frage ob man das irgendwie einfacher schreiben kann?

Gruß T-Rex + T-Rex + T-Rex + T-Rex + T-Rex + T-Rex + T-Rex = ?

  1. Hallo T-Rex,

    einfacher sicher - aber ob schneller?

    Möglichkeiten gips genug.

    class ValueList
    {
       private $values;
    
       private function summiere1($getterName) {
       {
          $summe = 0;
          foreach ($this->values as $v)
          {
             $summe += $v->$getterName();
          }
          return $summe;
       }
    
       public function getSumme1() 
       {
          return summiere("getZahl1");
       }
    
       public function getSumme2() 
       {
          return array_reduce($this->values, 
                              fn($summe, $val) => $summe + $val->getZahl2(),
                              0);
       }
    }
    

    getSumme1 verwendet die private summiere Funktion, die den Getter per Name aufruft. Ja, tatsächlich, geht: $this->$getter() ruft die Methode auf, deren Name in $getter steht.

    getSumme2 verwendet array_reduce, statt die Schleife selbst zu bilden. Die array_reduce Funktion erwartet als zweiten Parameter ein callable-Objekt, was in PHP viele Formen annehmen kann:

    • Name einer globalen Funktion
    • Ein Array aus Objekt und Funktionsname
    • Eine anonyme Funktion: function($s, $v) { return $s + $v->getZahl2(); }
    • Eine Pfeilfunktion: fn($s, $v) => $s + $v->getZahl2(),

    Pfeilfunktionen (anderswo auch Lambda-Ausdruck genannt) sind die Kurzform einer anonymen Funktion, wenn diese nichts weiter tut als einen Ausdruck auszuwerten und das Ergebnis zurückzugeben. Sie haben auch den zusätzlichen Vorteil, automatisch Variablen des Scope, in dem sie definiert werden, in eine Closure aufzunehmen, ohne dass man dafür mit use herumbasteln muss.

    PHP hat da ein paar nette funktionale Elemente von von JavaScript und anderen Sprachen abgeguckt.

    Ob der Aufruf über Name oder per Pfeilfunktion schneller ist, müsste man messen. Aber es kann nicht viel ausmachen.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Interessante Ideen 😀.

      Das heißt aber, dass sich die Anzahl der Methoden nicht minimiert. Ich brauche immer noch für 10 Kumulierungen 10 Methodenaufrufe?

         public function getSumme1() 
         {
            return summiere("getZahl1");
         }
      
         public function getSumme3() 
         {
            return summiere("getZahl3");
         }
      
         public function getSumme4() 
         {
            return summiere("getZahl4");
         }
      

      Right? Bei der zweiten Lösung sieht es ähnlich aus, nur halt anders 😀.

      Wenn man die private Methode jedoch öffentlich macht, dann könnte man von außen den Methodennamen übergeben und hätte nur noch eine Methode?

      Andere Idee ... wenn summiere wüsste wie der Methodenname der Aufrufenden Methode lautet, dann bräuchte keinen Parameter mehr.

      Wie du merkst hast du meine Kreativität beflügelt. Vielen Dank!

      Gruß ausgrecheneter T-Rex

      1. Wenn man die private Methode jedoch öffentlich macht, dann könnte man von außen den Methodennamen übergeben

        Die Methode kann sogar privat bleiben:

        <?php
        
        class foo {
        	
        	private function bar() {
        		return "BAR";
        	}
        	
        	private function tok() {
        		return"TOK";
        	}
        
        	public function printOut( $what ) {
        		echo $this->$what()	. PHP_EOL;
        	}
        
        }
        
        $o = new foo;
        $o->printOut('bar'); # BAR
        $o->printOut('tok'); # TOK
        
        

        und hätte nur noch eine Methode?

        Ja. Genauer: Eine Methode zu „adressieren“.

      2. Hallo T-Rex,

        ja gut. Du kannst natürlich auch

        function getSumme(int $n) {
           if ($n < 1 || $n > 9)
              return 0;
           else
              return summiere("getZahl$n");
        }
        

        schreiben, wenn Du die gewünschte Zahl als Parameter angeben willst.#

        Oder, in der Version mit Pfeilfunktion, ein privates statisches Array vorhalten mit Pfeilfunktionen für die jeweiligen Getter.

        Um auf Raketenwillis Einwand zurückzukommen: "einfacher" ist Ansichtssache. Weniger Code ist nicht unbedingt besser - im Sinne von Performance oder Lesbarkeit.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Nein sorry, die Lösung geht nicht. Wie gesagt habe ich meine Situation nur vereinfacht dargestellt. Die Methoden heißen schon komplett anders 😀. Ich hoffe kein "echter" Programmierer hat Methodennamen mit Zahl1,2,3 😂.

          Ist aber natürlich eine gute und logische Möglichkeit unter diesen Gegebenheiten.

          Gruß T-Rex1,2,3,4

          1. Hallo unkreativer T-Rex,

            Nein sorry, die Lösung geht nicht.

            Dann mach sie halt gehend. Ich kann doch nur basierend auf dem was vorschlagen, was Du zeigst.

            Wenn die Methodennamen wild durcheinander sind, mach ein Mapping-Array. Ein statisches Array mit Pfeilfunktionen funktioniert leider nicht, ein statischer Initializer mag keine anonymen Funktionen haben. Vermutlich, weil der Kontext für eine Closure fehlt. Aber ein Array mit Methodennamen geht.

            Das kann man noch mit Klassenkonstanten dekorieren.

            https://onlinephp.io/c/684c4

            In PHP gehen so viele Sauereien, irgendeine davon wird bestimmt zufrieden in deinem Stall grunzen können.

            Rolf

            --
            sumpsi - posui - obstruxi
  2. ob man das irgendwie einfacher schreiben kann?

    Nur „einfacher“ und „am einfachsten“ ist schwieriger zu definieren, als „einfach“.

    1. Hab ich wieder einen Fauxpas gebaut über den wir diskutieren wollen ohne uns über das eigentliche Problem zu kümmern?

      Gruß Le Fauxpas Te Rex

      1. Hab ich wieder einen Fauxpas gebaut über den wir diskutieren wollen ohne uns über das eigentliche Problem zu kümmern?

        Gruß Le Fauxpas Te Rex

        Sei unbesorgt. Nur was für den einen „einfacher“ ist, ist dem anderen ein „schwerer Fall von Hirnknoten“ - und „visa versa“! Das macht es schwierig, eine „einfachere“ Lösung zu nennen.