Istilah (atau "pola"?) Untuk "Melakukan sesuatu jika belum dilakukan" [ditutup]

54

Kedengarannya cukup mendasar, saya tahu, tetapi saya baru-baru ini memiliki seorang kolega memberi tahu saya bahwa metode yang disebut startHttpServerterlalu rumit untuk dipahami karena hanya menjalankan server jika belum berjalan. Saya menemukan saya mendapat masalah ketika saya menanggapi dengan, "Serius? Saya sudah melakukan ini selama beberapa dekade - ini adalah pola umum dalam pemrograman." Lebih sering daripada saya mau mengakui bahwa dia kembali dengan beberapa bukti terdokumentasi yang menunjukkan bahwa seluruh komunitas pemrograman berada di belakang sudut pandangnya dan akhirnya saya merasa malu.

Pertanyaan : Apakah ada pola desain yang terdokumentasi di balik konsep metode yang tidak boleh jika tindakan yang diperlukan sudah berlaku? Atau, jika bukan sebuah pola, apakah ia memiliki nama juga? Dan jika tidak, apakah ada alasan untuk berpikir terlalu rumit untuk mempertimbangkan menulis metode dengan cara ini?

John Calcote
sumber
6
kedengarannya seperti caching - dan itu seringkali dianggap rumit memang (lihat juga Bagaimana cara saya menjelaskan $ {sesuatu} ke $ {seseorang}? )
gnat
8
Anda mendapat 3 jawaban berbeda, tetapi jawaban terbaik adalah, terapkan semuanya: ganti nama fungsi aslinya, bagi kodenya menjadi dua fungsi (di mana salah satu dari keduanya sekarang mendapatkan namanya startHttpServer), dan ya, istilah "idempoten" berlaku di sini baik.
Doc Brown
8
Bukti balik macam apa yang diberikan oleh kolega Anda? Bisakah Anda memberikan tautan ke sana?
Robert Harvey
5
Saya ingin tahu dari mana rekan Anda mendapatkan kutipannya. Setidaknya di sekitar Stack Overflow dan Rekayasa Perangkat Lunak, fungsi ini paling tidak akan dinamai tetapi tidak memiliki perilaku yang tidak biasa. Ingatlah bahwa itu bukan karena seseorang di suatu tempat meletakkan sesuatu di blog yang mewakili seluruh tampilan komunitas pemrograman. Bahkan nama-nama besar seperti Martin Fowler mengatakan beberapa hal yang sangat aneh dari waktu ke waktu. Kita semua hanyalah manusia.
T. Sar - Kembalikan Monica
4
Saya pikir cara yang lebih baik untuk berpikir tentang "Lakukan sesuatu jika belum dilakukan" adalah "Letakkan sistem pada keadaan ini". Tentu saja, jika sistem sudah pada keadaan itu, metode tidak akan melakukan apa pun - yang akan menjadi perilaku yang diharapkan.
T. Sar - Pasang kembali Monica

Jawaban:

127

Seperti yang sudah dikatakan NickWilliams : konsep yang dijelaskan OP disebut idempoten (kata benda Idempotensi ). Ini memang praktik umum, terutama di API tingkat tinggi.

TAPI: Ganti nama fungsinya.

Alih-alih startHttpServermenyebutnya makeSureHttpServerIsRunningatau ensureHttpServerIsRunning.

Ketika suatu fungsi dipanggil startHttpServer, pembaca mengharapkannya untuk memulai server HTTP; ketika dipanggil sepuluh kali berturut-turut, saya akan menjalankan sepuluh server. Fungsi Anda sebagian besar tidak melakukannya. Selain itu, nama dengan "mulai" menunjukkan bahwa jika saya ingin hanya satu server yang berjalan saya harus melacak apakah fungsi tersebut sudah dipanggil atau belum.

Ketika suatu fungsi dipanggil makeSureHttpServerIsRunning, saya menganggap itu akan melakukan hal-hal yang diperlukan untuk memastikan bahwa server HTTP sedang berjalan, kemungkinan besar dengan memeriksa apakah sudah berjalan, dan memulainya jika tidak. Saya juga berasumsi bahwa fungsi memastikan server benar-benar berjalan (memulai server mungkin melibatkan beberapa waktu di mana ia belum cukup berjalan).

gnasher729
sumber
83
Ini. Secara pribadi saya menggunakan "Pastikan" sebagai gantinya. Intinya adalah bahwa ini harus menjadi skema penamaan yang dipahami di tim Anda.
Euforia
3
atau sudah mulai melempar pengecualian jika sudah dimulai. seperti sqlconnection.open
Ewan
9
Saya selalu menggunakan fungsi "pastikan" untuk "lakukan jika belum".
Kaz
3
Nama lain yang sering saya lihat adalah getOrCreateSomething, yang hanya membuat pada panggilan pertama dan kemudian mengembalikan sesuatu
Fabich
5
@aroth Anda yakin tidak akan berharap untuk mencoba menemukan port yang tersedia dan mengembalikannya? Atau ditulis dengan buruk? ;)
jpmc26
33

Ubah nama menjadi EnsureServerRunning.

Sama sekali tidak ambigu dan jelas bahwa itu memastikan itu berjalan (jika tidak) tanpa menyiratkan restart jika itu.

(Alternatif:? StartServerIfNotRunning)

Stilez
sumber
1
Sebagian besar penelepon hanya peduli bahwa server berjalan setelah panggilan. Bagaimana itu dicapai, mereka tidak peduli.
gnasher729
29

Tidak benar-benar pola desain tetapi saya akan menyebut metode Anda idempoten . Ada istilah yang biasanya digunakan untuk merujuk ke panggilan jarak jauh tetapi deskripsi tampaknya cocok dengan apa yang Anda lakukan.

Metode Idempoten. Metode juga dapat memiliki properti "idempotence" di mana (selain dari kesalahan atau masalah kedaluwarsa) efek samping dari N> 0 permintaan identik adalah sama seperti untuk satu permintaan. ( Dari W3.org )

Efek samping server di sini adalah bahwa server http dimulai setelah metode dipanggil. Saya tidak melihat ada yang salah dengan metode melakukan ini.

Jika Anda memerlukan pola desain, saya kira Anda bisa mengekspos httpServer Anda sebagai singleton yang dimulai ketika diinisialisasi.

Nick Williams
sumber
5
Fungsi ini tidak idempoten jika memanggilnya sekali memulai server http, dan menyebutnya kedua kali tidak. Itu benar-benar kebalikan dari idempotensi. Akan idempoten jika setiap panggilan memulai server http baru.
Polygnome
36
@Polygnome Wikipedia mendefinisikan idempotence sebagai f (f (x)) = f (x) yang umumnya cocok dengan deskripsi Nick. Di sini fungsi input dan output akan menjadi status server implisit, sehingga status "server sedang berjalan" akan menjadi titik tetap untuk fungsi ini. Mungkin saya salah memahami artikel Wikipedia di sini, dapatkah Anda menautkan ke referensi lain?
amon
16
@ Polygnome: jawaban ini benar, idempotensi mengacu pada fungsi yang tidak masalah jika dipanggil sekali atau lebih dari sekali, hasilnya tetap sama. Di sini, hasilnya adalah satu menjalankan layanan http, terlepas dari berapa kali fungsi dipanggil.
Doc Brown
14
Kebingungan berasal dari fakta bahwa idempotensi pada intinya adalah konsep matematika yang diterapkan pada fungsi. Definisi ini bagus dan sederhana untuk mereka dan juga berfungsi dengan baik untuk bahasa fungsional murni. Segera setelah Anda mengenalkan efek sampingnya menjadi rumit - Anda harus mempertimbangkan lingkungan tempat Anda menjalankan fungsi sebagai argumen (tersirat) dan keluaran dari fungsi tersebut. Jika keadaan itu tidak diubah oleh panggilan lebih lanjut ke fungsi itu idempoten, kalau tidak maka tidak. Dalam hal ini negara yang relevan adalah "menjalankan server http" - jadi fungsinya idempoten.
Voo
10
@ Polygnome Maaf, Anda salah. Idempoten berarti bahwa permintaan memiliki efek yang sama apakah itu dieksekusi satu kali, atau lebih dari satu kali. Memulai N http server jika N duplikat dari permintaan diterima jelas bukan idempoten.
Kaz
7

Sebagai orang yang menerapkan ini alat , startHttpServer, Anda harus mencoba untuk membuatnya yang paling sederhana, halus dan mulus untuk digunakan ...

Logika fungsi

Secara teknis, dengan memisahkan startHttpServer's logika menjadi 2 fungsi dan memanggil mereka secara terpisah , semua apa yang Anda lakukan adalah bergerak startHttpServer ' s idempotency ke dalam kode memanggil kedua fungsi bukan ... Selanjutnya, kecuali jika Anda membungkus kedua logika dalam fungsi ketiga (yang adalah apa yang dilakukannya startHttpServerdi tempat pertama), ini memaksa Anda untuk menulis kode unDRYed, menduplikasinya secara eksponensial di mana pun Anda harus menelepon startHttpServer. Singkatnya, startHttpServer harus menyebut dirinya isHttpServerRunningfungsi.

Jadi maksud saya adalah:

  • Melaksanakan isHttpServerRunningfungsi karena ini mungkin diperlukan secara mandiri ...
  • Terapkan startHttpServermembuatnya digunakan isHttpServerRunninguntuk menentukan tindakan selanjutnya sesuai ...

Namun, Anda dapat startHttpServermengembalikan nilai apa pun yang mungkin diperlukan oleh pengguna fungsi ini, misalnya:

  • 0 => kegagalan memulai server
  • 1 => keberhasilan memulai server
  • 2 => server sudah dimulai

Penamaan fungsi

Pertama-tama, apa tujuan utama pengguna? Untuk memulai server HTTP , bukan?

Pada dasarnya, tidak ada masalah dengan berniat untuk memulai sesuatu yang sudah dimulai, AKA 1*1=1. Jadi, setidaknya bagi saya, menyebutnya " ensureHttpServerIsRunning" tampaknya tidak dibutuhkan secara kritis, saya akan lebih peduli tentang berapa lama, alami dan mudah diingat nama fungsi itu.

Sekarang jika Anda ingin tahu cara kerja secara detail fungsi di bawah tenda, ada dokumentasi atau sumber kode untuk itu, maksud saya seperti untuk fungsi lain dari library / framework / API / etc ...

Anda mempelajari fungsinya satu kali saat Anda menulisnya beberapa kali ...

Jadi, saya tetap dengan startHttpServeryang lebih pendek, lebih sederhana dan lebih eksplisit daripada ensureHttpServerIsRunning.

ClemC
sumber
1
Terutama karena metode ini harus mengambil beberapa argumen konfigurasi, seperti direktori root, pengaturan keamanan, mungkin nomor port, saya 100% nyaman dengan "mulai" di sini.
user949300
@ user949300: Penelepon "sureHttpServerIsRunning" tidak peduli dengan konfigurasi, direktori root, dll. Itu urusan orang yang mengimplementasikannya.
gnasher729
2
@ gnasher729 Itu mengasumsikan server http adalah Singleton. Lajang itu jahat. Dan mungkin tidak pantas di sini. Seseorang dapat dengan mudah memiliki beberapa server pada banyak port. Jika hanya ada satu server http, seluruh metode ini, IMO, desain buruk. Lebih baik memulai server sekali saja saat inisialisasi program.
user949300
2

Saya kira kolega Anda berarti startHttpServermelakukan terlalu banyak:

  • Memeriksa apakah server sudah berjalan,
  • Memulai server jika perlu.

Itu adalah dua bagian kode yang tidak terkait. Misalnya, situasi serupa terjadi ketika aplikasi desktop harus memastikan bahwa itu belum berjalan ketika diluncurkan; akan ada bagian dari kode yang menangani instance aplikasi (misalnya menggunakan mutex), dan kode yang akan memulai loop pesan aplikasi.

Ini berarti Anda tidak harus memiliki satu, tetapi setidaknya - dua metode :

  • isHttpServerRunning: boolean
  • startHttpServer

Titik masuk aplikasi akan memanggil metode pertama, dan yang kedua jika nilai baliknya false. Sekarang, setiap metode melakukan satu dan satu hal, dan mudah dimengerti.


¹ Jika logika yang diperlukan untuk mengetahui apakah server sudah berjalan terlalu kompleks, mungkin memerlukan pemisahan lebih lanjut menjadi beberapa metode.

Arseni Mourzenko
sumber
21
Sekarang hal lain yang memanggil kedua fungsi tersebut adalah melakukan dua hal, ditambah, mengekspos fungsi secara terpisah dapat memperkenalkan kondisi balapan. Tidak makan siang gratis.
whatsisname
1
Jika itu terlalu banyak untuk kolega, semoga sukses.
gnasher729
@ gnasher729: jawaban ini tidak bertentangan dengan Anda - justru sebaliknya, mungkin memang ide yang baik untuk menggabungkan penggantian nama metode dengan membaginya menjadi dua. Dan selama kita tidak melihat kode asli, kita tidak tahu seberapa rumit kode itu.
Doc Brown
10
Jawaban ini tampaknya menentang abstraksi dan KERING. Bagaimana jika startHttpServerdisebut lebih dari satu tempat dalam kode? Haruskah beberapa baris serupa disalin-tempel di mana-mana? Haruskah ini dilakukan dengan semua fungsi? Segera program Anda akan menjadi ukuran yang tak terbatas.
JacquesB
3
Saya pikir efek samping pertama dari pendekatan ini adalah awal dari startHttpServermetode ini akan terlihat seperti kira-kira if (isHttpServerRunning()){ return; }. Anda menegaskan aturan bisnis bahwa "tidak sah untuk memulai server http jika sudah berjalan", tetapi kemudian membuatnya menjadi tanggung jawab orang lain untuk menegakkan aturan itu. Ad-hoc dan berulang kali di setiap lokasi tempat mereka menelepon startHttpServer.
aroth
2

Karena Anda tidak menentukan bahasa, dalam JavaScript banyak perpustakaan memiliki fungsi "satu kali" misalnya Garis Bawah . Jadi, jika itu sudah tidak asing lagi bagi Anda, sebut saja itu pola "sekali" dan mungkin ganti nama metode Anda.

Saya sendiri, datang lebih banyak dari Jawa, istilah "caching" atau "evaluasi malas" datang ke pikiran. "Idempoten" secara teknis benar dan merupakan pilihan yang baik, esp. jika Anda memiliki latar belakang yang lebih fungsional.

pengguna949300
sumber
Mungkin tidak "sekali", jika server dapat dihentikan.
gnasher729
@ gnasher729. Saya bisa membayangkan metode yang jarang digunakan restartHttpServer(). Tapi hanya berhenti - apa gunanya di sana? Anda suka kegagalan koneksi sporadis? :-)
user949300
Tidak perlu ada kasus penggunaan untuk hanya menghentikan server HTTP karena mungkin telah dihentikan oleh sesuatu di luar program itu sendiri - server HTTP mungkin telah rusak dan mati, admin mungkin secara manual menghentikannya untuk beberapa alasan, dll
Dave Sherohman
Dave, kecelakaan itu "luar biasa" dan harus ditangani seperti itu. Selain itu, karena kerusakan dapat terjadi kapan saja , bukankah setiap baris kode Anda harus demikian ensureRunning()? :-) Adapun admin menghentikannya, memiliki kode lain ini terus-menerus restart sementara admin mencoba untuk memodifikasi atau memperbaiki sesuatu akan sangat menjengkelkan dan salah. Biarkan admin memulai kembali, bukan kodenya.
user949300
-2

Saya lebih suka startHttpServerIfNotIsRunning.

Dengan cara ini, kondisinya disebutkan dengan jelas dalam nama metode. Ensureatau makeSuresepertinya agak kabur bagi saya, karena itu bukan ekspresi teknis. Sepertinya kita tidak tahu persis apa yang akan terjadi.

Herr Derb
sumber
Saya berasumsi Anda bukan penutur asli? Karena memastikan memiliki definisi yang tepat tentang apa yang kita inginkan. Tetapi tetap merupakan poin yang adil bahwa dokumentasi dan API seharusnya tidak mengharuskan tingkat penutur asli Bahasa Inggris dapat dipahami.
Voo
1
Terima kasih saya persis bukan apa Ensureartinya. Saya masih tidak suka kata ini untuk ungkapan teknis. Ensureadalah sesuatu yang manusiawi. Suatu sistem tidak dapat memastikan apa pun, hanya akan melakukan apa yang seharusnya dilakukan.
Herr Derb
2
@ Voo - Saya seorang penutur asli dan saya tidak yakin apa yang dimaksud dengan means.
Jonathan Cast
Karena semua orang yang memposting di sini memiliki akses ke internet, mengapa mereka tidak mencari definisi 'Pastikan' jika mereka tidak yakin apa artinya? Sedikit hal-hal sepele ... banyak orang menukar penggunaan 'Pastikan' dan 'Pastikan' tanpa menyadari bahwa menggunakan salah satu dari kata-kata itu secara tidak sengaja dapat membuat mereka 'secara hukum' bertanggung jawab sementara yang lain tidak.
Dunk
@cast: Serius?
gnasher729
-3

Apa yang seharusnya dikatakan oleh kolega Anda adalah bahwa Anda tidak memiliki bisnis yang menulis metode itu. Sudah ditulis berkali-kali, dan ditulis lebih baik daripada kemungkinan Anda menulisnya. Misalnya: http://docs.ansible.com/ansible/latest/systemd_module.html https://docs.saltstack.com/en/latest/ref/states/all/salt.states.service.html

Dari perspektif arsitektur, memiliki sedikit kode sewenang-wenang mengelola server web adalah hal-hal buruk. Kecuali jika mengelola layanan secara eksklusif apa yang dilakukan kode Anda. Tapi saya kira Anda tidak menulis monit (atau kubernetes atau ...).

Andrew
sumber
2
Bahasanya adalah Java dan metode ini dirancang untuk memulai server tertanam grizzly dalam aplikasi Jersey yang berdiri sendiri. Saya tidak bisa menyalahkan keakuratan komentar Anda, mengingat saya tidak memberi Anda banyak konteks, tetapi Anda berasumsi banyak dalam membuat pernyataan Anda. Tidak masalah untuk memiliki metode yang memanggil jetty atau grizzly untuk memulai server.
John Calcote
1
Ada jutaan aplikasi bisnis yang menyertakan server HTTP yang di-host sendiri untuk menyediakan API. Dan semua itu akan memerlukan beberapa kode yang sangat sederhana yang benar-benar mengatur perpustakaan yang mereka gunakan dan memberikan informasi seperti antarmuka mana yang akan diikat, port mana yang akan digunakan, pengaturan keamanan, dll. Mempunyai startServerfungsi atau sejenisnya sama sekali tidak umum . Itu tidak berarti Anda akan menulis detail tingkat rendah seluk beluk.
Voo