0%

🏗️ Objektorientētā programmēšana

ES6 klases sniedz skaidru un ērti lasāmu sintaksi objektorientētai programmēšanai JavaScript – mantošana, iekapsulācija un polimorfisms.

javascript

Klases un konstruktors

Teksts
// Klases definīcija
class Dzīvnieks {
    // Lauki (ES2022 – klases lauki)
    #nosaukums;      // Privāts lauks (#)
    #suga;
    veids = "Dzīvnieks"; // Publisks lauks ar noklusējumu

    // Konstruktors – tiek izsaukts ar "new"
    constructor(nosaukums, suga) {
        this.#nosaukums = nosaukums;
        this.#suga = suga;
        this.dzimšanas_gads = new Date().getFullYear();
    }

    // Metodes
    runāt() {
        return `${this.#nosaukums} saka: ${this.skaņa()}`;
    }

    skaņa() {
        return "...";
    }

    // Getter
    get nosaukums() {
        return this.#nosaukums;
    }

    // Setter ar validāciju
    set nosaukums(jaunais) {
        if (typeof jaunais !== "string" || jaunais.trim().length < 1) {
            throw new Error("Nosaukumam jābūt ne-tukšai virknei");
        }
        this.#nosaukums = jaunais.trim();
    }

    // Statiskā metode (uz klases, ne instances)
    static radīt(nosaukums, suga) {
        return new Dzīvnieks(nosaukums, suga);
    }

    toString() {
        return `${this.#suga}: ${this.#nosaukums}`;
    }
}

// Objekta izveide
const suns = new Dzīvnieks("Rex", "Suns");
console.log(suns.nosaukums);        // "Rex" (getter)
suns.nosaukums = "Reks";            // Setter
console.log(suns.runāt());          // "Reks saka: ..."
console.log(`${suns}`);             // "Suns: Reks" (toString)

// Statiskās metodes izsaukums uz klases
const kaķis = Dzīvnieks.radīt("Muris", "Kaķis");

Mantošana (extends)

Teksts
class Suns extends Dzīvnieks {
    #šķirne;

    constructor(nosaukums, šķirne) {
        super(nosaukums, "Suns"); // Obligāts! Izsauc vecākklases konstruktoru
        this.#šķirne = šķirne;
    }

    // Metodes pārrakstīšana (override)
    skaņa() {
        return "Hau!"; // Vecāku metode tagad atgriež "Hau!"
    }

    apraksts() {
        return `${this.nosaukums} ir ${this.#šķirne} šķirnes suns`;
    }
}

class Kaķis extends Dzīvnieks {
    constructor(nosaukums) {
        super(nosaukums, "Kaķis");
    }
    skaņa() { return "Miau!"; }
}

const reks = new Suns("Reks", "Dobermans");
const muris = new Kaķis("Muris");

console.log(reks.runāt());         // "Reks saka: Hau!"
console.log(muris.runāt());        // "Muris saka: Miau!"
console.log(reks.apraksts());      // "Reks ir Dobermans šķirnes suns"
console.log(reks instanceof Suns);     // true
console.log(reks instanceof Dzīvnieks); // true (mantošana)

// ======================================
// Polimorfisms
// ======================================
const dzīvnieki = [
    new Suns("Reks", "Dobermans"),
    new Kaķis("Muris"),
    new Dzīvnieks("Papagailis", "Putns"),
];

dzīvnieki.forEach(d => console.log(d.runāt()));
// "Reks saka: Hau!"
// "Muris saka: Miau!"
// "Papagailis saka: ..."

Privātie lauki un metodes

Teksts
class BankaKonts {
    #atlikums = 0;          // Privāts lauks
    #darījumi = [];

    constructor(īpašnieks, sākotnējā_nauda = 0) {
        this.īpašnieks = īpašnieks;
        if (sākotnējā_nauda > 0) this.#atlikums = sākotnējā_nauda;
    }

    iemaksāt(summa) {
        this.#validētSummu(summa);
        this.#atlikums += summa;
        this.#reģistrētDarījumu("iemaksa", summa);
        return this;  // Method chaining
    }

    izmaksāt(summa) {
        this.#validētSummu(summa);
        if (summa > this.#atlikums) {
            throw new Error("Nepietiek līdzekļu");
        }
        this.#atlikums -= summa;
        this.#reģistrētDarījumu("izmaksa", -summa);
        return this;
    }

    // Privātas metodes (arī ar #)
    #validētSummu(summa) {
        if (typeof summa !== "number" || summa <= 0) {
            throw new Error("Summai jābūt pozitīvam skaitlim");
        }
    }

    #reģistrētDarījumu(tips, summa) {
        this.#darījumi.push({
            tips,
            summa,
            laiks: new Date().toISOString(),
            atlikums: this.#atlikums,
        });
    }

    get atlikums() { return this.#atlikums; }

    vēsture() {
        return [...this.#darījumi]; // Kopija, nevis atsauce
    }
}

const konts = new BankaKonts("Anna", 100);
konts.iemaksāt(50).iemaksāt(25); // Method chaining
konts.izmaksāt(30);

console.log(konts.atlikums);  // 145
// konts.#atlikums            // ❌ SyntaxError: privāts lauks

Statiskās metodes un lauki

Teksts
class Matemātika {
    static PI = 3.14159265358979;    // Statisks lauks

    static aplis_laukums(r) {
        return Matemātika.PI * r ** 2;
    }

    static aplis_perimetrs(r) {
        return 2 * Matemātika.PI * r;
    }

    static #palīgs(x) {  // Privāts statisks
        return Math.round(x * 100) / 100;
    }

    static noapaļot(x) {
        return Matemātika.#palīgs(x);
    }
}

// Izsauc uz klases, ne instances!
console.log(Matemātika.PI);                      // 3.14159...
console.log(Matemātika.aplis_laukums(5));        // 78.54
// const m = new Matemātika();
// m.aplis_laukums // ← Kļūda, statiskās nav uz instancēm

// Singleton modelis
class Konfigurācija {
    static #instance = null;
    #iestatījumi = {};

    static iegūt() {
        if (!Konfigurācija.#instance) {
            Konfigurācija.#instance = new Konfigurācija();
        }
        return Konfigurācija.#instance;
    }

    iestatīt(atslēga, vērtība) {
        this.#iestatījumi[atslēga] = vērtība;
        return this;
    }
    iegūtVērtību(atslēga) { return this.#iestatījumi[atslēga]; }
}

const konf1 = Konfigurācija.iegūt();
const konf2 = Konfigurācija.iegūt();
console.log(konf1 === konf2); // true – viena instance!

Mixins un kompozīcija

Teksts
// Mixin – funkcionalitātes pievienošana bez mantošanas
const Sērijveida = (SuperKlase) => class extends SuperKlase {
    uz_json() {
        return JSON.stringify(this);
    }
    static no_json(json) {
        return Object.assign(new this(), JSON.parse(json));
    }
};

const Validejams = (SuperKlase) => class extends SuperKlase {
    validēt() {
        const kļūdas = [];
        for (const [atslēga, noteikums] of Object.entries(this.validācijas ?? {})) {
            if (!noteikums(this[atslēga])) {
                kļūdas.push(`${atslēga} nav derīgs`);
            }
        }
        return kļūdas;
    }
};

class Lietotājs extends Sērijveida(Validejams(class {})) {
    validācijas = {
        vārds:  v => typeof v === "string" && v.length >= 2,
        email:  e => typeof e === "string" && e.includes("@"),
    };

    constructor(vārds, email) {
        super();
        this.vārds = vārds;
        this.email = email;
    }
}

const l = new Lietotājs("Anna", "anna@piemers.lv");
console.log(l.validēt());  // []
console.log(l.uz_json());  // {"vārds":"Anna","email":"anna@piemers.lv",...}