Re-inventing desain sistem untuk Scala

35

Banyak, banyak, bulan yang lalu, saya melakukan master saya di Rekayasa Perangkat Lunak Berorientasi Objek. Saya membahas semuanya: inisiasi proyek, persyaratan, analisis, desain, arsitektur, pengembangan, dll, dll. Buku IT favorit saya sepanjang masa adalah Mengembangkan Perangkat Lunak Berorientasi Objek, Pendekatan Berbasis Pengalaman (IBM-1996). Sebuah buku yang dibuat oleh sekelompok ahli sejati pada masanya. Ini menjelaskan pendekatan sentris produk kerja untuk analisis berorientasi objek, desain dan metode pengembangan.

Saya mendesain dan mengembangkan dan merasa bahagia dan berada di puncak permainan saya, tetapi saya mulai merasa sedikit ketinggalan zaman: Gerakan lincah menjadi mode saat itu dan merek-ulang beberapa pendekatan iteratif dan inkremental yang terkenal dengan kata-kata pinggul baru. Tiba-tiba pengembang yang tidak berpengalaman mulai mengerutkan kening ketika saya mengatakan "persyaratan" atau "arsitektur" seolah-olah hal-hal ini digantikan oleh sihir.

Desain dan pengembangan hilang itu menyenangkan dan saya akan meninggalkan seluruh industri TI di belakang saya.

Kemudian saya menemukan Scala. Oh, seperti hujan lembut di jalan berdebu. Segalanya menjadi jernih, udaranya menjadi manis lagi dan sedikit cahaya hard drive saya akan berkedip jauh ke dalam malam, dengan riang menemani saya di dunia penemuan saya.

Saya suka desain sistem. Saya seorang arsitek, lebih dari seorang programmer. Saya suka menganalisis, mendesain, berpikir, berdebat, meningkatkan - Saya suka desain yang sederhana, bersih dan segar.

Bagaimana kita merancang solusi Scala murni?

Tentunya diagram urutan konvensional, diagram interaksi, diagram objek, dll, semua bisa diganti atau ditingkatkan untuk kebaikan pencampuran imperatif dan fungsional sistem berorientasi objek. Tentunya ada celah untuk sesuatu yang sama sekali berbeda!

Saya tidak mencari kerumitan yang membengkak - Saya mencari kesederhanaan yang fleksibel. Mirip seperti Scala sebenarnya sederhana dan mudah (walaupun berbeda), tetapi dapat diperpanjang sesuai kebutuhan Anda seperti sepasang celana Yoga. Harus ada sistem desain untuk bahasa yang indah ini yang akan mulai sederhana, dan dapat diperluas agar sesuai dengan kebutuhan Anda dan domain Anda. Tentunya UML tidak bisa seperti itu!

Jadi bagaimana kita merancang sistem Scala murni? Bayangkan sejenak, Anda memiliki kemewahan untuk merancang sistem yang lengkap dari awal, dan tahu bahwa Anda hanya akan menggunakan Scala - Bagaimana model Anda terlihat? Apa jenis diagram yang harus Anda uraikan tentang struktur dan perilaku? Bagaimana Anda memodelkan Opsi, korek api, mixin, Object tunggal, dll. Tanpa ditarik ke dalam kerumitan dalam memperluas teknik pemodelan yang ada alih-alih alat-alat yang segar, ringan, dan inovatif.

Apakah ada desain / proses / solusi semacam itu , atau sudah saatnya menciptakannya?

Mendongkrak
sumber
+1 untuk "dapat diperpanjang agar sesuai dengan kebutuhan Anda seperti sepasang celana Yoga."
Russell
FWIW, saya telah melihat pertanyaan tentang UML dan Scala, yang mungkin menarik bagi Anda. Juga, jika Anda pergi ke sisi fungsi, maka notasi teori kategori mungkin layak untuk dilihat. Gregory Meredith biasanya menautkan ke beberapa presentasi menarik tentang hal itu, meskipun saya tidak yakin apa yang dia miliki di blog-nya - bagaimanapun juga patut dicoba.
Daniel C. Sobral
Ini sangat berharga ;-) Ini memberi saya arahan untuk menggali. Terima kasih Daniel
Jack
Patut diperiksa, UML "dimodifikasi" untuk Scala, github.com/mnn/Dia2Scala/blob/master/other/Notation.md
WeiChing Lin

Jawaban:

12

Saya tidak bisa memberikan jawaban untuk pertanyaan ini, tetapi izinkan saya menawarkan beberapa pemikiran.

Saya seorang mahasiswa teknik komputer di sebuah universitas, dan pada semester terakhir saya dan sebuah kelompok mengerjakan proyek perangkat lunak besar di mana bahasa utama kami adalah Scala. Kami melakukan desain dengan cara tradisional: use case, model domain, diagram urutan, diagram kelas UML; beban biasa. Dan, seperti yang sudah Anda sadari, mereka tidak cocok. Berikut ini beberapa alasannya.

Pertama, pertimbangkan diagram urutan. The nuansa dari diagram urutan dari beberapa stabil, berumur panjang, aktor semi-otonom berbicara satu sama lain. Dalam Scala, objek cenderung dibuat untuk waktu yang singkat. Selain itu, kelas kasus mendorong objek "bodoh", dengan hanya data dan tanpa kode, sehingga konsep meneruskan pesan tidak pada tempatnya. Tetapi tantangan tersulit untuk diagram urutan adalah bahwa mereka tidak memiliki cara yang baik untuk menggabungkan fungsi-fungsi kelas satu; dalam desain OO hanya menggunakan callback di sini atau di sana itu dapat dibuat untuk bekerja, tetapi jika itu adalah sarana utama Anda mentransfer kontrol Anda akan menemukan diagram urutan mencerminkan sedikit dari kode Anda yang sebenarnya.

Diagram kelas UML lebih cocok untuk Scala; ya, mixin tidak masuk dengan baik, tapi itu adalah sesuatu yang bisa "tweak" daripada dirombak. Tapi saya pikir itu mungkin tidak penting.

Pertimbangkan lagi mengapa Scala memiliki "benda bisu". Jika Anda menulis kelas kasus tanpa metode:

case class NewInvite(user: User, league: League)

Anda tidak memerlukan metode apa pun, karena tipe anggota menjanjikan sesuatu tentang apa yang dapat Anda lakukan dengan mereka. Bahkan, mereka menjanjikan segalanya . Dan, dalam setiap ruang lingkup dalam program, variabel-variabel dalam ruang lingkup dan tipenya menjanjikan sesuatu tentang ke mana kode tersebut dapat berjalan pada saat itu.

Jadi, mungkin, jika kita mundur sedikit, dan, alih-alih memikirkan dengan cermat tentang tipe per se, rancang program kita dalam konteks apa yang akan dijalani kode kita, dan apa yang dijanjikan kepada programmer (dan, akhirnya, pengguna ) di masing-masing konteks ini, kami akan menemukan deskripsi yang lebih sesuai untuk Scala. Namun saya hanya menebak-nebak di sini, dan saya pikir Anda benar bahwa masih banyak yang perlu dieksplorasi di area ini.

Owen
sumber
"Apa yang dijanjikan kepada programmer" - yang memiliki cincin yang bagus untuk itu ;-)
Jack
12

UML muncul dari "Bubble Wars" pada akhir 80-an dan awal 90-an. Itu selalu merupakan kompromi untuk notasi , bukan metode desain. Banyak calumny yang dikaitkan dengan UML secara diam-diam adalah hasil dari metode desain yang mengatakan "Langkah 1: menggambar model kelas."

Saya menyarankan agar kita tidak memerlukan metode desain baru sebanyak kita harus merevitalisasi beberapa yang lama. Mulailah dengan mendefinisikan dan mengalokasikan tanggung jawab, kemudian bergerak untuk memahami implementasi mereka, dan ikuti dengan notasi dan representasi yang baik untuk mengkomunikasikan ide-ide itu.

Tanggung jawab

CRC bekerja dengan baik untuk Scala seperti halnya untuk bahasa OO lainnya, yang mengatakan bahwa ia bekerja dengan sangat baik! Memodelkan alokasi tanggung jawab oleh aktor antropomorfisasi dan memahami kolaborasi mereka masih bekerja dengan baik.

Pada skala besar, Anda masih harus mendesain dengan objek. Batas objek adalah batas enkapsulasi. Simpan rincian keadaan dan implementasi yang bisa berubah di dalam batas objek tersebut. Kelas kasus membuat objek antarmuka yang baik, begitu pula koleksi vanila dan struktur data.

Memahami implementasi

Lewati struktur data ini melintasi batas objek daripada objek lain, dan Anda akan menemukan a) lebih mudah untuk menyembunyikan isi objek di dalam, dan b) implementasi fungsional perilaku objek sangat masuk akal.

Anda tidak akan ingin memperlakukan struktur skala besar dari program Anda sebagai "kumpulan besar fungsi-fungsi kecil". Sebagai gantinya, Anda secara alami akan condong ke antarmuka sempit antara bagian-bagian sistem yang terisolasi. Ini terlihat dan terasa sangat mirip objek, jadi silakan saja dan implementasikan sebagai objek!

Representasi dan Notasi

Jadi, dalam struktur desain ini, Anda masih perlu mengeluarkan pikiran dari pikiran Anda dan membaginya dengan orang lain.

Saya menyarankan agar UML masih dapat berguna untuk menggambarkan struktur skala besar sistem Anda. Ini tidak akan membantu pada level detail yang lebih rendah.

Pada detail halus, cobalah teknik pemrograman melek huruf.

Saya juga suka menggunakan minidocs. Minidoc adalah dokumen satu atau dua halaman yang menjelaskan beberapa segi tertentu dari perilaku sistem. Itu harus berada di dekat kode sehingga diperbarui, dan harus cukup singkat untuk memungkinkan pembelajaran tepat waktu. Dibutuhkan beberapa latihan untuk mencapai tingkat abstraksi yang tepat: perlu cukup spesifik untuk berguna tetapi tidak terlalu spesifik sehingga menjadi tidak valid dengan perubahan kode berikutnya.


sumber
9

Bagaimana kita merancang solusi Scala murni?

Saya pikir Anda mengambil sudut yang salah dengan pertanyaan ini. Desain keseluruhan Anda untuk solusi tidak boleh dikaitkan dengan bahasa tertentu; melainkan harus sesuai dengan masalah yang dihadapi. Hal yang hebat tentang Scala adalah ia cukup fleksibel untuk tidak menghalangi Anda ketika tiba saatnya untuk mengimplementasikan desain Anda. Java, di sisi lain, memberlakukan beberapa keputusan desain (terutama, mentalitas segalanya-adalah-kelas) yang akan membuatnya terasa agak kikuk.

Saat mendesain, cobalah memodelkan keadaan secara eksplisit . Data yang tidak dapat diubah dan keadaan eksplisit menyebabkan desain yang jauh lebih mudah untuk dipikirkan. Sebagian besar waktu, ini akan diterjemahkan dengan baik ke dalam solusi pemrograman fungsional, meskipun kadang-kadang Anda mungkin menemukan bahwa itu lebih efisien atau lebih mudah untuk menggunakan mutasi dan gaya imperatif; Scala mengakomodasi keduanya dengan cukup baik.

Alasan lain mengapa Scala begitu hebat untuk keluar dari jalan Anda adalah kemampuannya untuk membuat DSL yang sesuai dengan kebutuhan Anda . Anda dapat membuat bahasa kecil Anda sendiri yang memecahkan masalah tertentu dengan cara "normal", dan kemudian cukup menerapkannya dalam Scala.

Singkatnya, poin utama saya adalah bahwa Anda tidak boleh mencoba "merancang solusi Scala murni". Anda harus merancang solusi dengan cara yang paling alami dan dapat dipahami, dan kebetulan Scala adalah alat yang sangat fleksibel yang dapat membantu Anda mengimplementasikan solusi tersebut dalam berbagai cara. Ketika semua yang Anda ketahui adalah palu, setiap masalah mulai terlihat seperti paku, jadi pastikan untuk menjelajahi berbagai pendekatan yang tersedia untuk Anda. Jangan mencoba untuk menutup proses desain ke langkah X, Y, dan Z, jangan sampai Anda kehilangan kemungkinan A sampai W.

Dan Burton
sumber
4

Anda benar bahwa OOD terbatas dan ketinggalan zaman. Keuntungan utamanya adalah bahwa ia sangat ditentukan dan diritualkan, dengan nama-nama mewah untuk masing-masing dan setiap pola dan trik desain yang sepele. Anda tidak akan menemukan sistem yang sebanding untuk pendekatan yang lebih modern dan rasional untuk desain sistem.

Saya hanya dapat membuat daftar beberapa hal: pendekatan semantik untuk desain, yang sebagiannya disebut Pemrograman Berorientasi Bahasa (dan banyak digunakan oleh komunitas Scala) dan Desain Berbasis Domain. Yang terakhir adalah pandangan yang disederhanakan dan OOPised pada pendekatan desain semantik yang lebih umum, Anda hanya akan dapat menikmatinya jika Anda suka OOD.

Saya juga merekomendasikan untuk belajar satu atau dua trik dari komunitas pemrograman fungsional lainnya - misalnya Haskell dan Skema, tidak semua pengetahuan desain mereka telah ditransfer ke Scala.

Logika SK
sumber
3

Pertanyaannya adalah: Bagaimana kita merancang solusi Scala murni?

Jawabannya seperti,

  1. Kami menggunakan XYZ karena ....
  2. Kami menggunakan ABC tetapi seperti itu ....
  3. Kami mulai menggunakan XYZ, tetapi kemudian menemukan ABC menjadi lebih baik karena ...

akan menunjukkan beberapa kedewasaan, atau keberadaan yang diterima secara umum, dari seperangkat alat atau solusi tersebut.

Komentar yang berkaitan dengan apakah pantas untuk mempertimbangkan set alat desain khusus Scala adalah masalah yang berbeda. Pendapat saya tentang ini adalah bahwa Scala revolusioner dengan cara menggabungkan pemrograman imperatif dan fungsional. Scala menangkap napas menakjubkan praktik pengembangan, konsep dan prinsip, dan dengan demikian, dalam menemukan solusi yang sangat cocok untuk Scala, kita mungkin akan menemukan solusi yang mana subset akan melayani banyak bahasa lainnya dengan sangat baik juga.

Apakah aman mengatakan bahwa kita tahu jawaban untuk pertanyaan mundur:

"... apakah ini saatnya untuk menciptakannya?"

Mungkinkah itu ya ? (Di mana ' ya ' tidak meresepkan cara untuk solusi. Itu bisa dicapai baik dengan memperluas solusi yang ada, atau membuat satu dari bawah ke atas. Memang mengakui bahwa ada kekurangan yang signifikan dalam opsi desain yang ada)

Anda boleh memilih jawaban saya jika Anda tidak setuju. Saya akan menerimanya seperti seorang pria.

Mendongkrak
sumber
Revolusioner? Mengapa? Lisp telah menjadi kombinasi sempurna dari pendekatan imperatif dan fungsional selama beberapa dekade. Scala menarik karena sistem tipenya.
SK-logic
3
Permintaan maaf jika 'revolusioner' adalah kata yang kuat dan kuat. Saya tidak menyiratkan Scala menciptakan kembali roda - tetapi tentu saja karet itu bisa kembali. Sebenarnya, Scala adalah campuran yang indah dari semua kebaikan banyak bahasa pemrograman. Saya yakin itu meminjam banyak dari Lisp juga. Bagi saya, dan saya tidak mengklaim orang lain, bahasa Scala terasa revolusioner karena saya memikirkan kode dengan cara yang sangat alami. Bahasa desain / pemodelan yang melakukan hal yang sama pasti akan gratis. Itulah satu-satunya poin saya - jangan tersinggung Lisp dan semua bahasa hebat lainnya.
Jack
2

Saya tidak akan membedakan antara bahasa, saya akan melakukannya dengan cara yang sama di java. Namun saya dari sekolah OO bukan dari fungsional (Haskell aljabar satu atau yang lisp). Desain aplikasi Haskell benar-benar berbeda dari Scala atau Clojure. Juga desain aplikasi Scala.js berbeda karena DOM stateful - sulit untuk melawan bagian stateful dari aplikasi Anda yang wajib. Seiring waktu saya terbiasa melakukannya dengan cara OO, sambil mencoba untuk menghilangkan keadaan dan mutabilitas sebanyak mungkin. Jika saya adalah penggemar typelevel atau scalaz, aplikasi saya mungkin akan terlihat sangat berbeda.

  1. Memutuskan tumpukan teknologi dan keputusan desain utama seperti tanggung jawab klien / server, sifat terdistribusi - apakah kita benar-benar membutuhkan ketekunan data? Apa yang paling sesuai dengan aplikasi kita? (tentu saja belum perpustakaan atau kerangka kerja)

  2. Dimulai dengan hanya satu App.scalafile di mana saya mendefinisikan jenis kunci (ciri dan kelas kasus) dan peran - banyak perenungan dan pemikiran saat menjadi AFK. Sangat mudah untuk bermain dengannya saat dalam satu file. Bahkan prototipe mungkin muncul jika itu adalah aplikasi yang sederhana. Saya mendedikasikan banyak waktu untuk fase ini karena tidak ada yang lebih penting daripada memiliki prototipe yang paling benar dan terutama transparan dan masuk akal. Itu semua membantu untuk membuat perenungan kita lebih akurat dan meningkatkan kemungkinan keberhasilan.

  3. Sekarang ketika kita telah membuktikan kebenaran dari solusi kita, kita memiliki visi yang jelas dan semua masalah potensial terungkap, sekarang saatnya untuk memikirkan perpustakaan atau metodologi pihak ketiga seperti apa yang harus kita bawa. Apakah kita memerlukan kerangka permainan atau hanya beberapa lib? Apakah kita akan membuat aktor-aktor kenegaraan ini, apakah kita benar-benar membutuhkan ketahanan Akka,, ..., atau mungkin tidak? Apakah kita menggunakan Rx untuk stream ini? Apa yang kami gunakan untuk UI sehingga kami tidak akan menjadi gila setelah memiliki 10 fitur interaktif?

  4. Bagi App.scalafile menjadi beberapa karena sifat dan kelas baru muncul dan menjadi lebih besar. Antarmuka mereka harus menceritakan tentang peran mereka. Sekarang saatnya untuk memikirkan tentang penggunaan kelas tipe dan polimorfisme ad-hoc jika memungkinkan. Sehingga siapa pun yang memanggil antarmuka Anda, itu harus fleksibel untuk mereka. Intinya adalah untuk mengimplementasikan fungsionalitas umum saja, meninggalkan detail ke pemanggil. Kadang-kadang apa yang Anda coba implementasikan mungkin mirip-Turing-jadi Anda harus menyediakan cara bagi penelepon untuk mengimplementasikan secara spesifik sendiri alih-alih menumpuk permintaan fitur di GitHub. Barang ini sulit untuk ditambahkan nanti. Itu harus dipikirkan sejak awal. Serta merancang DSL.

  5. memikirkan desain karena aplikasi akan tumbuh dan harus siap untuk menahan fitur baru, optimisasi, ketekunan, UI, remoting, dll - hal-hal yang belum dilaksanakan atau entah bagaimana diejek atau diabstraksi. Ingat bahwa walaupun semuanya ada dalam satu file, cukup mudah untuk mengubah banyak hal. Otak manusia mulai kehilangannya setelah tersebar di 10+ file. Dan itu seperti tumor yang tumbuh, begitulah rekayasa awal dimulai. Saya perhatikan bahwa aplikasi saya berakhir dengan beberapa fungsi parsial panjang yang membentuk semacam tulang punggungnya. Saya merasa mudah untuk bekerja dengannya. Saya kira saya terinfeksi oleh aktor Akka.

  6. Sekarang setelah fitur aktual pertama ada, bukan hanya fungsi inti, sekarang saatnya untuk mulai menulis tes. Kenapa terlambat? Karena selama waktu ini Anda mungkin telah melalui penulisan ulang besar dan Anda akan membuang waktu untuk mempertahankan tes tersebut. Seseorang seharusnya tidak menulis tes kecuali seseorang benar-benar yakin tidak akan ada desain ulang utama. Saya lebih suka tes integrasi yang mudah menahan refactoring terus menerus dan saya tidak akan menghabiskan lebih banyak waktu pengujian yang benar-benar mengembangkan barang. Itu terjadi pada saya beberapa kali sehingga saya menghabiskan terlalu banyak waktu untuk mempertahankan tes. Memperbaiki bug potensial tentu akan membayar lebih daripada tes buruk. Tes yang buruk atau tes yang ditulis sejak saat pertama dapat menyebabkan rasa sakit yang hebat. Perhatikan bahwa saya tentu bukan penggemar TDD - IMHO itu omong kosong.

  7. Refactoring, membuat dan menjaga desain aplikasi tetap mudah dibaca, peran komponen jelas. Ketika aplikasi tumbuh tidak ada cara itu tetap seperti itu kecuali jika Anda seorang programmer jenius dan jika Anda membaca jawaban ini Anda mungkin tidak :-) Tidak juga. Biasanya beberapa hal stateful ada karena Anda tidak tahu bagaimana cara menghindari itu - yang dapat menyebabkan masalah, cobalah untuk menghilangkannya. Pastikan juga untuk menghilangkan bau kode yang mengganggu Anda selama beberapa waktu. Pada titik ini manajer proyek Anda (jika ada) mungkin mulai menggelengkan kepalanya. Katakan padanya itu akan membuahkan hasil.

  8. Selesai, apakah aplikasinya dirancang dengan baik? Itu tergantung: Apakah komponen memiliki peran yang terdefinisi dengan baik yang benar-benar masuk akal bahkan di luar kepala Anda? Bisakah Anda mengambil beberapa dari mereka dan membukanya tanpa banyak usaha? Adakah pemisahan yang jelas dari keprihatinan di antara mereka? Bisakah Anda menambahkan fitur baru tanpa khawatir akan macet di tempat lain (pertanda Anda tahu persis apa yang Anda lakukan)? Secara umum, itu harus dibaca sebagai buku dengan solusi pragmatis langsung untuk masalah. Yang merupakan IMHO tanda utama desain yang bagus.

Singkatnya, ini hanya kerja keras dan waktu yang mungkin tidak akan Anda dapatkan dari manajer Anda. Semua yang saya jelaskan di sini membutuhkan banyak waktu, tenaga, kopi, teh, dan terutama memikirkan AFK. Semakin banyak Anda memberikannya, semakin baik desainnya. Tidak ada resep umum untuk merancang aplikasi, akal sehat, pragmatisme, KISS, kerja keras, pengalaman berarti desain yang baik.

lisak
sumber