Di dunia (OOP), terdapat banyak garis panduan, corak atau prinsip reka bentuk. Lima prinsip ini biasanya dikumpulkan bersama dan dikenali dengan singkatan SOLID. Walaupun masing-masing dari lima prinsip ini menjelaskan sesuatu yang spesifik, mereka bertindih juga sehingga menerapkan salah satu daripadanya menyiratkan atau membawa kepada penerapan yang lain. Dalam artikel ini kita akan Memahami Prinsip SOLID di Jawa.
Sejarah Prinsip SOLID di Jawa
Robert C. Martin memberikan lima prinsip reka bentuk berorientasikan objek, dan singkatan 'S.O.L.I.D' digunakan untuknya. Apabila anda menggunakan semua prinsip S.O.L.I.D secara gabungan, menjadi lebih mudah bagi anda untuk membangunkan perisian yang dapat diuruskan dengan mudah. Ciri lain menggunakan S.O.L.I.D adalah:
- Ia mengelakkan bau kod.
- Kod refractor dengan cepat.
- Boleh melakukan pengembangan perisian adaptif atau tangkas.
Apabila anda menggunakan prinsip S.O.L.I.D dalam pengekodan anda, anda mula menulis kod yang cekap dan berkesan.
Apakah maksud S.O.L.I.D?
Pepejal mewakili lima prinsip java iaitu:
- S : Prinsip tanggungjawab tunggal
- ATAU : Prinsip tertutup terbuka
- L : Prinsip penggantian Liskov
- Saya : Prinsip pengasingan antara muka
- D : Prinsip penyongsangan ketergantungan
Di blog ini, kita akan membincangkan kelima prinsip SOLID Java secara terperinci.
Prinsip Tanggungjawab Tunggal di Jawa
Apakah maknanya?
Robert C. Martin menggambarkannya sebagai satu kelas harus mempunyai satu dan satu-satunya tanggungjawab.
Menurut prinsip tanggungjawab tunggal, hanya ada satu sebab di mana kelas harus ditukar. Ini bermaksud bahawa satu kelas harus mempunyai satu tugas yang harus dilakukan. Prinsip ini sering disebut sebagai subjektif.
Prinsipnya dapat difahami dengan baik dengan contoh. Bayangkan ada kelas yang melakukan operasi berikut.
Bersambung ke pangkalan data
Baca beberapa data dari jadual pangkalan data
Akhirnya, tuliskan ke fail.
Sudahkah anda membayangkan senario itu? Di sini kelas mempunyai banyak alasan untuk berubah, dan beberapa di antaranya adalah pengubahsuaian output fail, penggunaan pangkalan data baru. Apabila kita bercakap mengenai tanggungjawab prinsip tunggal, kita akan mengatakan, terdapat terlalu banyak alasan untuk kelas berubah, oleh itu, ia tidak sesuai dengan prinsip tanggungjawab tunggal.
Sebagai contoh, kelas Automobile boleh memulakan atau menghentikannya sendiri tetapi tugas mencucinya adalah milik kelas CarWash. Dalam contoh lain, kelas Buku mempunyai sifat untuk menyimpan nama dan teksnya sendiri. Tetapi tugas mencetak buku mesti tergolong dalam kelas Pencetak Buku. Kelas Pencetak Buku mungkin mencetak ke konsol atau media lain tetapi kebergantungan seperti itu dikeluarkan dari kelas Buku
Mengapa Prinsip ini Diperlukan?
Apabila Prinsip Tanggungjawab Tunggal dipatuhi, ujian lebih mudah. Dengan satu tanggungjawab, kelas akan mempunyai lebih sedikit kes ujian. Kurang fungsi juga bermaksud lebih sedikit kebergantungan kepada kelas lain. Ini membawa kepada organisasi kod yang lebih baik kerana kelas yang lebih kecil dan bertujuan lebih senang dicari.
Contoh untuk menjelaskan prinsip ini:
Katakan anda diminta untuk melaksanakan perkhidmatan UserSetting di mana pengguna boleh mengubah tetapan tetapi sebelum itu pengguna harus disahkan. Salah satu cara untuk melaksanakannya ialah:
kelas awam UserSettingService {public void changeEmail (User user) {if (checkAccess (user)) {// Beri pilihan untuk menukar}} public boolean checkAccess (Pengguna pengguna) {// Sahkan sama ada pengguna itu sah. }}
Semua kelihatan baik sehingga anda ingin menggunakan semula kod checkAccess di tempat lain ATAU anda ingin membuat perubahan pada cara checkAccess dilakukan. Dalam semua 2 kes, anda akhirnya akan menukar kelas yang sama dan dalam kes pertama anda harus menggunakan UserSettingService untuk memeriksa akses juga.
Salah satu cara untuk membetulkannya adalah dengan menguraikan UserSettingService menjadi UserSettingService dan SecurityService. Dan pindahkan kod checkAccess ke SecurityService.
perbezaan antara kelas dan antara muka di java
kelas awam UserSettingService {public void changeEmail (Pengguna pengguna) {if (SecurityService.checkAccess (pengguna)) {// Beri pilihan untuk menukar}}} kelas awam SecurityService {public static boolean checkAccess (Pengguna pengguna) {// periksa akses. }}
Buka Prinsip Tertutup di Jawa
Robert C. Martin menggambarkannya sebagai komponen Perisian harus dibuka untuk peluasan, tetapi ditutup untuk pengubahsuaian.
Tepatnya, menurut prinsip ini, kelas harus ditulis sedemikian rupa sehingga melaksanakan tugasnya dengan sempurna tanpa anggapan bahawa orang di masa depan hanya akan datang dan mengubahnya. Oleh itu, kelas harus tetap ditutup untuk pengubahsuaian, tetapi harus mempunyai pilihan untuk diperpanjang. Kaedah melanjutkan kelas merangkumi:
Warisan dari kelas
Menimpa tingkah laku yang diperlukan dari kelas
Memperluas tingkah laku tertentu kelas
Contoh terbaik prinsip tertutup terbuka dapat difahami dengan bantuan penyemak imbas. Adakah anda ingat memasang pelanjutan di penyemak imbas chrome anda?
Fungsi asas penyemak imbas krom adalah melayari laman web yang berbeza. Adakah anda ingin memeriksa tatabahasa semasa anda menulis e-mel menggunakan penyemak imbas chrome? Jika ya, anda hanya boleh menggunakan pelanjutan Grammarly, ini memberi anda pemeriksaan tatabahasa pada kandungannya.
Mekanisme ini di mana anda menambahkan perkara untuk meningkatkan fungsi penyemak imbas adalah lanjutan. Oleh itu, penyemak imbas adalah contoh fungsi yang sempurna yang terbuka untuk peluasan tetapi ditutup untuk pengubahsuaian. Dengan kata mudah, anda dapat meningkatkan fungsi dengan menambahkan / memasang pemalam pada penyemak imbas anda, tetapi tidak dapat membina sesuatu yang baru.
Mengapa prinsip ini diperlukan?
OCP penting kerana kelas mungkin datang kepada kami melalui perpustakaan pihak ketiga. Kita seharusnya dapat melanjutkan kelas tersebut tanpa perlu risau jika kelas asas tersebut dapat menyokong pelanjutan kita. Tetapi warisan boleh menyebabkan subkelas yang bergantung pada pelaksanaan kelas dasar. Untuk mengelakkan ini, penggunaan antara muka adalah disyorkan. Pengambilan tambahan ini membawa kepada gandingan yang longgar.
Katakan kita perlu mengira kawasan dengan pelbagai bentuk. Kami bermula dengan membuat kelas untuk Rectangle bentuk pertama kamiyang mempunyai 2 atribut panjang& lebar.
Rectangle kelas awam {public double length public double width}
Seterusnya kami membuat kelas untuk mengira luas segi empat tepat iniyang mempunyai kaedah mengiraRectangleAreayang mengambil segi empat tepatsebagai parameter input dan mengira luasnya.
AreaCalculator kelas awam {public double calculRectangleArea (segi empat tepat) {return rectangle.length * rectangle.width}}
Setakat ini begitu baik. Sekarang katakan kita mendapat bulatan bentuk kedua kita. Oleh itu, kami segera membuat Circle kelas barudengan jejari atribut tunggal.
bulatan kelas awam {public double radius}
Kemudian kami mengubahsuai Areacalculatorkelas untuk menambahkan pengiraan bulatan melalui kaedah baru hitungCircleaArea ()
AreaCalculator kelas awam {public double calculRectangleArea (Rectangle segiempat) {return rectangle.length * rectangle.width} public double calculCircleArea (Circle circle) {return (22/7) * circle.radius * circle.radius}}
Namun, perhatikan bahawa terdapat kelemahan dalam cara kami merancang penyelesaian kami di atas.
Katakan kita mempunyai pentagon bentuk baru. Dalam kes itu, kita sekali lagi akan mengubah kelas AreaCalculator. Apabila jenis bentuk tumbuh, ini menjadi semakin meruncing kerana AreaCalculator terus berubah dan mana-mana pengguna kelas ini harus terus mengemas kini perpustakaan mereka yang mengandungi AreaCalculator. Hasilnya, kelas AreaCalculator tidak akan dibahas (dimuktamadkan) dengan jaminan kerana setiap kali bentuk baru ia akan diubah. Jadi, reka bentuk ini tidak ditutup untuk pengubahsuaian.
AreaCalculator perlu terus menambahkan logik pengiraan mereka dalam kaedah yang lebih baru. Kami tidak benar-benar memperluas ruang lingkup bentuknya tetapi kami hanya melakukan penyelesaian makanan sepotong (sedikit demi sedikit) untuk setiap bentuk yang ditambahkan.
Pengubahsuaian reka bentuk di atas untuk mematuhi prinsip terbuka / tertutup:
Marilah kita sekarang melihat reka bentuk yang lebih elegan yang dapat menyelesaikan kekurangan reka bentuk di atas dengan mematuhi Prinsip Terbuka / Tertutup. Mula-mula kita akan menjadikan reka bentuknya dapat diperluas. Untuk ini, kita perlu terlebih dahulu menentukan Shape jenis asas dan mempunyai antara muka Shape implement Circle & Rectangle.
muka depan awam Bentuk {public double calculArea ()} kelas awam Rectangle mengimplementasikan Shape {double length double width public double calculArea () {return panjang * width}} kelas awam Lingkaran melaksanakan Shape {public double radius public double calculArea () {return (22 / 7) * jejari * jejari}}
Terdapat bentuk antara muka asas. Semua bentuk kini menerapkan Shape antara muka asas. Antaramuka bentuk mempunyai kaedah abstrak menghitungArea (). Kedua-dua bulatan & segi empat itu menyediakan kaedah kalkulatorArea () yang diganti menggunakan atribut mereka sendiri.
Kami telah membawa tingkat kepanjangan kerana bentuk sekarang menjadi contoh antara muka bentuk. Ini membolehkan kita menggunakan Shape dan bukannya kelas individu
Perkara terakhir di atas menyebut pengguna bentuk ini. Dalam kes kami, pengguna akan menjadi kelas AreaCalculator yang sekarang akan kelihatan seperti ini.
AreaCalculator kelas awam {public double calculationShapeArea (Shape shape) {return shape.calculateArea ()}}
Kalkulator Kawasan inikelas sekarang sepenuhnya menghilangkan kekurangan reka bentuk kami yang dinyatakan di atas dan memberikan penyelesaian bersih yang mematuhi Prinsip Tertutup Terbuka. Mari kita teruskan dengan Prinsip SOLID lain di Java
Prinsip Penggantian Liskov di Jawa
Robert C. Martin menggambarkannya sebagai jenis Berasal mesti diganti sepenuhnya dengan jenis asasnya.
Prinsip penggantian Liskov menganggap q (x) sebagai harta, dapat dibuktikan mengenai entiti x yang termasuk dalam jenis T. Sekarang, menurut prinsip ini, q (y) sekarang boleh dibuktikan untuk objek y yang termasuk dalam jenis S, dan S sebenarnya adalah sub-jenis T. Adakah anda sekarang keliru dan tidak tahu apa sebenarnya maksud prinsip penggantian Liskov? Definisi itu mungkin agak rumit, tetapi sebenarnya, agak mudah. Satu-satunya perkara adalah bahawa setiap subkelas atau kelas turutan harus diganti dengan kelas induk atau kelas asas mereka.
Anda boleh mengatakan bahawa ini adalah prinsip berorientasikan objek yang unik. Prinsip ini selanjutnya dapat dipermudahkan oleh jenis anak dari jenis ibu bapa tertentu tanpa membuat komplikasi atau meletupkan sesuatu harus mempunyai kemampuan untuk mendukung ibu bapa itu. Prinsip ini berkait rapat dengan prinsip Penggantian Liskov.
Mengapa prinsip ini diperlukan?
Ini mengelakkan penyalahgunaan harta pusaka. Ini membantu kita menyesuaikan diri dengan hubungan 'is-a'. Kita juga boleh mengatakan bahawa subkelas mesti memenuhi kontrak yang ditentukan oleh kelas asas. Dalam pengertian ini, ia berkaitan denganReka bentuk mengikut Kontrakyang pertama kali digambarkan oleh Bertrand Meyer. Contohnya, menggoda untuk mengatakan bahawa bulatan adalah jenis elips tetapi lingkaran tidak mempunyai dua fokus atau sumbu utama / kecil.
LSP dijelaskan dengan popular menggunakan contoh segiempat sama dan segi empat tepat. jika kita menganggap hubungan ISA antara Square dan Rectangle. Oleh itu, kami memanggil 'Square is a Rectangle.' Kod di bawah menunjukkan hubungan.
kelas awam Rectangle {private int length private int lebarth public int getLength () {return length} public void setLength (int length) {this.length = length} public int getBreadth () {return width} public void setBreadth (int lebar) { this.breadth = width} public int getArea () {return this.length * this.breadth}}
Berikut adalah kod untuk Square. Perhatikan bahawa Square memanjang segi empat tepat.
kelas awam Square memanjangkan segi empat tepat {public void setBreadth (int lebar) {super.setBreadth (lebar) super.setLength (lebar)} set kosong awam (panjang int) {super.setLength (panjang) super.setBreadth (panjang)}}
Dalam hal ini, kami berusaha untuk menjalin hubungan ISA antara Square dan Rectangle sehingga memanggil 'Square is a Rectangle' dalam kod di bawah ini akan mulai berkelakuan tidak disangka-sangka jika terdapat contoh Square. Kesalahan penegasan akan dilontarkan dalam hal memeriksa 'Kawasan' dan memeriksa 'Luas', walaupun program akan berakhir ketika kesalahan penegasan dilemparkan kerana kegagalan pemeriksaan Kawasan.
kelas awam LSPDemo {public void calculArea (Rectangle r) {r.setBreadth (2) r.setLength (3) assert r.getArea () == 6: printError ('area', r) tegaskan r.getLength () == 3: printError ('length', r) assert r.getBreadth () == 2: printError ('lebar', r)} private String printError (String errorIdentifer, Rectangle r) {return 'Nilai tidak dijangka' + errorIdentifer + ' sebagai contoh '+ r.getClass (). getName ()} public static void main (String [] args) {LSPDemo lsp = new LSPDemo () // Contoh Rectangle dilalui lsp.calculateArea (Rectangle baru ()) // Contoh Square dilalui lsp.calculateArea (Square baru ())}}
Kelas menunjukkan Prinsip Penggantian Liskov (LSP) Menurut prinsip, fungsi yang menggunakan rujukan ke kelas asas mesti dapat menggunakan objek dari kelas turunan tanpa menyedarinya.
Oleh itu, dalam contoh yang ditunjukkan di bawah ini, fungsi calculArea yang menggunakan rujukan 'Rectangle' harus dapat menggunakan objek dari kelas turunan seperti Square dan memenuhi syarat yang ditimbulkan oleh definisi Rectangle. Kita harus perhatikan bahawa menurut definisi Rectangle, berikut mesti selalu berlaku berdasarkan data di bawah:
- Panjang mestilah sama dengan panjang yang dilalui sebagai input ke kaedah, setLength
- Lebar mestilah sama dengan lebar yang diberikan sebagai input ke kaedah, setBreadth
- Luas mestilah sama dengan panjang dan lebar produk
Sekiranya, kami cuba menjalin hubungan ISA antara Square dan Rectangle sehingga kami memanggil 'Square is a Rectangle', kod di atas akan mula berkelakuan tidak disangka-sangka jika contoh Square dilewati Kesalahan penegasan akan dilemparkan sekiranya memeriksa kawasan dan cek untuk keluasan, walaupun program akan berakhir kerana kesalahan penegasan dilemparkan kerana kegagalan Pemeriksaan Kawasan.
Kelas Square tidak memerlukan kaedah seperti setBreadth atau setLength. Kelas LSPDemo perlu mengetahui perincian kelas turunan Rectangle (seperti Square) untuk membuat kod dengan betul untuk mengelakkan kesalahan melemparkan. Perubahan kod yang ada melanggar prinsip tertutup di tempat pertama.
Prinsip Pengasingan Antara Muka
Robert C. Martin menggambarkannya sebagai pelanggan tidak boleh dipaksa untuk melaksanakan kaedah yang tidak perlu yang tidak akan mereka gunakan.
MenurutPrinsip pengasingan antara mukapelanggan, tidak kira apa yang tidak boleh dipaksa untuk melaksanakan antara muka yang tidak digunakannya atau pelanggan tidak semestinya wajib bergantung pada kaedah apa pun, yang tidak digunakan oleh mereka. Oleh itu, pada asasnya, prinsip pemisahan antara muka mengikut kehendak anda antaramuka, yang kecil tetapi khusus pelanggan dan bukannya antara muka monolitik dan lebih besar. Ringkasnya, adalah buruk bagi anda untuk memaksa klien bergantung pada perkara tertentu, yang mereka tidak perlukan.
Contohnya, antara muka pembalakan tunggal untuk menulis dan membaca log berguna untuk pangkalan data tetapi tidak untuk konsol. Membaca log tidak masuk akal untuk logger konsol. Teruskan dengan artikel Prinsip SOLID ini di Java.
Mengapa prinsip ini diperlukan?
Katakan bahawa terdapat antara muka Restoran yang mengandungi kaedah untuk menerima pesanan dari pelanggan dalam talian, pelanggan dailan atau telefon dan pelanggan masuk. Ini juga mengandungi kaedah untuk menangani pembayaran dalam talian (untuk pelanggan dalam talian) dan pembayaran sendiri (untuk pelanggan masuk dan juga pelanggan telefon ketika pesanan mereka dihantar di rumah).
Sekarang mari kita buat Java Interface untuk Restaurant dan beri nama sebagai RestaurantInterface.java.
antara muka awam RestaurantInterface {public void acceptOnlineOrder () public void takeTelephoneOrder () public void payOnline () public void walkInCustomerOrder () public void payInPerson ()}
Terdapat 5 kaedah yang ditentukan dalam RestaurantInterface iaitu untuk menerima pesanan dalam talian, mengambil pesanan melalui telefon, menerima pesanan dari pelanggan yang masuk, menerima pembayaran dalam talian dan menerima pembayaran secara peribadi.
Mari kita mulakan dengan menerapkan RestaurantInterface untuk pelanggan dalam talian sebagai OnlineClientImpl.java
kelas awam OnlineClientImpl mengimplementasikan RestaurantInterface {public void acceptOnlineOrder () {// logik untuk membuat pesanan dalam talian} public void takeTelephoneOrder () {// Tidak Berlaku untuk Pesanan Dalam Talian melemparkan UnsupportedOperationException ()} log awam yang tidak sah PayOnline () {// logik untuk membayar online} public void walkInCustomerOrder () {// Tidak Berlaku untuk Pesanan Dalam Talian membuang UnsupportedOperationException ()} public void payInPerson () {// Tidak Berlaku untuk Pesanan Dalam Talian baru membuang UnsupportedOperationException baru ()}}
Oleh kerana kod di atas (OnlineClientImpl.java) adalah untuk pesanan dalam talian, buang UnsupportedOperationException.
Pelanggan dalam talian, telefon dan walk-in menggunakan pelaksanaan RestaurantInterface khusus untuk setiap mereka.
Kelas pelaksanaan untuk klien Telephonic dan klien Walk-in akan mempunyai kaedah yang tidak disokong.
Oleh kerana 5 kaedah tersebut merupakan sebahagian daripada RestaurantInterface, kelas pelaksanaan harus melaksanakan kesemua 5 daripadanya.
Kaedah yang setiap kelas pelaksanaan membuang UnsupportedOperationException. Seperti yang anda lihat dengan jelas - melaksanakan semua kaedah tidak cekap.
Sebarang perubahan dalam mana-mana kaedah RestaurantInterface akan diteruskan ke semua kelas pelaksanaan. Penyelenggaraan kod kemudian mulai menjadi sangat membebankan dan kesan regresi perubahan akan terus meningkat.
apa yang ada di java
RestaurantInterface.java melanggar Prinsip Tanggungjawab Tunggal kerana logik untuk pembayaran dan juga untuk penempatan pesanan dikelompokkan dalam satu muka.
Untuk mengatasi masalah yang disebutkan di atas, kami menerapkan Prinsip Pengasingan Antara Muka untuk memfaktorkan semula reka bentuk di atas.
Pisahkan fungsi pembayaran dan penempatan pesanan menjadi dua antara muka ramping yang terpisah, PaymentInterface.java dan OrderInterface.java.
Setiap pelanggan menggunakan satu pelaksanaan masing-masing PaymentInterface dan OrderInterface. Contohnya - OnlineClient.java menggunakan OnlinePaymentImpl dan OnlineOrderImpl dan sebagainya.
Prinsip Tanggungjawab Tunggal kini dilampirkan sebagai antara muka Pembayaran (PaymentInterface.java) dan antara muka Pemesanan (OrderInterface).
Perubahan pada salah satu antara muka pesanan atau pembayaran tidak akan mempengaruhi yang lain. Mereka bebas sekarang. Tidak perlu melakukan apa-apa pelaksanaan palsu atau membuang UnsupportedOperationException kerana setiap antara muka hanya mempunyai kaedah yang akan selalu digunakannya.
Selepas memohon ISP
Prinsip Penukaran Ketergantungan
Robert C. Martin menerangkannya kerana ia bergantung pada abstraksi bukan pada konkrit. Menurutnya, modul tahap tinggi tidak boleh bergantung pada modul tahap rendah. sebagai contoh
Anda pergi ke kedai tempatan untuk membeli sesuatu, dan anda memutuskan untuk membayarnya dengan menggunakan kad debit anda. Oleh itu, apabila anda memberikan kad anda kepada kerani untuk membuat pembayaran, kerani tidak perlu menyusahkan untuk memeriksa jenis kad yang telah anda berikan.
Walaupun anda telah memberikan kad Visa, dia tidak akan mengeluarkan mesin Visa untuk menggesek kad anda. Jenis kad kredit atau kad debit yang anda miliki untuk membayar tidak kira mereka akan meleretnya. Oleh itu, dalam contoh ini, anda dapat melihat bahawa anda dan kerani bergantung pada pengambilan kad kredit dan anda tidak bimbang tentang spesifik kad tersebut. Inilah prinsip penyongsangan ketergantungan.
Mengapa prinsip ini diperlukan?
Ini membolehkan pengaturcara untuk membuang kebergantungan hardcoded sehingga aplikasi menjadi longgar dan dapat diperpanjang.
pelajar kelas awam {alamat Alamat peribadi pelajar awam () {alamat = Alamat baru ()}}
Dalam contoh di atas, kelas Pelajar memerlukan objek Alamat dan bertanggungjawab untuk memulakan dan menggunakan objek Alamat. Sekiranya kelas Alamat diubah pada masa akan datang, maka kita juga harus membuat perubahan di kelas Pelajar. Ini menjadikan gandingan erat antara objek Pelajar dan Alamat. Kami dapat menyelesaikan masalah ini dengan menggunakan corak reka bentuk penyongsangan ketergantungan. Objek alamat akan dilaksanakan secara bebas dan akan diberikan kepada Pelajar apabila Pelajar diberi contoh dengan menggunakan pemboleh ubah bersandar berasaskan konstruktor atau berasaskan setter.
Dengan ini, kita akan mengakhiri Prinsip SOLID ini di Jawa.
Lihat oleh Edureka, sebuah syarikat pembelajaran dalam talian yang dipercayai dengan rangkaian lebih daripada 250,000 pelajar berpuas hati yang tersebar di seluruh dunia. Kursus latihan dan pensijilan Java J2EE dan SOA Edureka dirancang untuk pelajar dan profesional yang ingin menjadi Pembangun Java. Kursus ini dirancang untuk memberi Anda awal dalam pengaturcaraan Java dan melatih anda untuk konsep Java teras dan maju bersama dengan pelbagai kerangka kerja Java seperti Hibernate & Spring.
Ada soalan untuk kami? Sila sebutkan di bahagian komen dari blog 'Prinsip SOLID di Java' ini dan kami akan menghubungi anda secepat mungkin.