Johnny Hogenbirk: Artikelen, code-snippets, etc.


Home

Weg met classes - 07-02-2020


Inleiding

Degene die software gaat leren ontwikkelen, zal vaak eerst leren omgaan met functions, daarna met classes.
Die overgang naar classes is dan best lastig. Het kost tijd. Dat zou niet erg zijn als het nut zou hebben met classes te werken.
Maar dat nut is er niet. Althans niet voor de meeste talen, zoals PHP en Python (zie ook de note hieronder). Ik weet dat ik veel ervaren ontwikkelaars keihard voor de schenen trap. Maar, van ontwikkelaars verwacht je (en ik ook) veel leervermogen, dus ik denk dat ook zij nu verder lezen.
In dit artikel lees je dus een onderbouwing van de stelling dat classes niet nodig zijn. Inclusief vergelijk van code, met en zonder classes.

Note 1: Soms wordt in leerstof gesproken over 'Objectgeoriënteerd programmeren' of 'Object Oriented Programming'. Ook tref je vaak de afkorting OO, die dus voor Object Oriented staat. Een class is een blauwdruk voor objecten, vandaar het verband tussen de benamingen classes en OO. Note 2: ik ga qua termen en code uit van PHP, maar het gedachtengoed geldt voor veel talen. Behalve dan de pure OO-talen, zoals Java. Het is soms wel wat lastig: als je je verdiept in Javascript, lees over de objecten in Javascript. Bijna alles is een object, behalve null en undefined. Je kunt in Javascript daarnaast ook zelf functies en classes aanmaken. En daarover gaat dus dit artikel, niet over de standaard aanwezige objecten in Javascript. Zie ook de heerlijk verwarring op WikiPedia-Classes


Wat zou het nut moeten zijn?

Als je artikelen/blogs op het web leest over het waarom van classes, drijft veelal één reden naar boven: het groeperen van functies, zodat je code netjes/overzichtelijk/beheerbaar blijft.
Zie evt. WikiPedia Objectgeoriënteerd: 'Het dient uitsluitend om programma's beter te structureren, met het oog op de overzichtelijkheid, onderhoudbaarheid en herbruikbaarheid van componenten.'
Wel grappig, het woord 'uitsluitend'. Daar is niet iedereen het mee eens. Je leest ook wel het argument dat de echte wereld ook uit objecten bestaat en code die ook objectgeoriënteerd is opgezet daardoor veel begrijpelijker is.


Vergelijking

Voor de vergelijking heb ik twee stukken code geschreven. Heel basic en simpel, maar met wel verschillende veel voorkomende gebruiksmogelijkheden van classes.
Eerst de code met classes:
class Htmlmaker{
  public $text = "";
  public $font_size = "18px";
  private $font_color = "black";
  protected $font_family = "Arial, Helvetica, sans-serif";

  function __construct($font_color = NULL){
    $this->font_color = $font_color;
  }

  public function p($string_value){
    $this->text .= "<p style=font-size:" . $this->font_size . ";color:" . $this->font_color . ";font-family:" . $this->font_family . ";>" . $string_value . "</p>";<br>
  }

  public function div($string_value){
    $this->text .= "<div style=font-size:" . $this->font_size . ";color:" . $this->font_color . ";font-family:" . $this->font_family . ";>" . $string_value . "</div>";
  }

  public static function input($type, $name){
    return "<input type=$type name=$name>";
  }
}

class HtmlmakerSub extends Htmlmaker{
  public function span($string_value, $font_color){
    $this->text .= "<span style=font-size:" . $this->font_size . ";color:$font_color;font-family:" . $this->font_family . ";>" . $string_value . "</span>";
  }
}

$obj1 = new Htmlmaker();
$obj1->text = "standard font-size: " . $obj1->font_size . "<br>";
$obj1->div("div");
$obj1->p("p");

$obj2 = new Htmlmaker("red");
$obj2->div("div");
$obj2->p("p");

$obj3 = new HtmlmakerSub();
$obj3->span("span", "purple");
$obj3->text .= Htmlmaker::input("hidden", "jo");

echo $obj1->text . "<br>";
echo $obj2->text . "<br>";
echo $obj3->text . "<br>";

Dan de code met functions:
function Htmlmaker_main($font_color = NULL){
  $result["text"] = "";
  $result["font_size"] = "18px";
  $result["font_color"] = "black";
  if($font_color){
    $result["font_color"] = $font_color;
  }
  return $result;
}

function Htmlmaker_protected($arr){
  $arr["font_family"] = "Arial, Helvetica, sans-serif";
  return $arr;
}

function Htmlmaker_p($arr, $string_value){
  $arr = Htmlmaker_protected($arr);
  return "<p style=font-size:" . $arr["font_size"] . ";color:" . $arr["font_color"] . ";font-family:" . $arr["font_family"] . ";>" . $string_value . "</p>";
}

function Htmlmaker_div($arr, $string_value){
  $arr = Htmlmaker_protected($arr);
  return "<div style=font-size:" . $arr["font_size"] . ";color:" . $arr["font_color"] . ";font-family:" . $arr["font_family"] . ";>" . $string_value . "</div>";
}

function Htmlmaker_input($type, $name){
  return "<input type=$type name=$name>";
}

function HtmlmakerSub_span($string_value, $font_color){
  $htlm_object = Htmlmaker_main($font_color);
  $htlm_object = Htmlmaker_protected($htlm_object);
  $htlm_object["text"] .= "<span style=font-size:" . $htlm_object["font_size"] . ";color:$font_color;font-family:" . $htlm_object["font_family"] . ";>" . $string_value . "</span>";
  return $htlm_object;
}

$arr1 = Htmlmaker_main();
$arr1["text"] .= "standard font-size: " . $arr1["font_size"] . "<br>";
$arr1["text"] .= Htmlmaker_div($arr1, "div");
$arr1["text"] .= Htmlmaker_p($arr1, "p");

$arr2 = Htmlmaker_main("red");
$arr2["text"] .= Htmlmaker_div($arr2, "div");
$arr2["text"] .= Htmlmaker_p($arr2, "p");

$arr3 = HtmlmakerSub_span("span", "purple");
$arr3["text"] .= Htmlmaker_input("hidden", "jo");

echo $arr1["text"] . "<br>";
echo $arr2["text"] . "<br>";
echo $arr3["text"] . "<br>";


Toelichting

Het gaat hier niet om de kwaliteit van de code, om punten en komma's, maar om het globale beeld. Het belangrijkste is dat beide exact dezelfde uitkomst hebben!

Ik heb hierboven de meest voorkomende gebruiksmogelijkheden gebruikt. Er zijn zeker nog meer. Maar ik kon niets bedenken dat niet via functions kan. En zoals in de inleiding aangegeven, ik heb ook niet op het web iets gelezen over een unieke functie van classes, die de reden zou zijn om classes te gebruiken.

Even de verschillen en overeenkomsten bij langs.
- Variabelen in de class en construct Zoals aangegeven, variabelen in een class kunnen publiekelijk opvraagbaar zijn, alleen voor de class zijn of ook in andere classes. Als een object wordt aangemaakt, wordt via de __construct evt. een waarde toegekend aan variabele.
Bij functies wordt geen object aangemaakt met new, maar wordt de main functie opgevraagd. Dit is de initialisatie, gelijk als bij __instruct. Het onderscheid tussen publiek en private wordt niet gehanteerd. Het nut ontgaat mij ook. Je bent zelf de baas over de code, waarom mag een functie een waarde van een andere functie niet gebruiken? Hij is keurig opgeborgen in een array.
Bij functies is een soort protected wel nagespeeld, via een functie die alleen door de bijbehorende functies wordt opgeroepen. Het nut hiervan ontgaat me ook, maar goed, het kan.
Bij classes heb je waarden in het object $obj1 opgenomen, bij functies in de array $arr. Beide zitten gewoon in het geheugen. Technisch is er geen verschil.
- Variabelen gebruiken Bij objecten zijn (public) waarden van variabelen op te vragen via $obj1->font_size.
Bij array's zijn waarden van variabelen op te vragen via $arr["font_size"].
Ok, je schrijft het anders, maar wat is het verschil, nix.
- Variabelen vullen Bij objecten zijn (public) waarden van variabelen te vullen via bijv. $obj1->div("div").
Bij array's zijn waarden van variabelen te vullen via bijv. $arr["text"] .= Htmlmaker_div($arr1, "div").
Ok, je schrijft het anders, maar wat is het verschil, nix.
Alhoewel, er is een verschil. Bij array's zie je direct wat je vult. Bij objecten moet je eerst het object raadplegen om het te zien.
- Overerven Een kenmerk van classes is overerving, hierboven dus: HtmlmakerSub extends Htmlmaker. Het nut ontgaat mij. Ik kan me alleen voorstellen dat als je classes van iemand anders hebt gekregen en je wilt die in tact laten (vanwege bijv. updates), dat je dan overerving toepast. Maar, het wordt er minder inzichtelijk van.
Maar, hoe dan ook, je kunt dat met functies ook nabootsen, zie dus: HtmlmakerSub_span.


Conclusie

Je zou classes moeten gebruiken omdat het je code beter structureert. De class groepeert functies.
Maar je kunt net zo goed een verzameling functions in een apart bestand zetten. Door dezelfde beginletters te gebruiken bij alle functies en die beginletters te gebruiken voor de bestandsnaam ('htmlmaker.php'), houd je de functions ook mooi bij elkaar.
De array is feitelijk het object, de groep functies de class.

Nog terugkomend op het af en toe gelezen 2e argument, die 'de wereld bestaat uit objecten'. Da's mooi. De wereld bestaat ook uit processen. Uit materie. Uit functies. Tja, zo ken ik er nog een paar honderd.

Kortom, ik zie geen reden om classes te gebruiken. Als je functions kent, waarom zou je dan iets nieuws leren, zonder dat het nut heeft.


Note: qua performance: ik had verwacht dat het werken met functions het zou winnen van classes.
V.w.b. de aanroep van de static class klopte dat wel, maar slechts met paar procent.
V.w.b. het initieren van het object en de code obj2->div("div"); klopte dat echter niet en wel met 10% tot 20%.
Doorgaans is performance geen issue. Als het wel een kritisch punt is, dan is dit wel op te lossen. Vervang bijv. aanroep van Htmlmaker_protected door define van een constante. Daarna is de functie code weer goed 10% sneller dan de class code.


Meer artikelen hierover lezen? Een paar leuke:
Brian Will
The Wrong Way, deel 'Always use object-oriented Programming'
En meer neutraal artikel: Tomas Engquist, Harpreet Ghotra en Alexander Riccio


Opmerkingen? Ze zijn van harte welkom: email@johnnyhogenbirk.nl!