Tutorial Pasukan Lucee MY

Warisan dan antara muka dalam komponen CFML

Warisan komponen CFML: extends, super, menindih kaedah, had protected dalam Lucee, dan cara antara muka menguatkuasakan kontrak.

Warisan dan antara muka dalam komponen CFML
0
Kandungan

Warisan dan antara muka dalam komponen CFML

Bahagian keempat siri komponen. Setakat ini kita bina komponen tunggal, kemudian mengurus keadaan dan skop. Bahagian ini tentang hubungan antara komponen: satu mewarisi yang lain, dan satu mematuhi kontrak yang ditetapkan antara muka.

Kedua-duanya berkuasa, dan kedua-duanya mudah disalah guna. Artikel ini menunjukkan cara ia berkelakuan dalam Lucee 7.1.1.17 dengan output sebenar, termasuk satu had yang tidak didokumenkan dengan jelas: protected tidak diterima dalam cfscript.

extends: satu komponen meluaskan yang lain

Komponen asas menyediakan tingkah laku lalai.

component output="false" {
    public string function nama() {
        return "Alat asas";
    }
    public numeric function caj(required numeric jumlah) {
        return arguments.jumlah;
    }
}

Komponen anak mewarisinya dan menindih dua kaedah.

component extends="komponen.AlatBayar" output="false" {
    public string function nama() {
        return "Kad kredit";
    }
    public numeric function caj(required numeric jumlah) {
        return super.caj(arguments.jumlah) * 1.029;
    }
}

Hasil ujian:

  • Alat asas dengan caj 100 memberi 100
  • Kad kredit dengan caj 100 memberi 102.900
  • isInstanceOf(k, "komponen.AlatBayar") memberi true

Baris terakhir itu penting. Anak ialah ibu bapa dari sudut jenis. Mana-mana kod yang menerima AlatBayar akan menerima BayarKad juga, dan itu asas kepada polimorfisme.

super: memanggil versi ibu bapa

super.caj() memanggil pelaksanaan ibu bapa, bukan versi anak. Tanpa super, menulis semula logik yang sama dalam anak akan menyebabkan dua salinan, dan satu daripadanya akan ketinggalan apabila peraturan berubah.

Gunakan super apabila anak hanya menambah sesuatu pada tingkah laku asas. Jangan gunakan super apabila anak sepatutnya menggantikan tingkah laku itu sepenuhnya; dalam kes itu, menindih tanpa super lebih jelas.

Bila menindih menjadi masalah

Menindih kaedah kelihatan seperti ciri, tetapi ia mengikat anak kepada dalaman ibu bapa.

Jika ibu bapa menukar maksud caj() daripada "jumlah asas" kepada "jumlah selepas diskaun", anak yang memanggil super.caj() akan mewarisi perubahan itu tanpa disedari. Ujian pada anak mungkin masih lulus kerana ia menguji angka, bukan maksud.

Petua praktikal: dokumenkan kontrak setiap kaedah yang direka untuk ditindih, dan elakkan menindih kaedah yang mula-mula ditulis sebagai helper dalaman.

protected tidak diterima dalam cfscript Lucee

Dalam banyak bahasa, protected bermaksud "boleh dilihat oleh anak". Dalam Lucee 7.1, menulisnya dalam komponen cfscript menghasilkan ralat sintaks:

invalid syntax, at [8:34] in [    protected string function log(required string mesej) {]

Yang tersedia ialah public, private, package dan remote. Untuk perkongsian dalam keluarga komponen, dua pilihan yang berfungsi:

  • private: hanya boleh dipanggil dari dalam komponen itu sendiri, termasuk dari kaedah lain dalam fail yang sama
  • package: boleh dilihat oleh komponen dalam pakej yang sama

Jika anda perlukan anak memanggil logik ibu bapa, jadikan kaedah itu public dan dokumentasikan sebagai sebahagian kontrak, atau letakkan logik itu dalam komponen pembantu berasingan yang kedua-duanya panggil.

implements: kontrak yang dikuatkuasakan

Antara muka menetapkan kaedah yang mesti ada, tanpa memberi pelaksanaan.

interface {
    public numeric function caj(required numeric jumlah);
    public string function nama();
}

Komponen yang mematuhinya menulis implements.

component implements="komponen.KaedahBayaran" output="false" {
    public numeric function caj(required numeric jumlah) {
        return arguments.jumlah;
    }
    public string function nama() {
        return "Tunai";
    }
}

Jika satu kaedah tertinggal, enjin menolak komponen itu sebelum ia digunakan:

component [komponen.BayarRosak] does not implement the function [nama()] of the abstract component/interface

Ini berbeza daripada warisan. Warisan memberi kod; antara muka memberi janji. Kod yang menerima KaedahBayaran tahu bahawa caj() dan nama() pasti ada, tanpa perlu tahu kelas sebenar.

Dua ujian yang menangkap kesilapan warisan

Ujian pertama memastikan anak masih memenuhi janji ibu bapa. Ujian kedua memastikan anak tidak bergantung pada dalaman yang tidak dijanjikan.

it("menerima anak di tempat ibu bapa", function () {
    var alat = new komponen.BayarKad();
    expect(alat).toBeInstanceOf("komponen.AlatBayar");
    expect(alat.caj(100)).toBeGT(100);
});

Ujian jenis itu penting kerana ia menguji kontrak, bukan angka. Jika suatu hari BayarKad berhenti mewarisi AlatBayar, ujian itu gagal serta-merta, walaupun kedua-dua kelas masih berfungsi sendiri.

Ujian kedua lebih halus: panggil anak melalui antara muka, bukan melalui kelas sebenar.

var kaedah = new komponen.BayarTunai();
expect(kaedah.nama()).toBe("Tunai");

Jika kod lain hanya bergantung pada KaedahBayaran, ujian ini membuktikan bahawa penggantian pelaksanaan tidak memecahkan pemanggil.

Warisan atau komposisi

Warisan sesuai apabila hubungannya benar-benar "ialah": kad kredit ialah sejenis alat bayaran. Ia tidak sesuai apabila hubungannya hanya "menggunakan": laporan menggunakan kalkulator cukai, tetapi laporan bukan kalkulator.

Untuk hubungan "menggunakan", komposisi lebih selamat. Komponen menyimpan komponen lain dalam variables dan memanggilnya.

component output="false" {
    public any function init() {
        variables.kalkulator = new komponen.Cukai();
        return this;
    }
    public numeric function jumlah(required numeric harga) {
        return arguments.harga + variables.kalkulator.kira(arguments.harga);
    }
}

Komposisi mengelakkan rantai warisan yang panjang, dan ia lebih mudah diuji kerana setiap bahagian boleh diganti.

Perubahan pada ibu bapa sentiasa berisiko kepada anak. Tiga tabiat mengurangkan risiko:

  1. Tambah kaedah baharu, jangan ubah maksud kaedah sedia ada
  2. Jika maksud perlu berubah, tambah kaedah baharu dengan nama berbeza dan tinggalkan yang lama sebagai pemanggil nipis
  3. Jalankan ujian anak selepas setiap perubahan ibu bapa, bukan hanya ujian ibu bapa

Tabiat ketiga yang paling kerap dilupakan. Dalam projek kecil, ujian ibu bapa lulus dan ujian anak tidak dijalankan, lalu pepijat muncul di tempat yang tidak dijangka.

Tanda amaran dalam hierarki komponen

  • anak yang menindih hampir semua kaedah ibu bapa; mungkin hubungannya bukan warisan
  • ibu bapa yang menyimpan keadaan yang dikongsi semua anak
  • rantaian extends lebih daripada dua peringkat tanpa sebab jelas
  • antara muka dengan satu kaedah yang tidak pernah digunakan sebagai jenis

Antara muka sebagai titik sambungan

Kegunaan antara muka yang paling praktikal bukan dalam kod aplikasi harian, tetapi pada titik sambungan: tempat anda menjangka pelaksanaan berbeza pada masa hadapan.

Contohnya, sistem invois perlu menghantar notifikasi melalui e-mel, kemudian melalui WhatsApp. Jika kod memanggil new EmailNotifier() secara langsung, setiap saluran baharu menuntut perubahan pada pemanggil.

Dengan antara muka Notifier yang mempunyai kaedah hantar(penerima, mesej), pemanggil tidak berubah apabila saluran baharu ditambah. Ia hanya menerima objek yang mematuhi kontrak itu.

component output="false" {
    public any function init(required any notifier) {
        variables.notifier = arguments.notifier;
        return this;
    }
    public void function maklumkan(required string emel, required string mesej) {
        variables.notifier.hantar(arguments.emel, arguments.mesej);
    }
}

Komponen di atas tidak tahu sama ada notifier itu e-mel atau WhatsApp, dan itu tujuannya. Ia juga mudah diuji: berikan notifier palsu yang merekod panggilan, tanpa menghantar apa-apa.

Soalan lazim

Bolehkah satu komponen melaksanakan lebih daripada satu antara muka?

Boleh. Senaraikan dengan koma dalam atribut implements. Setiap antara muka mesti dipenuhi sepenuhnya.

Adakah antara muka menyokong sifat (property)?

Tidak dalam bentuk yang sama seperti kelas. Antara muka menyenaraikan kaedah; data dikendalikan melalui kaedah pengakses jika perlu.

Bolehkah anak mengakses variables ibu bapa?

Tidak secara langsung. variables milik setiap objek. Untuk berkongsi data, gunakan kaedah awam atau hantar nilai sebagai argumen.

Mengapa super saya gagal?

Biasanya kerana kaedah itu tiada dalam ibu bapa, atau namanya berbeza huruf. Semak senarai kaedah ibu bapa sebelum menyalahkan enjin.

Bahagian kelima, dan terakhir, menunjukkan cara komponen disusun sebagai perkhidmatan dalam aplikasi sebenar, termasuk ujian dengan TestBox. Bahagian pertama siri ini ada di Komponen CFML: apa itu .cfc.

Log masuk untuk menyimpan kemajuan bacaan anda.

0 komen

0 komen

Log masuk untuk menjawab atau memberi komen.

Log masuk