Klassenfrage

Dieses Thema im Forum "Backend: PHP und MySQL" wurde erstellt von Chance, 6 Juli 2010.

  1. Chance

    Chance Member

    Ich habe mal eine Frage... (mal wieder :D) .
    Mann soll ja nicht über andere Programmierweisen lästern ^^ . Also macht man sich schlau.

    Was ist besser ?

    1. Eine Methode in einer Klasse, die eine Funktion ausführt.
    2. Eine Klasse, die eine Funktion ausführt.
    3. Eine prozedurale Funktion.

    Diese Funktion / Methode / Klasse, wird in der Schleife ca. 50x aufgerufen.

    Ich meine 3 ist am schnellsten, gefolgt von 1.
    Aber Kollege meint 2.
     
  2. rellek

    rellek relativ sensationell Mitarbeiter

    Bei Sprachen kann man einfach nicht von "was ist besser" reden.
    Würdest du den Code kompillieren und deassemblieren, würde von der Länge her das prozedurale gewinnen.

    Dafür haben Klassen grosse Vorteile bei der Bedienbarkeit, weil ein
    PHP:
    $postid = new post($author$title$messagetime(), $threadid);
    eben einfacher ist als ein
    PHP:
    $res mysql_query("INSERT INTO beitraege VALUES (NULL, '".addslashes($author)."', '".addslashes($title)."', '".addslashes($message)."', '".time()."', '".intval($threadid)."')"$mysql_connection);
    $postid mysql_insert_id($mysql_connection);
    add_to_searchindex($postid);
    $res mysql_query("UPDATE foren SET lastpostid = '".$postid."' WHERE boardid = ".intval($boardid)." LIMIT 1"$mysql_connection);
    //even more?
    Zurück zur Frage. Variante 2 gibt es gar nicht. Wenn du nämlich das machst:
    PHP:
    <?php

    class dummy {
        
        echo 
    'haha verarscht!';
        
    }
    ?>
    bekommst du sowas:
    PHP:
    Parse errorsyntax errorunexpected T_ECHOexpecting T_FUNCTION in [path]/filename.php on line 5
    Und Variante 1 hat eine Reihe von Vorteilen. Damit kann man nämlich für Programmierer die Aufrufe von Funktionen gleich halten (z.b. das allseits bekannte und gleichermassen beliebte $db->query($query); ), denn ob es sich um eine MySQL, MySQLi, Oracle oder MSSQL-Datenbank handelt, wird inkl dem Rattenschwanz (Verbindung aufbauen und halten, dafür sorgen, dass insert-IDs zur Verfügung stehen) erstens woanders entschieden und zweitens von der Klasse übernommen. Das ist in manchen Situationen ein grosser Vorteil, denn auch durch die Vererbung kann man Methoden überschreiben. Durch die Sichtbarkeiten muss man nichts globalisieren und und und.

    Also an der Stelle auf Gedeih und Verderb nur über die nakte Geschwindigkeit zu reden, ist der Grundstein für ein Scheitern der Software. Man muss da immer miese Kompromisse eingehen, sei es bei der Erweiterbarkeit (im Vergleich zum Ausgangsquellcode) oder bei der Übersichtlichkeit. Stichwort Erweiterbarkeit.

    Um dir mal ein Beispiel zu geben.
    Es gibt irgendwo einen Parser.
    Der sieht so aus:
    PHP:
    class parser {
        
        function 
    parse($text) {
            return 
    str_replace('[b]''[b]'str_replace('[/b]''[/b]'$text));
        }
        
    }
    Folgerichtig ist der Aufruf dann irgendwo der hier:
    PHP:
    echo $parser->parse('Heute ist ein [b]regnerischer[/b] [u]Tag[/u]');
    Nun stellt ein Programmierer aber fest, dass dieser Parser ziemlich scheisse ist, weil er ja nur fette Schrift kann. Und schreibt sich eine eigene Klasse.
    PHP:
    class bessererParser extends parser {
        
        function 
    parse($text) {
            
    $text $preg_replace('/\[b\](.+)\[\/b\]/is''[b]$1[/b]');
            
    $text $preg_replace('/\[i\](.+)\[\/i\]/is''[i]$1[/i]');
            
    $text $preg_replace('/\[u\](.+)\[\/u\]/is''<u>$1</u>');
            
    //usw
            
    return $text;
        }
        
    }
    Diese ist krass überlegen, denn sie kann nicht nur einen BBCode, sondern interessiert sich nichtmal für Gross-Kleinschreibung im BBCode-Tag. Nun sagt der Programmierer:
    PHP:
    $parser = new bessererParser;
    Und ab dem Moment steht der neue Parser zur Verfügung. Der Aufruf hingegen ist genau derselbe geblieben:
    PHP:
    echo $parser->parse('Heute ist ein [b]regnerischer[/b] [u]Tag[/u]');
    Mach das mal prozedural. Also dass die Aufrufe identisch bleiben, obwohl der Code komplett ausgetauscht wurde.

    Das ist bei einem grösseren Projekt einfach von Vorteil, weil man sich die Schnittstelle, mit der man zu tun hat, vorher überlegen kann. Die ändert sich dann auch nicht mehr. Da der Code, wenn man das korrekt beherrscht, generell fehlerfreier ist, was ein grosser Vorteil ist, sollte man zugunsten der Geschwindigkeit nicht unbedingt mit der Brechstange jedes mögliche Byte einsparen.
     
  3. Chance

    Chance Member

    Also $db->query($query) meine ich mit Beispiel 1.

    Mit Beispiel 3 meine ich sowas wie:
    Code:
    function test { echo 'Test'};
    test();
    Und mit Beispiel 2:

    Code:
    class x extends xx {
    
    	public function __construct($x, $xx) {
    		if (!intval($x)) {
    			throw new ErrorException('Die Zahlweise muss vom Typ integer sein.');
    		}
    		$sql = "
    			SELECT
    				xxx
    			FROM
    				iak_xxxx
    			WHERE
    				xx = :xx 	AND
    				x= :x
    			LIMIT
    				1
    		";
    		try {
    			$stmt = Core::getVusDB()->prepare($sql);
    			$stmt->execute(array(
    				':x' => $x,
    				':xx' => $xx
    			));
    
    			$row = $stmt->fetch(PDO::FETCH_ASSOC);
    		} catch (PDOException $e) {
    			// @todo error_handler erweitern und RuntimeExceptions automatisch abfangen
    			throw new ErrorException($e->getMessage(), (int) $e->getCode());
    		}
    
    		parent::__construct($row);
    	}
    }
    Entfremdet natürlich.
    Und die Klasse wird dann ca. 50x erzeugt...

    ----


    Aber ich verstehe die Vorteile von OOP, das man es gut erweitern kann und die Geschwindigkeit eigentlich auch ok ist.
    Und das es schwierig ist, mit Prozeduraler Programmierung was ähnliches hinzubekommen.
    Schon lange.

    Mir geht es nur darum, ob Variante 2 die langsammste ist, nicht um die Erweiterbarkeit etc.
     
  4. rellek

    rellek relativ sensationell Mitarbeiter

    Also da der Construktor sowieso beim Erstellen einer Klasse aufgerufen wird, ist das denk ich kaum ein Unterschied zur Funktion. Allenfalls in der Menge des belegten Speichers.

    Wobei ich sag, dass Variante 1 und 2 nach deiner Definition so ziemlich das gleiche ist. Weil wenn man das mit gleichen Mitteln nachmessen wollen würde (alle "3" Varianten), dann wäre der Construktor diejenige Methode, die aufgerufen werden müsste und den Code enthält, der letztlich das tut, was man messen will.
     
  5. Chance

    Chance Member

    Also ist kein signifikanter Unterschied zwischen eins und zwei vorhanden ?
     
  6. rellek

    rellek relativ sensationell Mitarbeiter

    Wenn man alle 3 fair miteinander vergleichen wollte, wäre 1. und 2. identisch. Sonst müssten wir 3. umbauen und wenn man nur nach dem Stack schaut, ist dann sogar 2. im Vorteil.


    PHP:
    // variante 1
    class nr1 {
        
        function 
    __constructor() {
            
    // macht nix.
        
    }
        
        public static function 
    doSomething() {
            echo 
    'Ich mach ja schon.';
        }
        
    }


    new 
    nr1;
    nr1::doSomething();
    PHP:
    // variante 2
    class nr2 {
        
        function 
    __constructor() {
            echo 
    'Ich mach ja schon.';
        }
        
    }


    new 
    nr1;
    PHP:
    // variante 3
    function nr3a() {
        
    nr3b();
    }

    function 
    nr3b() {
        echo 
    'ich mach ja schon.';
    }


    nr3a();
    Wenn du dir den Quellcode anschaust, stellst du fest, dass wir bei Nr. 2 nur eine Ebene haben und das ist die Konstruktion der Klasse. OK, dies hat zur Folge dass der Construktor ausgeführt wird und dann kommt das echo. Haben wir also rein von Zahlen her 3 Ebenen bis das Echo ausgegeben wird (Klasse, __constructor(), echo).

    Bei Nr 1 erstellen wir die Klasse und rufen dann die Methode auf. Das ist noch ein halbwegs effizienter Weg, weil ich die Methode static gemacht habe, also haben wir sie nur einmal im RAM, wir brauchen kein Objekt und gar nichts. Dennoch ist der Speicherverbrauch höher, denn die Methode lungert ja auch im RAM herum.

    In diesem Vergleich sieht Nr. 3 (ohne Klassen) sogar richtig schlecht aus. Wir haben 2 Funktionen im RAM und im Stack haben wir ausserdem 2 Rücksprungadressen.

    Also im Detail müsste man das messen, aber bei so billigen Überlegungen sollten die 3 Varianten gleich auf liegen. Ggf. könnte bei einer ausreichend hohen Wiederholung der Versuche die Constructor-Geschichte der Methoden-Variante leicht überlegen sein und weil keine Klassen dabei sind, dürfte das prozedurale weniger RAM brauchen. Aber ob es davon automatisch schneller wird, glaube ich fast nicht.
     
  7. Chance

    Chance Member

    Hmmm.... wenn 2 noch zusätliche Methoden bekommt, dann ist der Vorteil weg, oder ?
     
  8. rellek

    rellek relativ sensationell Mitarbeiter

    Dann müsste man den Testcase erweitern, sodass das wieder miteinander vergleichbar wird.
     
  9. Chance

    Chance Member

    Und im Beipsiel 2 wird die SQL Anfrage immer wieder ausgeführt, auch bei gleichen Anfragen.
    Ein Cache im Speicher mit einem Array in einem static wie bei prozeduralen Funktionenist auch nicht möglich, oder ?
     
  10. rellek

    rellek relativ sensationell Mitarbeiter

    Das static bezieht sich nur auf die Methode selbst, nicht auf das, was anderer Funktionen in dieser Methode machen.
     
  11. Chance

    Chance Member

    Dacht ich mir.
     

Diese Seite empfehlen

  1. Diese Seite verwendet Cookies, um Inhalte zu personalisieren, diese deinem Erleben anzupassen und dich nach der Registrierung angemeldet zu halten.
    Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies.
    Information ausblenden