Rekayasa Perangkat Lunak seperti yang diajarkan hari ini sepenuhnya berfokus pada pemrograman berorientasi objek dan pandangan berorientasi objek 'alami' dunia. Ada metodologi terperinci yang menjelaskan cara mengubah model domain menjadi model kelas dengan beberapa langkah dan banyak (UML) artefak seperti use-case-diagram atau class-diagram. Banyak programmer telah menginternalisasi pendekatan ini dan memiliki ide bagus tentang bagaimana merancang aplikasi berorientasi objek dari awal.
Hype baru adalah pemrograman fungsional, yang diajarkan di banyak buku dan tutorial. Tapi bagaimana dengan rekayasa perangkat lunak fungsional? Saat membaca tentang Lisp dan Clojure, saya menemukan dua pernyataan menarik:
Program fungsional sering dikembangkan dari bawah ke atas bukan dari atas ke bawah ('On Lisp', Paul Graham)
Pemrogram Fungsional menggunakan Peta di mana Pemrogram OO menggunakan objek / kelas ('Clojure for Java Programmers', berbicara oleh Rich Hickley).
Jadi apa metodologi untuk desain sistematis (berbasis model?) Dari aplikasi fungsional, yaitu dalam Lisp atau Clojure? Apa langkah-langkah umum, artefak apa yang saya gunakan, bagaimana cara memetakannya dari ruang masalah ke ruang solusi?
Jawaban:
Terima kasih Tuhan bahwa orang-orang rekayasa perangkat lunak belum menemukan pemrograman fungsional. Berikut ini beberapa persamaannya:
Banyak "pola desain" OO ditangkap sebagai fungsi tingkat tinggi. Misalnya, pola Pengunjung dikenal di dunia fungsional sebagai "lipatan" (atau jika Anda adalah ahli teori berkepala runcing, "katamorfisme"). Dalam bahasa fungsional, tipe data sebagian besar pohon atau tupel, dan setiap tipe pohon memiliki katamorfisme alami yang terkait dengannya.
Fungsi-fungsi tingkat tinggi ini sering datang dengan hukum pemrograman tertentu, alias "teorema bebas".
Pemrogram fungsional menggunakan diagram jauh lebih sedikit daripada pemrogram OO. Banyak dari apa yang diungkapkan dalam diagram OO sebaliknya dinyatakan dalam tipe , atau dalam "tanda tangan", yang harus Anda anggap sebagai "tipe modul". Haskell juga memiliki "kelas tipe", yang sedikit mirip dengan tipe antarmuka.
Pemrogram fungsional yang menggunakan tipe umumnya berpikir bahwa "setelah Anda mendapatkan tipe yang benar; kode praktis menulis sendiri."
Tidak semua bahasa fungsional menggunakan tipe eksplisit, tetapi buku How To Design Program , sebuah buku yang bagus untuk mempelajari Skema / Lisp / Clojure, sangat bergantung pada "deskripsi data", yang terkait erat dengan tipe.
Setiap metode desain berdasarkan abstraksi data berfungsi dengan baik. Saya kebetulan berpikir bahwa ini lebih mudah ketika bahasa tersebut memiliki tipe eksplisit, tetapi itu berfungsi bahkan tanpa. Buku bagus tentang metode desain untuk tipe data abstrak, yang mudah diadaptasi untuk pemrograman fungsional, adalah Abstraksi dan Spesifikasi dalam Pengembangan Program oleh Barbara Liskov dan John Guttag, edisi pertama . Liskov memenangkan penghargaan Turing sebagian untuk pekerjaan itu.
Metodologi desain lain yang unik untuk Lisp adalah memutuskan ekstensi bahasa apa yang akan berguna dalam domain masalah tempat Anda bekerja, dan kemudian menggunakan makro higienis untuk menambahkan konstruksi ini ke bahasa Anda. Tempat yang bagus untuk membaca tentang desain semacam ini adalah artikel Matthew Flatt, Making Languages in Racket . Artikel tersebut mungkin berada di belakang paywall. Anda juga dapat menemukan materi yang lebih umum tentang desain semacam ini dengan mencari istilah "bahasa tertanam khusus domain"; untuk saran dan contoh khusus di luar apa yang dibahas Matthew Flatt, saya mungkin akan mulai dengan Graham's On Lisp atau mungkin ANSI Common Lisp .
Langkah umum:
Identifikasi data dalam program Anda dan operasi di dalamnya, dan tentukan tipe data abstrak yang mewakili data ini.
Identifikasi tindakan umum atau pola perhitungan, dan ungkapkan sebagai fungsi atau makro tingkat tinggi. Berharap untuk mengambil langkah ini sebagai bagian dari refactoring.
Jika Anda menggunakan bahasa fungsional yang diketik, gunakan pemeriksa tipe lebih awal dan sering. Jika Anda menggunakan Lisp atau Clojure, praktik terbaik adalah menulis kontrak fungsi terlebih dahulu termasuk pengujian unit — pengembangan yang didorong oleh tes hingga maksimal. Dan Anda akan ingin menggunakan versi QuickCheck apa pun yang telah porting ke platform Anda, yang dalam kasus Anda sepertinya disebut ClojureCheck . Ini adalah pustaka yang sangat kuat untuk membuat tes kode acak yang menggunakan fungsi tingkat tinggi.
sumber
Untuk Clojure, saya sarankan kembali ke pemodelan relasional lama yang baik. Out of the Tarpit adalah bacaan inspirasional.
sumber
Secara pribadi saya menemukan bahwa semua praktik baik yang biasa dari pengembangan OO berlaku dalam pemrograman fungsional juga - hanya dengan beberapa penyesuaian kecil untuk memperhitungkan pandangan dunia fungsional. Dari perspektif metodologi, Anda tidak perlu melakukan sesuatu yang secara fundamental berbeda.
Pengalaman saya berasal dari pindah dari Jawa ke Clojure dalam beberapa tahun terakhir.
Beberapa contoh:
Memahami domain / model data bisnis Anda - sama pentingnya dengan apakah Anda akan merancang model objek atau membuat struktur data fungsional dengan peta bersarang. Dalam beberapa hal, FP bisa lebih mudah karena mendorong Anda untuk berpikir tentang model data secara terpisah dari fungsi / proses tetapi Anda masih harus melakukan keduanya.
Orientasi layanan dalam desain - sebenarnya berfungsi sangat baik dari sudut pandang FP, karena layanan tipikal sebenarnya hanya fungsi dengan beberapa efek samping. Saya pikir bahwa pandangan "bottom up" tentang pengembangan perangkat lunak yang kadang-kadang didukung di dunia Lisp sebenarnya hanya prinsip-prinsip desain API berorientasi layanan yang baik dalam kedok lain.
Pengembangan yang Didorong Tes - berfungsi dengan baik dalam bahasa FP, bahkan kadang-kadang lebih baik karena fungsi murni sangat cocok untuk menulis tes yang jelas dan dapat diulang tanpa perlu menyiapkan lingkungan yang stateful. Anda mungkin juga ingin membuat tes terpisah untuk memeriksa integritas data (mis. Apakah peta ini memiliki semua kunci di dalamnya yang saya harapkan, untuk menyeimbangkan fakta bahwa dalam bahasa OO definisi kelas akan memberlakukan ini untuk Anda pada waktu kompilasi).
Prototying / iterasi - berfungsi sama baiknya dengan FP. Anda bahkan dapat membuat prototipe langsung dengan pengguna jika Anda menjadi sangat pandai membuat alat / DSL dan menggunakannya di REPL.
sumber
Pemrograman OO secara ketat memasangkan data dengan perilaku. Pemrograman fungsional memisahkan keduanya. Jadi Anda tidak memiliki diagram kelas, tetapi Anda memiliki struktur data, dan Anda khususnya memiliki tipe data aljabar. Jenis-jenis itu dapat ditulis agar sangat sesuai dengan domain Anda, termasuk menghilangkan nilai yang tidak mungkin dengan konstruksi.
Jadi tidak ada buku dan buku di dalamnya, tetapi ada pendekatan mapan untuk, seperti kata pepatah, membuat nilai-nilai yang tidak mungkin tidak terwakili.
Dengan demikian, Anda dapat membuat berbagai pilihan tentang merepresentasikan tipe data tertentu sebagai fungsi saja, dan sebaliknya, merepresentasikan fungsi tertentu sebagai gabungan tipe data sehingga Anda bisa mendapatkan, misalnya serialisasi, spesifikasi yang lebih ketat, optimisasi, dll. .
Kemudian, mengingat itu, Anda menulis fungsi di atas iklan Anda sehingga Anda membuat semacam aljabar - yaitu ada hukum tetap yang berlaku untuk fungsi-fungsi ini. Beberapa mungkin idempoten - sama setelah beberapa aplikasi. Beberapa asosiatif. Beberapa transitif, dll.
Sekarang Anda memiliki domain tempat Anda memiliki fungsi yang menyusun menurut hukum yang berlaku. DSL tertanam sederhana!
Oh, dan diberi properti, Anda tentu saja dapat menulis tes acak secara otomatis (ala QuickCheck) .. dan itu baru permulaan.
sumber
Desain Berorientasi Objek tidak sama dengan rekayasa perangkat lunak. Rekayasa perangkat lunak harus dilakukan dengan seluruh proses bagaimana kita beralih dari persyaratan ke sistem kerja, tepat waktu dan dengan tingkat cacat rendah. Pemrograman fungsional mungkin berbeda dari OO, tetapi tidak menghilangkan persyaratan, desain tingkat tinggi dan terperinci, verifikasi dan pengujian, metrik perangkat lunak, estimasi, dan semua yang lainnya "hal-hal rekayasa perangkat lunak".
Selain itu, program fungsional menunjukkan modularitas dan struktur lainnya. Desain rinci Anda harus dinyatakan dalam konsep konsep dalam struktur itu.
sumber
Salah satu pendekatan adalah membuat DSL internal dalam bahasa pemrograman fungsional pilihan. "Model" kemudian adalah seperangkat aturan bisnis yang dinyatakan dalam DSL.
sumber
Lihat jawaban saya ke posting lain:
Bagaimana Clojure mendekati Separation of Concerns?
Saya setuju lebih banyak yang perlu ditulis pada subjek tentang bagaimana menyusun aplikasi besar yang menggunakan pendekatan FP (Ditambah lagi yang perlu dilakukan untuk mendokumentasikan UI berbasis-FP)
sumber
Walaupun ini mungkin dianggap naif dan sederhana, saya pikir "resep desain" (pendekatan sistematis untuk pemecahan masalah yang diterapkan untuk pemrograman seperti yang dianjurkan oleh Felleisen et al. Dalam buku mereka HtDP ) akan dekat dengan apa yang Anda cari.
Di sini, beberapa tautan:
http://www.northeastern.edu/magazine/0301/programming.html
http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.86.8371
sumber
Saya baru-baru ini menemukan buku ini: Pemodelan Domain Fungsional dan Reaktif
Saya pikir ini sangat sesuai dengan pertanyaan Anda.
Dari deskripsi buku:
sumber
Ada gaya "penghitungan program" / "desain dengan perhitungan" yang dikaitkan dengan Prof. Richard Bird dan kelompok Aljabar Pemrograman di Universitas Oxford (Inggris), saya tidak berpikir terlalu jauh untuk menganggap ini sebagai metodologi.
Secara pribadi sementara saya menyukai pekerjaan yang dihasilkan oleh kelompok AoP, saya sendiri tidak memiliki disiplin untuk mempraktikkan desain dengan cara ini. Namun itu kekurangan saya, dan bukan salah satu perhitungan program.
sumber
Saya telah menemukan Behavior Driven Development menjadi cocok alami untuk kode yang berkembang pesat di Clojure dan SBCL. Kelebihan nyata dari meningkatkan BDD dengan bahasa fungsional adalah bahwa saya cenderung menulis tes unit gandum yang jauh lebih baik daripada yang biasanya saya lakukan ketika menggunakan bahasa prosedural karena saya melakukan pekerjaan yang jauh lebih baik menguraikan masalah menjadi potongan-potongan fungsionalitas yang lebih kecil.
sumber
Jujur jika Anda ingin resep desain untuk program fungsional, lihat perpustakaan fungsi standar seperti Haskell's Prelude. Dalam FP, pola biasanya ditangkap oleh prosedur tingkat tinggi (fungsi yang beroperasi pada fungsi) sendiri. Jadi jika suatu pola terlihat, seringkali fungsi orde tinggi dibuat untuk menangkap pola itu.
Contoh yang baik adalah fmap. Fungsi ini mengambil fungsi sebagai argumen dan menerapkannya ke semua "elemen" dari argumen kedua. Karena ini adalah bagian dari kelas tipe Functor, setiap instance dari Functor (seperti daftar, grafik, dll ...) dapat diberikan sebagai argumen kedua untuk fungsi ini. Ini menangkap perilaku umum menerapkan fungsi ke setiap elemen argumen kedua.
sumber
Baik,
Umumnya banyak Bahasa Pemrograman Fungsional digunakan di universitas untuk waktu yang lama untuk "masalah mainan kecil".
Mereka semakin populer sekarang karena OOP mengalami kesulitan dengan "pemrograman paralel" karena "keadaan". Dan kadang-kadang gaya fungsional lebih baik untuk masalah yang dihadapi seperti Google MapReduce.
Saya yakin, ketika functioanl guys menabrak dinding [mencoba menerapkan sistem lebih dari 1.000.000 baris kode], beberapa dari mereka akan datang dengan metodologi rekayasa perangkat lunak baru dengan kata-kata buzz :-). Mereka harus menjawab pertanyaan lama: Bagaimana cara membagi sistem menjadi beberapa bagian sehingga kita dapat "menggigit" setiap bagian satu per satu? [bekerja berulang-ulang, tambahan dengan cara evolusioner] menggunakan Gaya Fungsional.
Tetapi apakah program-program fungsional akan digunakan untuk sistem sebesar itu? Apakah akan menjadi arus utama? Itu pertanyaannya .
Dan tidak ada yang bisa datang dengan metodologi realistis tanpa menerapkan sistem besar seperti itu, membuat tangannya kotor. Pertama, Anda harus membuat tangan Anda kotor kemudian menyarankan solusi. Solusi-Saran tanpa "rasa sakit dan kotoran" akan menjadi "fantasi".
sumber