Mengapa menggunakan paket?
Jawabannya sederhana: karena kekuatan paketnya. Paket waktu desain menyederhanakan pelepasan dan pemasangan komponen khusus; paket waktu proses memberikan kekuatan baru ke dalam pemrograman tradisional. Setelah Anda mengkompilasi kode yang dapat digunakan kembali ke dalam perpustakaan runtime, Anda dapat membagikannya ke beberapa aplikasi. Semua aplikasi dapat mengakses komponen standar melalui paket, dan Delphi sendiri yang melakukan hal ini. Karena aplikasi tidak harus menyalin pustaka komponen terpisah ke dalam file yang dapat dieksekusi, hal ini sangat menghemat sumber daya sistem dan ruang disk. Selain itu, paket mengurangi waktu yang dihabiskan untuk kompilasi karena Anda hanya perlu mengkompilasi kode khusus aplikasi.
Jika paket bisa digunakan secara dinamis, maka kita bisa mendapatkan keuntungan lebih. Paket memberikan pendekatan modular baru untuk mengembangkan aplikasi. Terkadang Anda mungkin ingin menjadikan modul tertentu sebagai komponen opsional aplikasi, seperti sistem akuntansi yang dilengkapi dengan modul HR opsional. Dalam beberapa kasus, Anda hanya perlu menginstal aplikasi dasar, sementara dalam kasus lain Anda mungkin perlu menginstal modul HR tambahan. Pendekatan modular ini dapat dengan mudah diimplementasikan melalui teknologi paket. Di masa lalu, hal ini hanya dapat dicapai dengan memuat DLL secara dinamis, namun dengan menggunakan teknologi pengemasan Delphi, Anda dapat "mengemas" setiap jenis modul aplikasi ke dalam bundel. Secara khusus, objek kelas yang dibuat dari paket dimiliki oleh aplikasi dan oleh karena itu dapat berinteraksi dengan objek dalam aplikasi.
Paket dan aplikasi runtime
Banyak pengembang yang hanya menganggap paket Delphi sebagai tempat meletakkan komponen, padahal sebenarnya paket dapat (dan seharusnya) digunakan dalam desain aplikasi modular.
Untuk mendemonstrasikan cara menggunakan paket untuk memodulasi aplikasi Anda, mari buat contoh:
1. Buat program Delphi baru dengan dua form: Form1 dan Form2;
2. Hapus Form2 dari daftar formulir yang dibuat secara otomatis (Proyek | Opsi | Formulir);
3. Tempatkan tombol pada Form1 dan masukkan kode berikut pada event handler OnClick tombol:
dengan TForm2.Create(aplikasi) lakukan
mulai
TampilkanModal;
Bebas;
Akhir;
4. Ingatlah untuk menambahkan Unit2 ke klausa penggunaan Unit1;
5. Simpan dan jalankan proyek.
Kami membuat aplikasi sederhana yang menampilkan formulir dengan tombol yang, ketika diklik, membuat dan menampilkan formulir lain.
Namun apa yang harus kita lakukan jika kita ingin memasukkan Form2 pada contoh di atas ke dalam modul yang dapat digunakan kembali dan membuatnya tetap berfungsi normal?
Jawabannya adalah: Bao!
Untuk membuat paket untuk Form2 memerlukan pekerjaan berikut:
1. Buka Manajer Proyek (Lihat | Manajer Proyek);
2. Klik kanan Grup Proyek dan pilih "Tambahkan Proyek Baru...";
3. Pilih “Paket” di daftar proyek “Baru”;
4. Sekarang Anda seharusnya dapat melihat editor paket;
5. Pilih item “Berisi” dan klik tombol “Tambah”;
6. Kemudian klik tombol "Browse..." dan pilih "Unit2.pas";
7. Paket sekarang harus berisi unit "Unit2.pas";
8. Terakhir simpan dan kompilasi paketnya.
Sekarang kami telah menyelesaikan paketnya. Seharusnya ada file bernama "package1.bpl" di direktori Project/BPL Anda. (BPL adalah singkatan dari Borland Package Library, dan DCP adalah singkatan dari Delphi CompiledPackage.)
Paket ini selesai. Sekarang kita perlu menghidupkan sakelar opsi paket
dan kompilasi ulang aplikasi aslinya.
1. Klik dua kali "Project1.exe" di manajer proyek untuk memilih proyek;
2. Klik kanan dan pilih "Options..." (Anda juga dapat memilih Project | Options... dari menu);
3. Pilih halaman opsi “Paket”;
4. Pilih kotak centang "Bangun dengan paket runtime";
5. Edit kotak edit "Paket Runtime": "Vcl50;Package1" dan klik tombol "OK";
6. Catatan: Jangan hapus Unit2 dari aplikasi;
7. Simpan dan jalankan aplikasi.
Aplikasi akan berjalan seperti sebelumnya, namun perbedaannya terlihat pada ukuran file.
Project1.exe kini hanya berukuran 14K, dibandingkan sebelumnya 293K. Jika Anda menggunakan Resource Browser untuk melihat konten file EXE dan BPL, Anda akan melihat bahwa DFM dan kode Form2 sekarang disimpan dalam paket.
Delphi menyelesaikan penautan statis paket selama kompilasi. (Inilah sebabnya Anda tidak dapat menghapus Unit2 dari proyek EXE.)
Pikirkan tentang apa yang dapat Anda peroleh dari ini: Anda dapat membuat modul akses data dalam paket, dan ketika Anda mengubah aturan akses data (seperti beralih dari koneksi BDE ke koneksi ADO), ubah sedikit dan publikasikan ulang paket tersebut. Alternatifnya, Anda bisa membuat formulir dalam satu paket yang menampilkan pesan "Opsi ini tidak tersedia dalam versi saat ini" dan kemudian membuat formulir yang berfungsi penuh dalam paket lain dengan nama yang sama. Sekarang kami memiliki produk dalam versi "Pro" dan "Perusahaan" tanpa usaha apa pun.
Bongkar muat paket secara dinamis
Dalam kebanyakan kasus, DLL atau BPL yang terhubung secara statis sudah cukup. Namun bagaimana jika kita tidak ingin merilis BPL? "Perpustakaan tautan dinamis Package1.bpl tidak dapat ditemukan di direktori yang ditentukan" adalah satu-satunya pesan yang bisa kita dapatkan sebelum aplikasi dihentikan. Atau, dalam aplikasi modular, bisakah kita menggunakan sejumlah plugin?
Kita perlu terhubung secara dinamis ke BPL saat runtime.
Untuk DLL, ada cara sederhana yaitu menggunakan fungsi LoadLibrary:
fungsi LoadLibrary(lpLibFileName: Pchar): HMODULE;stdcall;
Setelah memuat DLL, kita dapat menggunakan fungsi GetProcAddress untuk memanggil fungsi dan metode DLL yang diekspor:
fungsi GetProcAddress(hModule: HMODULE; lpProcName:LPCSTR): FARPROC;
Terakhir, kami menggunakan FreeLibrary untuk menghapus instalasi DLL:
fungsi FreeLibrary(hLibModule: HMODULE): BOOL;stdcall;
Dalam contoh berikut, kami memuat pustaka HtmlHelp Microsoft secara dinamis:
fungsi TForm1.ApplicationEvents1Help(Perintah: Word; Data: Integer; var CallHelp: Boolean):Boolean;
jenis
TFNHtmlHelpA = fungsi(hwndCaller: HWND; pszFile: PansiChar; uCommand: UINT;dwData: Dword): HWND;
var
Modul Bantuan: Modul H;
HtmlBantuan: TFNHtmlBantuanA;
mulai
Hasil := Salah;
HelpModule := LoadLibrary('HHCTRL.OCX');
jika HelpModule <> 0 maka
mulai
@HtmlHelp := GetProcAddress(HelpModule, 'HtmlHelpA');
jika @HtmlHelp <> nihil maka
Hasil := HtmlHelp(Application.Handle,Pchar(Application.HelpFile), Command,Data) <> 0;
Perpustakaan Gratis(HelpModule);
akhir;
CallHelp := Salah;
akhir;
Memuat BPL secara dinamis
Kita dapat menggunakan metode sederhana yang sama untuk menangani BPL, atau pada dasarnya harus saya katakan dengan cara sederhana yang sama.
Kita dapat memuat paket secara dinamis menggunakan fungsi LoadPackage:
fungsi LoadPackage (Nama const: string): HMODULE;
Kemudian gunakan fungsi GetClass untuk membuat objek tipe TPersistentClass:
fungsi GetClass(const AclassName: string):TPersistentClass;
Setelah semuanya selesai, gunakan UnLoadPackage(Module:HModule);
Mari kita membuat beberapa perubahan kecil pada kode aslinya:
1. Pilih "Project1.exe" di manajer proyek;
2. Klik kanan dan pilih "Opsi...";
3. Pilih halaman opsi “Paket”;
4. Hapus "Paket1" dari kotak edit "Paket Runtime" dan klik tombol OK;
5. Pada toolbar Delphi, klik tombol "Hapus file dari proyek";
6. Pilih "Unit2 | Form2" dan klik OK;
7. Sekarang di kode sumber "Unit1.pas", hapus Unit2 dari klausa penggunaan;
8. Masukkan kode waktu OnClick Button1;
9. Tambahkan dua variabel bertipe HModule dan TPersistentClass:
var
PaketModul: HModule;
Kelas A: Kelas Persisten;
10. Gunakan fungsi LoadPackage untuk memuat paket Pacakge1:
PackageModule := LoadPackage('Package1.bpl');
11. Periksa apakah PackageModule adalah 0;
12. Gunakan fungsi GetClass untuk membuat tipe persisten:
Kelas A := GetClass('TForm2');
13. Jika tipe persisten ini tidak nihil, kita bisa kembali ke tipe sebelumnya
Buat dan gunakan objek jenis ini dengan cara yang sama:
dengan TComponentClass(AClass).Create(Application) seperti yang dilakukan TcustomForm
mulai
TampilkanModal;
Bebas;
akhir;
14. Terakhir, gunakan proses UnloadPackage untuk menghapus instalasi paket:
BongkarPaket(PackageModule);
15. Simpan proyek.
Berikut adalah daftar lengkap event handler OnClick:
procedure TForm1.Button1Click(Pengirim: Objek);
var
PaketModul: HModule;
Kelas A: Kelas Persisten;
mulai
PackageModule := LoadPackage('Package1.bpl');
jika PackageModule <> 0 maka
mulai
Kelas A := GetClass('TForm2');
jika AClass <> nihil maka
dengan TComponentClass(AClass).Create(Application) seperti yang dilakukan TcustomForm
mulai
TampilkanModal;
Bebas;
akhir;
BongkarPaket(PackageModule);
akhir;
akhir;
Sayangnya, bukan itu saja.
Masalahnya adalah fungsi GetClass hanya bisa mencari tipe yang terdaftar. Kelas formulir dan kelas komponen yang biasanya direferensikan dalam formulir didaftarkan secara otomatis saat formulir dimuat. Namun dalam kasus kami, formulir tidak dapat dimuat lebih awal. Jadi dimana kita mendaftarkan tipenya? Jawabannya ada di dalam tas. Setiap unit dalam paket diinisialisasi saat paket dimuat dan dibersihkan saat paket dibongkar.
Sekarang kembali ke contoh kita:
1. Klik dua kali "Package1.bpl" di manajer proyek;
2. Klik tanda + di sebelah “Unit2” di bagian “Berisi”;
3. Klik dua kali "Unit2.pas" untuk mengaktifkan editor kode sumber unit;
4. Tambahkan bagian inisialisasi di akhir file;
5. Gunakan prosedur RegisterClass untuk mendaftarkan tipe formulir:
Kelas Daftar(TForm2);
6. Tambahkan bagian finalisasi;
7. Gunakan prosedur UnRegisterClass untuk membatalkan registrasi tipe formulir:
Batalkan PendaftaranKelas(TForm2);
8. Terakhir, simpan dan kompilasi paket tersebut.
Sekarang kita dapat menjalankan "Project1" dengan aman dan itu akan berfungsi seperti sebelumnya, tetapi sekarang Anda dapat memuat paket sesuka Anda.
akhir
Ingat, apakah Anda menggunakan paket secara statis atau dinamis, aktifkan Project |. Options |.
Sebelum Anda menghapus sebuah paket, ingatlah untuk menghancurkan semua objek kelas dalam paket dan membatalkan pendaftaran semua kelas yang terdaftar. Proses berikut mungkin membantu Anda:
prosedur DoUnloadPackage(Modul: HModule);
var
saya: Bilangan bulat;
M: TMemoryBasicInformation;
mulai
untuk saya := Application.ComponentCount - 1 hingga 0 lakukan
mulai
VirtualQuery(GetClass(Application.Components[i].ClassName), M, Sizeof(M));
jika (Modul = 0) atau (HMODULE(M.AllocationBase) = Modul) maka
Aplikasi.Komponen[i].Gratis;
akhir;
Batalkan PendaftaranModuleClasses(Modul);
BongkarPaket(Modul);
akhir;
Sebelum memuat paket, aplikasi perlu mengetahui nama semua kelas yang terdaftar. Salah satu cara untuk memperbaiki situasi ini adalah dengan membuat mekanisme registrasi yang memberitahukan aplikasi nama semua kelas yang didaftarkan oleh paket.
Contoh
Beberapa paket: Paket tidak mendukung referensi melingkar. Artinya, suatu satuan tidak bisa mereferensikan satuan yang sudah mereferensikan satuan tersebut (hehe). Hal ini menyulitkan nilai-nilai tertentu dalam bentuk pemanggilan untuk ditetapkan oleh metode yang dipanggil.
Solusi untuk masalah ini adalah dengan membuat beberapa paket tambahan yang direferensikan oleh objek pemanggil dan objek dalam paket tersebut. Bayangkan bagaimana kita menjadikan Aplikasi sebagai pemilik segala bentuk? Variabel Aplikasi dibuat di Forms.pas dan disertakan dalam paket VCL50.bpl. Anda mungkin telah memperhatikan bahwa aplikasi Anda tidak hanya perlu mengkompilasi VCL50.pas, tetapi juga memerlukan VCL50 dalam paket Anda.
Dalam contoh ketiga, kami merancang aplikasi untuk menampilkan informasi pelanggan dan, berdasarkan permintaan, pesanan pelanggan (secara dinamis).
Jadi dari mana kita bisa mulai? seperti semua aplikasi database
Prosedurnya sama, kita perlu terhubung. Kami membuat modul data utama yang berisi koneksi TDataBase. Kemudian kita merangkum modul data ini dalam sebuah paket (cst_main).
Sekarang di aplikasi, kami membuat formulir pelanggan dan mereferensikan DataModuleMain (kami menghubungkan VCL50 dan cst_main secara statis).
Kemudian kita membuat paket baru (cst_ordr) yang berisi form pemesanan pelanggan dan membutuhkan cst_main. Sekarang kita dapat memuat cst_ordr secara dinamis di aplikasi. Karena modul data utama sudah ada sebelum paket dinamis dimuat, cst_ordr dapat langsung menggunakan instance modul data utama aplikasi.
Gambar di atas adalah diagram fungsional dari aplikasi ini:
Paket yang Dapat Diganti: Kasus penggunaan lain untuk paket adalah membuat paket yang dapat diganti. Penerapan fungsi ini tidak memerlukan kemampuan pemuatan dinamis paket. Misalkan kita ingin merilis versi uji coba program dengan waktu terbatas, bagaimana cara mencapainya?
Pertama kita membuat formulir "Splash", biasanya gambar dengan kata "Percobaan" di atasnya, dan menampilkannya saat aplikasi dimulai. Kemudian kita membuat form “Tentang” yang memberikan beberapa informasi tentang aplikasi tersebut. Terakhir, kami membuat fungsi yang menguji apakah perangkat lunak tersebut kedaluwarsa. Kami merangkum kedua bentuk dan fungsi ini ke dalam sebuah paket dan merilisnya dengan versi uji coba perangkat lunak.
Untuk versi berbayar, kami juga membuat formulir "Splash" dan formulir "Tentang" - dengan nama kelas yang sama dengan dua formulir sebelumnya - dan fungsi pengujian (yang tidak melakukan apa pun) dan menambahkannya. Dienkapsulasi ke dalam paket dengan fungsi yang sama nama.
Opo opo? Apakah ini berguna, Anda bertanya? Ya, kami bisa merilis versi uji coba perangkat lunak tersebut ke publik. Jika pelanggan membeli aplikasi, kami hanya perlu mengirimkan paket non-uji coba. Hal ini sangat menyederhanakan proses rilis perangkat lunak, karena hanya diperlukan satu instalasi dan satu upgrade paket registrasi.
Paket ini membuka pintu lain menuju desain modular untuk komunitas pengembangan Delphi dan C++ Builder. Dengan paket Anda tidak perlu lagi melewati window handles, tidak ada lagi fungsi callback, tidak ada lagi teknologi DLL lainnya. Hal ini juga memperpendek siklus pengembangan pemrograman modular. Yang harus kita lakukan adalah membiarkan paket Delphi bekerja untuk kita.