Rolf B: JavaScript

Beitrag lesen

Hallo ROGA,

nachdem Felix Dich jetzt schon auf die Konstruktoren und die class Syntax gestupst hat, sollte man vielleicht auch entsprechende Beispiele machen.

Denn: Objekte so anzulegen, wie wir das hier bisher getan haben, ist JavaScript von vor 15 Jahren. Heute gelingt das besser mit class Syntax, das kann Node.js auch.

In solchen Fällen ist es aber auch hilfreich, die Klassendefinition in einer eigenen Quellcode-Datei zu haben, und JETZT kommt noch das Modulkonzept von Node.js hinzu. Dumm ist: aus historischen Gründen gibt es 2 solche Konzepte. Aber wenn man sich die IOBroker Doku anschaut, will er das ältere verwenden - CommonJS, mit der require()-Funktion.

// Solar Klasse in solar.js
module.exports = class Solar {
   // das kriegen wir später
}

Das, was Du an module.exports zuweist, wird von der require-Funktion zurückgegeben.

Im Hauptprogramm steht dann nur:

const Solar = require("./solar.js");

const pvDaten = new Solar();
// alles fertig, pvDaten kann verwendet werden

// später: Stand der pvDaten aktualisieren
pvDaten.loadData();

Die Solar-Klasse erstellen wir so, dass sie sich alle Daten selbst holt. Dafür gibt es die constructor-Methode, die JavaScript aufruft, sobald das Objekt mit new erstellt wurde. Man kann beim new-Aufruf auch Parameter angeben, die werden dann dem Konstruktor übergeben. Sowas kann für die Batterie interessant sein, wenn man mehrere davon hat.

// Solar Klasse in solar.js
module.exports = class Solar {
   constructor() {
      this.Batterien = [
         new Batterie(0),
         new Batterie(1)
      ];
      this.PV = new PV();
   }
   loadData() {
      for (let batt of this.Batterien)
         batt.loadData();

      this.PV.loadData();
   }
}

class Batterie {
   constructor(batterieNr) {
      this.myNumber = batterieNr;
      loadData();
   }
   loadData() {
      const prefix = `sun2000.${this.myNumber}.collected.`;
      this.Status = getState(prefix + 'SOC`).val;
      this.LadungEntladung = getState(prefix + 'chargeDischargePower').val;
      this.Dies = getState(...);
      this.Das = getState(...);
    }
}

class PV {
   constructor() {
      this.loadData();
   }

   loadData() {
      this.Leistung = getState('sun2000.generatedPower').val;
         // oder wie auch immer das heißen mag
   }
}

Nur die Klasse Solar wird exportiert, die Klassen Batterie und PV können intern im Modul bleiben, weil Du von diesen Klassen außerhalb des Moduls keine Objekt erstellen musst. Darum kümmert sich die Solar-Klasse.

Die ist so gebaut, dass sie beim Erzeugen die Unterobjekte anlegt. Und DIE sind wiederum so gebaut, dass sie sich beim Erzeugen ihre Daten selbst beschaffen. Für den Update der Daten ruft man auf dem solar-Objekt loadData() auf und der delegiert das an seine Unterobjekte. D.h. wenn Du weitere Unterobjekte ergänzt, musst Du auch loadData erweitern.

Ich habe für die Batterien ein Array gemacht. Falls Du nur eine hast, kannst Du auf das Array verzichten. Wenn es mehrere Batterien gibt, hast Du mehrere Batterie-Objekte und übergibst jedem seine Nummer. Es merkt sie sich in der batterieNr Eigenschaft, so dass dann jedes Batterieobjekt auf seine Batterienummer zugreift.

this steht innerhalb einer Methode für das Objekt, auf dem die Methode aufgerufen wurde.

Ein String, der mit ```-Zeichen (Backticks) gebildet wird, ist ein Template-Literal, ähnlich dem String-Parsing in PHP.

Auf weitere OOP-Feinheiten wie private Eigenschaften oder getter verzichte ich erstmal, sonst sprengt es Dir die Birne vollständig 😉. Abstraktion ist schön und gut, aber als Einsteiger muss man es noch nachvollziehen können.

Rolf

--
sumpsi - posui - obstruxi