Pola untuk melewati konteks melalui rantai metode

19

Ini adalah keputusan desain yang tampaknya muncul cukup banyak: bagaimana melewati konteks melalui metode yang tidak membutuhkannya ke metode yang melakukannya. Apakah ada jawaban yang benar atau tergantung pada konteksnya.

Kode sampel yang membutuhkan solusi

// needs the dependency
function baz(session) {
  session('baz');
}

// doesn't care about the dependency
function bar() {
  baz();
}

// needs the dependency
function foo(session) {
   session('foo')
   bar();
}

// creates the dependency
function start() {
  let session = new Session();
  foo(session);
}

Solusi yang memungkinkan

  • threadlocal
  • global
  • objek konteks
  • melewati ketergantungan melalui
  • kari baz dan operasikan ke bar dengan dependensi yang ditetapkan sebagai argumen pertama
  • injeksi ketergantungan

Contoh di mana muncul

Pemrosesan permintaan HTTP

Objek konteks dalam bentuk atribut permintaan sering digunakan: lihat expressjs, Java Servlets atau .net's owin.

Penebangan

Untuk Java logging rakyat sering menggunakan global / lajang. Lihat pola log4j / commons logging / java logging.

Transaksi

Utas lokal sering digunakan untuk menyimpan transaksi atau sesi yang terkait dengan serangkaian panggilan metode untuk menghindari keharusan meneruskannya sebagai parameter ke semua metode yang tidak membutuhkannya.

Jamie McCrindle
sumber
Silakan gunakan contoh yang lebih bermakna.
Tulains Córdova
Saya telah menambahkan beberapa contoh dari mana itu muncul.
Jamie McCrindle
3
Saya maksudkan contoh kode yang lebih bermakna.
Tulains Córdova

Jawaban:

11

Satu-satunya jawaban yang adil adalah bahwa itu tergantung pada idiom paradigma pemrograman Anda. Jika Anda menggunakan OO, hampir pasti salah untuk meneruskan ketergantungan dari metode ke metode ke metode. Ini bau kode di OO. Bahkan, itulah salah satu masalah yang dipecahkan OO - sebuah objek memperbaiki konteks. Jadi dalam OO, satu pendekatan yang benar (selalu ada cara lain) adalah memberikan ketergantungan melalui kontruktor atau properti. Seorang komentator menyebutkan "Injeksi Ketergantungan" dan itu sah-sah saja, tetapi itu tidak sepenuhnya diperlukan. Berikan saja dependensi sehingga tersedia sebagai anggota untuk foodan baz.

Anda menyebutkan currying, jadi saya akan menganggap pemrograman fungsional tidak keluar dari pertanyaan. Dalam hal itu, ekuivalen filosofis dari konteks objek adalah penutupan. Setiap pendekatan yang, sekali lagi, memperbaiki dependensi sehingga tersedia untuk tanggungan berfungsi dengan baik. Currying adalah salah satu pendekatan semacam itu (dan itu membuat Anda terdengar pintar). Hanya ingat ada cara lain untuk menutup ketergantungan. Beberapa dari mereka elegan dan beberapa dari mereka mengerikan.

Jangan lupa tentang pemrograman berorientasi aspek . Tampaknya telah jatuh dari nikmat dalam beberapa tahun terakhir, tetapi tujuan utamanya adalah memecahkan persis masalah yang Anda gambarkan. Faktanya, contoh Aspect klasik adalah pencatatan. Di AOP, ketergantungan ditambahkan secara otomatis setelah kode lain ditulis. Orang-orang AOP menyebut ini " tenun ". Aspek umum ditenun ke dalam kode di tempat yang sesuai. Ini membuat kode Anda lebih mudah untuk dipikirkan dan sangat keren, tetapi juga menambah beban pengujian baru. Anda akan membutuhkan cara untuk menentukan artefak akhir Anda yang bagus. AOP memiliki jawaban untuk itu juga, jadi jangan merasa terintimidasi.

Hanya sedikit Roger
sumber
Mengklaim bahwa melewati parameter dalam metode OO adalah bau kode adalah pernyataan yang sangat kontroversial. Saya berpendapat sebaliknya: mendorong keadaan pencampuran dan fungsionalitas dalam kelas adalah salah satu kesalahan terbesar yang dibuat oleh paradigma OO dan menghindarinya dengan menyuntikkan dependensi langsung ke dalam metode, daripada melalui konstruktor adalah tanda dari karya yang dirancang dengan baik. kode, apakah OO atau tidak.
David Arno
3
@ DavidArno Saya sarankan menggunakan paradigma yang berbeda vs menyimpulkan objek statefulness adalah "salah satu kesalahan terbesar yang dibuat oleh paradigma OO" dan kemudian menghindari paradigma. Saya tidak menentang hampir semua pendekatan tetapi umumnya tidak suka kode di mana penulis memperjuangkan alat mereka. Status pribadi adalah fitur yang membedakan OO. Jika Anda menghindari fitur itu, Anda kehilangan sebagian kekuatan OO.
Scant Roger
1
@ Davidvido Kelas yang semuanya state dan tidak ada fungsi tidak memiliki mekanisme untuk menegakkan hubungan invarian di negara. Kelas semacam itu sama sekali bukan OO.
Kevin Krumwiede
@KevinKrumwiede, sampai batas tertentu, Anda telah menerapkan reducto ad absudium pada komentar saya, tetapi poin Anda masih dibuat dengan baik. Invarian pada keadaan adalah bagian penting dari "pindah dari OO". Jadi menghindari pencampuran fungsi dan keadaan harus memungkinkan fungsi yang cukup dalam objek keadaan yang diperlukan untuk mencapai invarian (bidang enkapsulasi yang ditetapkan oleh konstruktor dan diakses melalui getter).
David Arno
@ ScantRoger, saya setuju paradigma lain dapat diadopsi, yaitu paradigma fungsional. Menariknya, sebagian besar bahasa "OO" modern memiliki daftar fitur fungsional yang terus berkembang sehingga memungkinkan untuk tetap menggunakan bahasa-bahasa tersebut dan mengadopsi paradigma fungsi, tanpa "melawan alat".
David Arno
10

Jika bartergantung pada baz, yang pada gilirannya mengharuskan dependency, maka bardiperlukan dependencyjuga agar dapat digunakan dengan benar baz. Oleh karena itu, pendekatan yang benar akan baik melewati ketergantungan sebagai parameter untuk bar, atau menjelajah bazdan meneruskannya bar.

Pendekatan pertama lebih sederhana untuk diimplementasikan dan dibaca, tetapi menciptakan hubungan antara bardan baz. Pendekatan kedua menghapus kopling itu, tetapi bisa menghasilkan kode yang kurang jelas. Pendekatan mana yang terbaik karena itu kemungkinan akan tergantung pada kompleksitas dan perilaku kedua fungsi. Misalnya, jika bazatau dependencymemiliki efek samping, kemudahan pengujian kemungkinan akan menjadi pendorong besar di mana solusi dipilih.

Saya menyarankan semua opsi lain yang Anda usulkan bersifat "hacky" dan cenderung menyebabkan masalah baik dengan pengujian maupun dengan sulit untuk melacak bug.

David Arno
sumber
1
Saya hampir sepenuhnya setuju. Ketergantungan Injeksi mungkin merupakan pendekatan Non-"hacky" lain.
Jonathan van de Veen
1
@ JonathanvandeVeen, tentu saja tindakan dependencyberkeliling melalui parameter adalah ketergantungan injeksi?
David Arno
2
@DavidArno Ketergantungan kerangka kerja injeksi tidak menyingkirkan jenis dependensi ini, mereka hanya memindahkannya. Ajaibnya adalah bahwa mereka memindahkan mereka di luar kelas Anda, ke tempat di mana pengujian adalah Masalah Seseorang Lain.
Kevin Krumwiede
@ JonathanvandeVeen Saya setuju, injeksi ketergantungan adalah solusi yang valid. Sebenarnya itu yang paling sering saya pilih.
Jamie McCrindle
1

Berbicara secara filosofis

saya setuju dengan keprihatinan David Arno .

Saya membaca OP sebagai mencari solusi implementasi. Namun, jawabannya adalah mengubah desainnya . "Pola"? Desain OO, bisa dikatakan, semuanya tentang konteks. Ini adalah selembar kertas kosong yang mengandung kemungkinan.

Berurusan dengan kode yang ada adalah konteks yang berbeda.



Saya sedang mengerjakan 'masalah yang sama saat ini. Yah, saya sedang memperbaiki ratusan baris kode copy-n-paste yang sudah selesai sehingga nilai bisa disuntikkan.

Modularisasi kode

Saya membuang 600 baris kode duplikat lalu refactored jadi alih-alih "A memanggil B memanggil C memanggil D ..." Saya punya "Panggil A, kembali, Panggil B, kembali, Panggil C ...". Sekarang kita hanya perlu menyuntikkan nilai ke salah satu metode tersebut, katakanlah metode E.

Tambahkan parameter default ke konstruktor. Penelepon yang ada tidak berubah - "opsional" adalah kata yang digunakan di sini. Jika argumen tidak lulus nilai default digunakan. Maka hanya 1 baris yang diubah untuk meneruskan variabel ke dalam struktur modular yang telah direactored; dan perubahan kecil dalam metode E untuk menggunakannya.


Penutupan

Utas Pemrogram - "Mengapa program menggunakan penutupan?"

Pada dasarnya, Anda menyuntikkan nilai ke dalam metode yang mengembalikan metode yang disesuaikan dengan nilai-nilai tersebut. Metode khusus itu kemudian dieksekusi.

Teknik ini memungkinkan Anda untuk memodifikasi metode yang ada tanpa mengubah tanda tangannya.

radarbob
sumber
Pendekatan ini terlihat asing ...
Roger tentang masalah kopling sementara (tautan Anda), @Snowman. Penting agar perintah eksekusi yang diperlukan dienkapsulasi.
radarbob