Tutorial Pasukan Lucee MY

Komponen CFML: apa itu .cfc dan bila patut guna

Fahami komponen CFML: beza .cfc dan .cfm, pemboleh ubah instance, kaedah peribadi, sempadan akses, dan bila komponen tidak berbaloi digunakan.

Komponen CFML: apa itu .cfc dan bila patut guna
0
Kandungan

Komponen CFML: apa itu .cfc dan bila patut guna

Fail .cfm mudah difahami: ia templat, ia dicetak, ia habis. Masalah timbul apabila kod yang sama perlu digunakan di tiga tempat, dan setiap tempat mula menyalin tampal logik yang sama. Bila satu pepijat ditemui, ia perlu dibaiki di tiga tempat, dan biasanya satu tercicir.

Komponen CFML, iaitu fail .cfc, menyelesaikan masalah itu. Ia membungkus data dan fungsi dalam satu unit yang boleh dipanggil berulang kali, diuji secara berasingan, dan diganti tanpa menyentuh templat.

Artikel ini bahagian pertama daripada siri komponen. Kita mulakan dengan soalan asas: apa sebenarnya .cfc, bagaimana ia berkelakuan, dan bila ia tidak berbaloi.

Apa yang membezakan .cfc daripada .cfm

Dari luar, kedua-duanya fail CFML. Perbezaannya pada cara enjin memperlakukannya.

  • .cfm dijalankan sebagai templat. Outputnya dihantar ke pelayar. Ia boleh mengandungi HTML, <cfquery>, dan kod yang bercampur.
  • .cfc dimuatkan sebagai objek. Ia tidak mencetak apa-apa kecuali anda meminta. Ia mengandungi fungsi dan data, dan setiap panggilan melalui nama fungsi.

Perbezaan ini penting kerana ia mengubah cara anda berfikir. Dalam .cfm, anda menulis urutan arahan. Dalam .cfc, anda menulis keupayaan yang boleh dipanggil dari mana-mana.

Satu salah faham biasa: .cfc bukan "fail include yang lebih moden". Ia objek sebenar dengan keadaan sendiri. Dua objek daripada komponen yang sama tidak berkongsi data, dan itu yang kita buktikan di bawah.

Komponen paling kecil yang berguna

Contoh di bawah mengira harga termasuk cukai. Ia kecil, tetapi memenuhi tiga ciri komponen yang sebenar: ada keadaan awal, ada kaedah awam, dan ada kaedah peribadi.

component output="false" {

    // Pemboleh ubah instance: kekal sepanjang hayat objek.
    variables.kadarCukai = 0.06;

    public any function init(numeric kadarCukai = 0.06) {
        variables.kadarCukai = arguments.kadarCukai;
        return this;
    }

    public numeric function hargaTermasukCukai(required numeric harga) {
        var cukai = arguments.harga * variables.kadarCukai;
        return round((arguments.harga + cukai) * 100) / 100;
    }

    public string function ringkasan(required numeric harga) {
        var jumlah = hargaTermasukCukai(arguments.harga);
        return "RM" & numberFormat(arguments.harga, "0.00") & " + cukai = RM" & numberFormat(jumlah, "0.00");
    }

    private numeric function kiraCukai(required numeric harga) {
        return arguments.harga * variables.kadarCukai;
    }
}

Perhatikan output="false". Tanpa itu, ruang kosong di luar blok fungsi boleh tercetak ke respons. Pada aplikasi yang membina JSON atau PDF, satu baris kosong sudah cukup merosakkan output.

Pemboleh ubah instance dan objek yang berasingan

variables.kadarCukai ialah keadaan objek. Ia ditetapkan semasa init() dan kekal untuk objek itu sahaja.

Kod ujian di bawah membina tiga objek dengan kadar berbeza dan membandingkan hasilnya.

h  = new komponen.Harga(0.06);
h2 = new komponen.Harga(0.08);
h3 = new komponen.Harga(0.10);

writeOutput(h.hargaTermasukCukai(100));   // 106
writeOutput(h2.hargaTermasukCukai(100));  // 108
writeOutput(h3.hargaTermasukCukai(50));   // 55
writeOutput(h.hargaTermasukCukai(50));    // 53

Hasil sebenar pada Lucee 7.1.1.17: 106, 108, 55, 53. Objek ketiga tidak mengubah objek pertama, walaupun ketiga-tiganya daripada komponen yang sama.

Ini punca kesilapan paling mahal dalam komponen: menyimpan data pengguna dalam variables pada komponen yang disimpan dalam application scope. Semua pengguna akan berkongsi nilai yang sama. Peraturan ringkasnya, keadaan milik satu permintaan harus berada dalam local atau argumen, bukan variables.

Kaedah peribadi dan sempadan akses

Kata kunci private bukan hiasan. Ia menutup kaedah daripada panggilan luar, dan itu yang membolehkan anda menukar dalaman komponen tanpa memecahkan pemanggil.

try {
    h.kiraCukai(100);
    writeOutput("boleh dipanggil");
} catch (any e) {
    writeOutput(e.message);
}

Output sebenar: Component [komponen.Harga] has no function with name [kiraCukai].

Mesej itu kelihatan mengelirukan pada mulanya. Enjin tidak mengatakan "private", ia hanya mengatakan fungsi itu tiada. Sebabnya, dari sudut pemanggil, kaedah peribadi memang tidak wujud dalam kontrak awam komponen.

Bagaimana enjin mencari komponen

Apabila anda menulis new app.services.HargaService(), Lucee menukar titik kepada pemisah direktori dan mencari app/services/HargaService.cfc relatif kepada root aplikasi. Nama fail mesti sepadan dengan huruf sebenar pada cakera; pada Linux, HargaService.cfc dan hargaservice.cfc ialah dua fail berbeza.

Jika komponen berada di luar root, daftarkan pemetaan dalam Application.cfc:

this.mappings["/kongsi"] = expandPath("../kongsi");

Selepas itu new kongsi.Harga(0.06) akan mencari /kongsi/Harga.cfc. Pemetaan diselesaikan sekali semasa aplikasi dimulakan, jadi perubahan pada Application.cfc memerlukan aplikasi dimulakan semula.

Kesilapan yang sering berlaku: komponen diuji dalam rig yang rootnya berbeza, jadi laluan yang berfungsi pada mesin pembangun gagal pada pelayan. Semak root aplikasi sebelum menyalahkan kod.

Bila komponen TIDAK berbaloi

Komponen bukan jawapan untuk semua kod. Tiga keadaan di mana ia menambah kerja tanpa pulangan:

  • Satu templat, satu kegunaan, tiada ujian. Fail .cfm lebih pendek dan lebih jelas
  • Logik yang hanya menyusun HTML. Itu kerja templat, bukan objek
  • Skrip sekali jalan, contohnya pembersihan data. Komponen menambah lapisan tanpa memberi faedah

Peraturan praktikal: apabila kod yang sama muncul kali kedua, dan kali ketiga dijangka, baru jadikan komponen. Sebelum itu, salinan kedua masih lebih murah daripada abstraksi yang salah.

Komponen atau fungsi?

Bukan semua logik perlu menjadi objek. Fungsi biasa dalam .cfm atau dalam komponen utiliti selalunya cukup, dan lebih murah untuk diuji.

Gunakan fungsi apabila:

  • tiada keadaan yang perlu disimpan antara panggilan
  • logiknya pendek dan berdiri sendiri
  • hanya satu atau dua tempat memanggilnya

Gunakan komponen apabila:

  • ada keadaan awal, contohnya kadar cukai atau konfigurasi
  • beberapa kaedah berkongsi data yang sama
  • anda perlu menggantikannya dengan pelaksanaan lain semasa ujian

Peralihan yang sihat berlaku begini: fungsi dahulu, kemudian apabila keadaan mula menempel pada argumen, barulah bungkus menjadi komponen.

Kos prestasi yang diukur

Komponen sering dituduh lambat. Angka sebenar pada Lucee 7.1.1.17 menunjukkan sebaliknya untuk kegunaan biasa.

  • 1,000 kali membina objek: 10 hingga 27 milisaat
  • 1,000 kali memanggil kaedah pada objek sedia ada: 2 hingga 4 milisaat
  • 1,000 kali memanggil fungsi biasa: 2 hingga 3 milisaat

Maksudnya, membina objek sekitar 10 hingga 27 mikrosaat setiap satu, manakala panggilan kaedah hampir sama kos dengan panggilan fungsi. Untuk permintaan web biasa yang memanggil beberapa kaedah, perbezaan itu tidak dapat dikesan.

Yang benar-benar mahal bukan komponen, tetapi membina objek di dalam gelung untuk setiap baris data, atau memuatkan semula komponen besar pada setiap permintaan tanpa cache.

Corak: komponen sebagai perkhidmatan

Dalam aplikasi yang lebih besar, komponen jarang berdiri sendiri. Ia disusun sebagai perkhidmatan, satu komponen bagi satu tanggungjawab, dan templat hanya memanggilnya.

// dalam pengendali
var harga = new app.services.HargaService(kadarCukai = 0.08);
var jumlah = harga.hargaTermasukCukai(form.harga);

Struktur ini membolehkan tiga perkara yang sukar dilakukan dengan .cfm sahaja: menguji logik tanpa pelayar, menukar kadar cukai mengikut konfigurasi, dan menggunakan semula logik yang sama dalam kerja berjadual.

Lima kesilapan lazim

  1. Meletakkan keadaan pengguna dalam variables pada komponen yang dikongsi. Semua pengguna melihat data yang sama
  2. Terlupa output="false", lalu ruang kosong masuk ke respons JSON
  3. Memanggil kaedah peribadi dari luar, kemudian menyangka komponen rosak kerana mesejnya menyebut "no function"
  4. Membina komponen 800 baris yang mengendalikan pangkalan data, e-mel dan pemformatan sekali. Ia menjadi lebih sukar diuji daripada .cfm asal
  5. Tidak menetapkan nilai awal dalam init(), jadi hasil bergantung kepada urutan panggilan

Soalan lazim

Adakah new lebih baik daripada createObject?

Untuk komponen dalam aplikasi yang sama, new app.services.HargaService() lebih pendek dan membolehkan laluan argumen bernama. createObject("component", ...) masih berguna apabila laluan dibina secara dinamik.

Bolehkah komponen menyimpan sambungan pangkalan data?

Boleh, tetapi jangan simpan objek sambungan dalam variables komponen yang dikongsi. Gunakan queryExecute dengan datasource bernama supaya enjin menguruskan kolam sambungan.

Perlukah setiap komponen ada init()?

Tidak. Komponen tanpa keadaan tidak memerlukan init(). Tetapi jika ada nilai awal, letakkan dalam init() dan bukan pada peringkat atas fail, supaya niatnya jelas.

Bagaimana menguji komponen tanpa pelayar?

Panggil dari skrip CLI atau dari halaman ujian dalam rig, seperti yang digunakan untuk artikel ini. Setiap output dalam artikel ini ialah hasil sebenar daripada Lucee 7.1.1.17, bukan andaian.

Bolehkah satu komponen mewarisi komponen lain?

Boleh, menggunakan extends. Anak mewarisi semua kaedah awam dan peribadi ibu bapa, dan boleh menindih kaedah tertentu. Bahagian keempat siri ini membincangkan bila warisan membantu dan bila ia hanya menambah kerumitan.

Adakah komponen melindungi daripada suntikan SQL?

Tidak. Komponen tidak menyemak input secara automatik. Perlindungan datang daripada cfqueryparam atau parameter bernama dalam queryExecute, sama seperti dalam .cfm. Komponen hanya mengelompokkan kod; ia bukan lapisan keselamatan dengan sendirinya.

Siri ini diteruskan dengan kaedah, argumen dan nilai pulangan. Kalau anda baru bermula, artikel Fungsi dan Komponen (CFC) dalam CFML memberi latar yang lebih asas.

Log masuk untuk menyimpan kemajuan bacaan anda.

0 komen

0 komen

Log masuk untuk menjawab atau memberi komen.

Log masuk