Klassenfrage

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.
 

rellek

relativ sensationell
Teammitglied
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, $message, time(), $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 error: syntax error, unexpected T_ECHO, expecting 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.
 

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.
 

rellek

relativ sensationell
Teammitglied
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.
 

rellek

relativ sensationell
Teammitglied
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.
 

rellek

relativ sensationell
Teammitglied
Dann müsste man den Testcase erweitern, sodass das wieder miteinander vergleichbar wird.
 

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 ?
 

rellek

relativ sensationell
Teammitglied
Das static bezieht sich nur auf die Methode selbst, nicht auf das, was anderer Funktionen in dieser Methode machen.
 
Oben