Haruskah konsistensi lebih disukai daripada konvensi pemrograman?

14

Ketika mendesain sebuah kelas haruskah konsistensi dalam perilaku lebih disukai daripada praktik pemrograman yang umum? Untuk memberikan contoh spesifik:

Sebuah konvensi umum adalah ini: Jika sebuah kelas memiliki objek (misalnya ia menciptakannya) ia bertanggung jawab untuk membersihkannya setelah selesai. Contoh spesifik akan di .NET bahwa jika kelas Anda memiliki IDisposableobjek itu harus membuangnya di akhir masa pakainya. Dan jika Anda tidak memilikinya maka jangan menyentuhnya.

Sekarang jika kita melihat StreamWriterkelas di .NET maka kita dapat menemukan dalam dokumentasi bahwa itu menutup aliran yang mendasarinya ketika sedang ditutup / dibuang. Ini diperlukan dalam kasus di mana StreamWriterinstantiated dengan mengirimkan nama file sebagai penulis membuat aliran file yang mendasarinya dan karena itu perlu menutupnya. Namun seseorang juga dapat melewati aliran eksternal yang penulis juga tutup.

Ini telah mengganggu saya banyak kali (ya saya tahu Anda dapat membuat pembungkus non-penutupan tetapi bukan itu intinya) tetapi tampaknya Microsoft telah membuat keputusan bahwa lebih konsisten untuk selalu menutup aliran tidak peduli dari mana asalnya.

Ketika saya menemukan pola seperti itu di salah satu kelas saya, saya biasanya membuat ownsFooBarbendera yang disetel ke false dalam kasus-kasus di mana FooBardisuntikkan melalui konstruktor dan untuk benar sebaliknya. Dengan cara ini, tanggung jawab untuk membersihkannya diteruskan ke penelepon ketika dia memberikan contoh secara eksplisit.

Sekarang saya bertanya-tanya apakah mungkin konsistensi lebih disukai daripada praktik terbaik (atau mungkin praktik terbaik saya tidak sebagus itu)? Adakah argumen untuk / menentangnya?

Edit untuk klarifikasi

Dengan "konsistensi" yang saya maksud: Perilaku konsisten kelas selalu mengambil kepemilikan (dan menutup aliran) vs "praktik terbaik" untuk hanya mengambil kepemilikan atas suatu objek jika Anda membuatnya atau secara eksplisit memindahkan kepemilikan.

Adapun contoh di mana itu membuat:

Asumsikan Anda memiliki dua kelas yang diberikan (dari beberapa perpustakaan pihak ke-3) yang menerima aliran untuk melakukan sesuatu dengannya, seperti membuat dan memproses beberapa data:

 public class DataProcessor
 {
     public Result ProcessData(Stream input)
     {
          using (var reader = new StreamReader(input))
          {
              ...
          }
     }
 }

 public class DataSource
 {
     public void GetData(Stream output)
     {
          using (var writer = new StreamWriter(output))
          {
               ....
          }
     }
 }

Sekarang saya ingin menggunakannya seperti ini:

 Result ProcessSomething(DataSource source)
 {
      var processor = new DataProcessor();
      ...
      var ms = new MemoryStream();
      source.GetData(ms);
      return processor.ProcessData(ms);
 }

Ini akan gagal kecuali Cannot access a closed streamdi pengolah data. Ini sedikit dibangun tetapi harus menggambarkan intinya. Ada berbagai cara untuk memperbaikinya, namun saya merasa saya harus mengerjakan sesuatu yang seharusnya tidak perlu saya lakukan.

ChrisWue
sumber
Maukah Anda menguraikan MENGAPA perilaku ilustrasi dari StreamWriter menyebabkan Anda kesakitan? Apakah Anda ingin mengkonsumsi aliran yang sama di tempat lain? Mengapa?
Pekerjaan
@ Pekerjaan: Saya telah mengklarifikasi pertanyaan saya
ChrisWue

Jawaban:

9

Saya menggunakan teknik ini juga, saya menyebutnya 'handoff', dan saya percaya itu layak mendapatkan status suatu pola. Ketika suatu objek A menerima objek sekali pakai B sebagai parameter waktu konstruksi, ia juga menerima boolean yang disebut 'handoff', (yang defaultnya salah,) dan jika itu benar, maka pembuangan kaskade A ke pembuangan B.

Saya tidak mendukung untuk memperbanyak pilihan orang lain yang tidak beruntung, jadi saya tidak akan pernah menerima praktik buruk Microsoft sebagai konvensi yang mapan, saya juga tidak akan menganggap orang lain yang membabi buta mengikuti pilihan Microsoft yang tidak menguntungkan sebagai "perilaku konsisten" dengan cara, bentuk atau bentuk apa pun .

Mike Nakis
sumber
Catatan: Saya menulis definisi formal dari pola ini dalam sebuah posting di blog saya: michael.gr: Pola "Handoff" .
Mike Nakis
Sebagai perpanjangan lebih lanjut dari handOffpola, jika properti seperti itu diatur dalam konstruktor, tetapi ada metode lain untuk memodifikasinya, bahwa metode lain juga harus menerima handOffbendera. Jika handOffditentukan untuk item yang dipegang saat ini, item itu harus dibuang, maka item baru harus diterima dan handOffbendera internal diperbarui untuk mencerminkan status item baru. Metode ini tidak perlu memeriksa referensi lama dan baru untuk kesetaraan, karena jika referensi asli "diserahkan" semua referensi yang dimiliki oleh penelepon asli harus ditinggalkan.
supercat
@supercat saya setuju, tetapi saya memiliki masalah dengan kalimat terakhir: jika handoff benar untuk item yang saat ini dipegang, tetapi item yang baru dipasok sama dengan merujuk ke item yang saat ini dipegang, kemudian membuang item yang saat ini dipegang berlaku membuang item yang baru dipasok. Saya pikir memasok kembali barang yang sama harus ilegal.
Mike Nakis
Memasok kembali barang yang sama umumnya ilegal jika objek tidak berubah, tidak benar-benar memiliki sumber daya apa pun, dan tidak peduli jika dibuang. Misalnya, jika Disposepermintaan diabaikan untuk sikat sistem, metode "color.CreateBrush` mungkin akan membuat sikat baru (yang akan membutuhkan pembuangan) atau mengembalikan sikat sistem (yang akan mengabaikan pembuangan). Cara paling sederhana untuk mencegah menggunakan kembali suatu benda jika ia peduli dengan pembuangan, tetapi mengizinkan penggunaan kembali jika tidak, adalah membuangnya
supercat
3

Saya pikir Anda benar, jujur ​​saja. Saya pikir Microsoft mengacaukan kelas StreamWriter, khususnya, untuk alasan yang Anda jelaskan.

Namun, sejak itu saya telah melihat banyak kode di mana orang bahkan tidak mencoba membuang Stream mereka sendiri karena kerangka kerja akan melakukannya untuk mereka, jadi memperbaikinya sekarang akan lebih berbahaya daripada manfaatnya.

pdr
sumber
2

Saya akan pergi dengan konsistensi. Seperti yang Anda sebutkan kepada kebanyakan orang, masuk akal bahwa jika objek Anda membuat objek anak, ia akan memiliki dan menghancurkannya. Jika diberi referensi dari luar, pada suatu saat "luar" juga memegang benda yang sama dan tidak boleh dimiliki. Jika Anda ingin mentransfer kepemilikan dari luar ke objek Anda menggunakan metode Lampirkan / Lepaskan atau konstruktor yang menerima boolean yang menjelaskan kepemilikan yang ditransfer.

Setelah mengetik semua itu untuk jawaban yang lengkap, saya pikir sebagian besar pola desain umum tidak akan jatuh ke dalam kategori yang sama dengan kelas StreamWriter / StreamReader. Saya selalu berpikir bahwa alasan MS memilih agar StreamWriter / Reader secara otomatis mengambil alih kepemilikan adalah karena dalam contoh khusus ini, memiliki kepemilikan bersama tidak masuk akal. Anda tidak dapat memiliki dua objek berbeda secara bersamaan membaca dari / menulis ke aliran yang sama. Saya yakin Anda bisa menulis semacam pembagian waktu yang deterministik, tetapi itu akan menjadi semacam anti-pola. Jadi itu mungkin mengapa mereka berkata, karena tidak masuk akal untuk berbagi aliran, mari kita selalu mengambil alih. Tetapi saya tidak akan menganggap perilaku ini pernah menjadi konvensi generik di seluruh kelas untuk semua kelas.

Anda dapat menganggap Streams sebagai pola yang konsisten di mana objek yang dilewatkan tidak dapat dibedakan dari sudut pandang desain dan karenanya tanpa bendera tambahan, pembaca / penulis hanya mengambil alih kepemilikannya.

DXM
sumber
Saya mengklarifikasi pertanyaan saya - dengan konsistensi yang saya maksud adalah perilaku selalu mengambil kepemilikan.
ChrisWue
2

Hati-hati. Apa yang Anda anggap sebagai "konsistensi" mungkin hanya kumpulan kebiasaan acak.

"Mengkoordinasikan antarmuka pengguna untuk konsistensi", SIGCHI Bulletin 20, (1989), 63-65. Maaf, tidak ada tautan, ini adalah artikel lama. "... lokakarya dua hari yang terdiri dari 15 ahli tidak dapat menghasilkan definisi konsistensi." Ya, ini tentang "antarmuka", tetapi saya percaya bahwa jika Anda berpikir tentang "konsistensi" untuk sementara waktu, Anda akan menemukan Anda juga tidak dapat mendefinisikannya.

Bruce Ediger
sumber
Anda benar, jika para ahli datang bersama-sama dari lingkaran yang berbeda, tetapi ketika mengembangkan kode, saya merasa mudah untuk mengikuti beberapa pola yang ada (atau kebiasaan jika Anda mau) yang tim saya sudah kenal. Sebagai contoh, suatu hari salah satu dari teman-teman kami bertanya tentang beberapa operasi asinkron yang kami miliki dan ketika saya mengatakan kepadanya bahwa mereka berperilaku seperti Win32 Overlapped I / O, dalam 30 detik ia memahami seluruh antarmuka ... dalam hal ini keduanya saya sendiri dan dia datang dari server yang sama latar belakang Win32. Konsistensi ftw! :)
DXM
@ Bruce Saya mengerti maksud Anda - saya pikir sudah jelas apa yang saya maksud dengan konsistensi tetapi ternyata tidak.
ChrisWue