FP vs OOP

Belum lama ini, beberapa posting muncul di hub yang kontras dengan pendekatan fungsional dan objek, yang menghasilkan komentar diskusi hangat tentang apa itu sebenarnya - pemrograman berorientasi objek dan bagaimana hal itu berbeda dari fungsional. Saya, walaupun dengan beberapa penundaan, ingin berbagi dengan yang lain apa yang Robert Martin, juga dikenal sebagai Paman Bob, tentang hal ini.



Selama beberapa tahun terakhir, saya telah berulang kali dapat memprogram bersama orang-orang yang mempelajari Pemrograman Fungsional, yang bias tentang OOP. Ini biasanya diungkapkan dalam bentuk pernyataan seperti: "Yah, ini terlalu mirip objek sesuatu."


Saya pikir ini berasal dari keyakinan bahwa FP dan OOP saling eksklusif. Banyak yang berpikir bahwa jika program itu fungsional, maka itu tidak berorientasi objek. Saya percaya bahwa pembentukan pendapat semacam itu adalah konsekuensi logis dari studi tentang sesuatu yang baru.


Ketika kita mengambil teknik baru, kita sering mulai menghindari teknik-teknik lama yang kita gunakan sebelumnya. Ini wajar, karena kami percaya bahwa teknik baru itu "lebih baik" dan karena itu teknik lama mungkin "lebih buruk".


Dalam posting ini, saya dibenarkan dalam pandangan bahwa sementara OOP dan FP adalah ortogonal, ini bukan konsep yang saling eksklusif. Bahwa program fungsional yang baik dapat (dan harus) berorientasi objek. Dan program berorientasi objek yang baik dapat (dan seharusnya) berfungsi. Tetapi untuk melakukan ini, kita harus menentukan persyaratannya.


Apa itu OOP?


Saya akan mendekati masalah ini dari perspektif reduksionis. Ada banyak definisi OOP yang benar yang mencakup banyak konsep, prinsip, teknik, pola, dan filosofi. Saya bermaksud mengabaikannya dan fokus pada garam itu sendiri. Di sini, reduksionisme diperlukan karena semua kekayaan peluang yang mengelilingi OOP ini bukanlah sesuatu yang spesifik untuk OOP; itu hanya sebagian dari banyak peluang yang ditemukan dalam pengembangan perangkat lunak secara umum. Di sini saya akan fokus pada bagian OOP, yang mendefinisikan dan tidak dapat dilepas.


Lihatlah dua ungkapan:


1: f (o); 2: dari ();


Apa bedanya?


Jelas tidak ada perbedaan semantik. Seluruh perbedaan sepenuhnya ada dalam sintaksis. Tapi yang satu terlihat prosedural, dan yang lain berorientasi objek. Ini karena kita terbiasa dengan fakta bahwa ungkapan 2 secara implisit menyiratkan semantik perilaku khusus yang tidak dimiliki oleh ekspresi 1. Semantik perilaku khusus ini adalah polimorfisme.


Ketika kita melihat ekspresi 1. kita melihat fungsi f , yang dipanggil ke mana objek o ditransfer. Ini menyiratkan bahwa hanya ada satu fungsi bernama f, dan bukan fakta bahwa itu adalah anggota dari kohort standar fungsi di sekitar o.


Di sisi lain, ketika kita melihat ekspresi 2. kita melihat sebuah objek dengan nama o di mana pesan dengan nama f dikirim. Kami berharap bahwa mungkin ada jenis objek lain yang menerima pesan f, dan oleh karena itu kami tidak tahu perilaku spesifik apa yang diharapkan dari f setelah panggilan. Perilaku tergantung pada tipe o. yaitu, f adalah polimorfik.


Fakta ini yang kita harapkan dari metode perilaku polimorfik adalah inti dari pemrograman berorientasi objek. Ini adalah definisi reduksionis dan properti ini tidak dapat dihapus dari OOP. OOP tanpa polimorfisme bukanlah OOP. Semua properti OOP lainnya, seperti enkapsulasi data dan metode yang dilampirkan pada data ini dan bahkan pewarisan, lebih terkait dengan ekspresi 1. daripada ekspresi 2.


Pemrogram yang menggunakan C dan Pascal (dan sampai batas tertentu bahkan Fortran dan Cobol) selalu menciptakan sistem fungsi dan struktur yang dienkapsulasi. Untuk membuat struktur seperti itu, Anda bahkan tidak memerlukan bahasa pemrograman berorientasi objek. Enkapsulasi dan bahkan pewarisan sederhana dalam bahasa-bahasa semacam itu jelas dan alami. (Dalam C dan Pascal lebih alami daripada yang lain)


Karena itu, yang benar-benar membedakan program OOP dari program non-OOP adalah polimorfisme.


Anda mungkin ingin berdebat bahwa poliforisme dapat dilakukan hanya dengan menggunakan di dalam saklar f atau panjang jika rantai lain. Ini benar, jadi saya perlu menetapkan batasan lain untuk OOP.


Penggunaan polimorfisme seharusnya tidak menciptakan ketergantungan pemanggil pada yang dipanggil.


Untuk menjelaskan ini, mari kita lihat kembali ekspresi. Ekspresi 1: f (o) tampaknya bergantung pada fungsi f pada level kode sumber. Kami menarik kesimpulan ini karena kami juga berasumsi bahwa f hanya satu dan karena itu penelepon harus tahu tentang callee.


Namun, ketika kita melihat Ekspresi 2. dari () kita mengasumsikan sesuatu yang lain. Kita tahu bahwa mungkin ada banyak realisasi dari f dan kita tidak tahu mana dari fungsi-fungsi ini yang akan dipanggil. Oleh karena itu, kode sumber yang mengandung ekspresi 2 tidak tergantung pada fungsi yang dipanggil pada tingkat kode sumber.


Lebih khusus lagi, ini berarti bahwa modul (file dengan kode sumber) yang berisi panggilan fungsi polimorfik tidak boleh merujuk ke modul (file dengan kode sumber) yang berisi implementasi fungsi-fungsi ini. Tidak boleh ada menyertakan atau menggunakan atau mengharuskan atau kata kunci lain yang membuat beberapa file kode sumber bergantung pada orang lain.


Jadi, definisi reduksionis kami tentang OOP adalah:


Teknik yang menggunakan polimorfisme dinamis untuk memanggil fungsi dan tidak membuat dependensi pemanggil pada panggilan di level kode sumber.

Apa itu AF?


Dan lagi, saya akan menggunakan pendekatan reduksionis. FP memiliki tradisi dan sejarah yang kaya, yang akarnya lebih dalam dari pemrograman itu sendiri. Ada prinsip, teknik, teorema, filosofi, dan konsep yang menembus paradigma ini. Saya akan mengabaikan semua ini dan langsung ke esensi, ke properti yang melekat yang memisahkan FP dari gaya lain. Ini dia:


f (a) == f (b) jika a == b.


Dalam program fungsional, memanggil fungsi dengan argumen yang sama memberikan hasil yang sama tidak peduli berapa lama program telah berjalan. Ini kadang-kadang disebut transparansi referensial.


Oleh karena itu di atas bahwa f tidak boleh mengubah bagian-bagian dari negara global yang memengaruhi perilaku f. Selain itu, jika kita mengatakan bahwa f mewakili semua fungsi dalam sistem - yaitu, semua fungsi dalam sistem harus transparan secara referensi - maka tidak ada fungsi dalam sistem yang dapat mengubah keadaan global. Tidak ada fungsi yang dapat melakukan apa pun yang dapat menyebabkan fungsi lain dari sistem mengembalikan nilai yang berbeda dengan argumen yang sama.


Ini memiliki konsekuensi yang lebih dalam - tidak ada nilai yang dapat diubah. Artinya, tidak ada operator penugasan.


Jika Anda dengan hati-hati mempertimbangkan pernyataan ini, Anda dapat sampai pada kesimpulan bahwa suatu program yang hanya terdiri dari fungsi-fungsi transparan yang transparan tidak dapat berbuat apa-apa - karena perilaku sistem yang berguna mengubah keadaan sesuatu; bahkan jika itu hanya keadaan printer atau layar. Namun, jika kita mengecualikan besi dari persyaratan untuk transparansi referensial dan semua elemen dunia di sekitar kita, ternyata kita dapat membuat sistem yang sangat berguna.


Fokusnya, tentu saja, adalah rekursi. Pertimbangkan fungsi yang mengambil struktur dengan status sebagai argumen. Argumen ini terdiri dari semua informasi status yang harus berfungsi. Ketika pekerjaan selesai, fungsi menciptakan struktur baru dengan keadaan yang isinya berbeda dari yang sebelumnya. Dan dengan tindakan terakhir, fungsi menyebut dirinya dengan struktur baru sebagai argumen.


Ini hanyalah salah satu trik sederhana yang dapat digunakan program fungsional untuk menyimpan perubahan status tanpa harus mengubah status [1].


Jadi, definisi reduksionis dari pemrograman fungsional:


Transparansi Referensial - Anda tidak dapat menetapkan kembali nilai.

FP vs OOP


Pada titik ini, baik pendukung OOP dan pendukung FI sudah melihat saya melalui pemandangan optik. Reduksionisme bukanlah cara terbaik untuk berteman. Tapi terkadang itu bermanfaat. Dalam hal ini, saya pikir berguna untuk menjelaskan holivar anti-OOP yang tidak memudar.


Jelas bahwa dua definisi reduksionis yang saya pilih sepenuhnya ortogonal. Polimorfisme dan Transparansi Referensial tidak ada hubungannya satu sama lain. Mereka tidak berpotongan dengan cara apa pun.


Tetapi ortogonalitas tidak menyiratkan saling pengecualian (tanya James Clerk Maxwell). Sangat mungkin untuk membuat sistem yang menggunakan polimorfisme dinamis dan transparansi referensial. Itu tidak hanya mungkin, itu benar dan baik!


Mengapa kombinasi ini baik? Untuk alasan yang persis sama bahwa kedua komponennya! Sistem yang dibangun di atas polimorfisme dinamis adalah baik karena mereka memiliki konektivitas rendah. Ketergantungan dapat dibalik dan ditempatkan pada sisi yang berbeda dari batas arsitektur. Sistem ini dapat diuji menggunakan Moki dan Palsu dan jenis Uji Ganda lainnya. Modul dapat dimodifikasi tanpa membuat perubahan ke modul lain. Karena itu, sistem seperti itu lebih mudah untuk dimodifikasi dan diperbaiki.


Sistem yang dibangun berdasarkan transparansi referensial juga bagus karena dapat diprediksi. Keadaan tidak berubah membuat sistem seperti itu lebih mudah untuk dipahami, diubah, dan ditingkatkan. Ini sangat mengurangi kemungkinan ras dan masalah multithreading lainnya.


Ide utama di sini adalah ini:


Tidak ada FP dan OOP holivar

FP dan OOP bekerja sama dengan baik. Keduanya baik dan layak digunakan dalam sistem modern. Sistem, yang didasarkan pada kombinasi prinsip-prinsip OOP dan FP memaksimalkan fleksibilitas, rawatan, pengujian, kesederhanaan dan kekuatan. Jika Anda menghapus satu untuk menambahkan yang lain, itu hanya akan memperburuk struktur sistem.


[1] Karena kami menggunakan mesin dengan arsitektur Von Neumann, kami menganggap bahwa mereka memiliki sel memori yang kondisinya benar-benar berubah. Dalam mekanisme rekursi yang saya jelaskan, optimasi rekursi ekor tidak akan memungkinkan pembuatan bingkai kaca baru dan bingkai kaca asli akan digunakan. Tetapi pelanggaran transparansi referensial ini (biasanya) disembunyikan dari programmer dan tidak memengaruhi apa pun.

Source: https://habr.com/ru/post/id474518/


All Articles