Mengirim objek dua kali ke metode yang sama atau menggabungkan dengan antarmuka gabungan?

15

Saya memiliki metode yang membuat file data setelah berbicara dengan papan digital:

CreateDataFile(IFileAccess boardFileAccess, IMeasurer boardMeasurer)

Di sini boardFileAccessdan boardMeasurermerupakan contoh yang sama dari Boardobjek yang mengimplementasikan keduanya IFileAccessdan IMeasurer. IMeasurerdigunakan dalam kasus ini untuk metode tunggal yang akan menetapkan satu pin di papan aktif untuk membuat pengukuran sederhana. Data dari pengukuran ini kemudian disimpan secara lokal di papan menggunakan IFileAccess. Boardterletak di proyek terpisah.

Saya sampai pada kesimpulan bahwa CreateDataFilemelakukan satu hal dengan melakukan pengukuran cepat dan kemudian menyimpan data, dan melakukan keduanya dengan metode yang sama lebih intuitif untuk orang lain yang menggunakan kode ini kemudian harus melakukan pengukuran dan menulis ke file sebagai pemanggilan metode terpisah.

Bagi saya, rasanya aneh untuk melewatkan objek yang sama ke metode dua kali. Aku sudah dianggap membuat antarmuka lokal IDataFileCreatoryang akan memperpanjang IFileAccessdan IMeasurerkemudian memiliki implementasi yang berisi Boardcontoh yang hanya akan memanggil diperlukan Boardmetode. Menimbang bahwa objek papan yang sama akan selalu digunakan untuk pengukuran dan penulisan file, apakah merupakan praktik yang buruk untuk melewatkan objek yang sama ke metode dua kali? Jika demikian, apakah menggunakan antarmuka lokal dan implementasi merupakan solusi yang tepat?

pavuxun
sumber
2
Sulit untuk tidak mungkin mengetahui maksud kode Anda dari nama yang Anda gunakan. Antarmuka bernama IDataFileCreator yang diteruskan ke metode bernama CreateDataFile sangat membingungkan. Apakah mereka bersaing untuk tanggung jawab untuk mempertahankan data? Lagi pula, kelas apa itu metode CreateDataFile? Mengukur tidak ada hubungannya dengan data yang ada, begitu banyak yang jelas. Pertanyaan Anda bukan tentang masalah terbesar yang Anda hadapi dengan kode Anda.
Martin Maat
Apakah mungkin objek akses file Anda dan objek pengukur Anda mungkin dua objek yang berbeda? Saya akan mengatakan ya. Jika Anda mengubahnya sekarang, Anda harus mengubahnya kembali di versi 2 yang mendukung pengukuran di seluruh jaringan.
user253751
2
Inilah pertanyaan lain - mengapa akses file data dan objek pengukuran sama?
user253751

Jawaban:

40

Tidak, ini baik-baik saja. Ini hanya berarti bahwa API direkayasa berlebihan terkait dengan aplikasi Anda saat ini .

Tetapi itu tidak membuktikan bahwa tidak akan pernah ada kasus penggunaan di mana sumber data dan pengukur berbeda. Inti dari API adalah untuk menawarkan kemungkinan programmer aplikasi, tidak semuanya akan digunakan. Anda tidak boleh secara artifisial membatasi apa yang dapat dilakukan oleh pengguna API kecuali jika mempersulit API sehingga pemahaman internet menurun.

Kilian Foth
sumber
7

Setuju dengan jawaban @ KilianFoth bahwa ini baik-baik saja.

Namun, jika Anda mau, Anda bisa membuat metode yang mengambil objek tunggal yang mengimplementasikan kedua antarmuka:

public object CreateDataFile<T_BoardInterface>(
             T_BoardInterface boardInterface
    )
    where T_BoardInterface : IFileAccess, IMeasurer
{
    return CreateDataFile(
                boardInterface
            ,   boardInterface
        );
}

Tidak ada alasan umum bahwa argumen harus objek yang berbeda, dan jika suatu metode memang membutuhkan argumen yang berbeda, itu akan menjadi persyaratan khusus kontraknya harus menjelaskan.

Nat
sumber
4

Saya sampai pada kesimpulan bahwa CreateDataFilemelakukan satu hal dengan melakukan pengukuran cepat dan kemudian menyimpan data, dan melakukan keduanya dengan metode yang sama lebih intuitif untuk orang lain yang menggunakan kode ini kemudian harus melakukan pengukuran dan menulis ke file sebagai pemanggilan metode terpisah.

Saya pikir ini masalah Anda sebenarnya. Metodenya tidak melakukan satu hal. Ini melakukan dua, operasi berbeda yang melibatkan I / O ke perangkat yang berbeda , yang keduanya memuat ke objek lain:

  • Ambil pengukuran
  • Simpan hasil itu ke file di suatu tempat

Ini adalah dua operasi I / O yang berbeda. Khususnya, yang pertama tidak mengubah sistem file dengan cara apa pun.

Bahkan, kita harus perhatikan bahwa ada langkah tengah tersirat:

  • Ambil pengukuran
  • Serialize pengukuran ke dalam format yang dikenal
  • Simpan pengukuran serial ke file

API Anda harus menyediakan masing-masing secara terpisah dalam beberapa bentuk. Bagaimana Anda tahu seorang penelepon tidak ingin melakukan pengukuran tanpa menyimpannya di mana pun? Bagaimana Anda tahu mereka tidak ingin mendapatkan pengukuran dari sumber lain? Bagaimana Anda tahu mereka tidak ingin menyimpannya di tempat lain selain perangkat? Ada alasan bagus untuk memisahkan operasi. Pada telanjang minimum, masing-masing bagian individu harus tersedia untuk pemanggil apapun. Saya seharusnya tidak dipaksa untuk menulis pengukuran ke file jika use case saya tidak meminta untuk itu.

Sebagai contoh, Anda dapat memisahkan operasi seperti ini.

IMeasurer memiliki cara untuk mengambil pengukuran:

public interface IMeasurer
{
    IMeasurement Measure(int someInput);
}

Jenis pengukuran Anda mungkin hanya sesuatu yang sederhana, seperti stringatau decimal. Saya tidak mendesak Anda membutuhkan antarmuka atau kelas untuk itu, tetapi itu membuat contoh di sini lebih umum.

IFileAccess memiliki beberapa metode untuk menyimpan file:

interface IFileAccess
{
    void SaveFile(string fileContents);
}

Maka Anda perlu cara membuat serial pengukuran. Bangun itu ke dalam kelas atau antarmuka yang mewakili pengukuran, atau miliki metode utilitas:

interface IMeasurement
{
    // As part of the type
    string Serialize();
}

// Utility method. Makes more sense if the measurement is not a custom type.
public static string SerializeMeasurement(IMeasurement m)
{
    return ...
}

Tidak jelas apakah Anda memiliki operasi serialisasi ini belum.

Pemisahan semacam ini meningkatkan API Anda. Ini memungkinkan penelepon memutuskan apa yang mereka butuhkan dan kapan, daripada memaksakan ide-ide Anda sebelumnya tentang apa yang saya / O lakukan. Penelepon harus memiliki kontrol untuk melakukan operasi yang valid , baik menurut Anda berguna atau tidak.

Setelah Anda memiliki implementasi yang terpisah untuk setiap operasi, CreateDataFilemetode Anda menjadi singkatan

fileAccess.SaveFile(SerializeMeasurement(measurer.Measure()));

Khususnya, metode Anda menambahkan nilai yang sangat kecil setelah Anda melakukan semua ini. Baris kode di atas tidak sulit bagi penelepon Anda untuk digunakan secara langsung, dan metode Anda murni untuk kenyamanan maksimal. Itu harus dan merupakan sesuatu yang opsional . Dan itu adalah cara yang benar untuk berperilaku API.


Setelah semua bagian yang relevan difaktorkan dan kami telah mengakui bahwa metode ini hanya kenyamanan, kami perlu mengulangi pertanyaan Anda:

Kasus penggunaan apa yang paling umum untuk penelepon Anda?

Jika seluruh poinnya adalah untuk membuat case use khas dari mengukur dan menulis ke papan yang sama sedikit lebih nyaman, maka masuk akal untuk membuatnya tersedia di Boardkelas secara langsung:

public class Board : IMeasurer, IFileAccess
{
    // Interface methods...

    /// <summary>
    /// Convenience method to measure and immediate record measurement in
    /// default location.
    /// </summary>
    public void ReadAndSaveMeasurement()
    {
        this.SaveFile(SerializeMeasurement(this.Measure()));
    }
}

Jika ini tidak meningkatkan kenyamanan, maka saya tidak akan repot dengan metode ini sama sekali.


Ini menjadi metode kenyamanan memunculkan satu pertanyaan lain.

Haruskah IFileAccessantarmuka tahu tentang jenis pengukuran dan bagaimana cara membuat serial? Jika demikian, Anda dapat menambahkan metode ke IFileAccess:

interface IFileAccess
{
    void SaveFile(string fileContents);
    void SaveMeasurement(IMeasurement m);
}

Sekarang penelepon lakukan saja ini:

fileAccess.SaveFile(measurer.Measure());

yang sama pendek dan mungkin lebih jelas daripada metode kenyamanan Anda sebagaimana dipahami dalam pertanyaan.

jpmc26
sumber
2

Klien yang mengkonsumsi tidak harus berurusan dengan sepasang barang saat satu barang mencukupi. Dalam kasus Anda, mereka hampir tidak, sampai doa CreateDataFile.

Solusi potensial yang Anda sarankan adalah membuat antarmuka turunan gabungan. Namun, pendekatan ini membutuhkan objek tunggal yang mengimplementasikan kedua antarmuka, yang agak membatasi, bisa dibilang abstraksi bocor yang pada dasarnya disesuaikan untuk implementasi tertentu. Pertimbangkan betapa rumitnya jika seseorang ingin mengimplementasikan dua antarmuka dalam objek terpisah: mereka harus mem-proxy semua metode di salah satu antarmuka untuk meneruskan ke objek lain. (FWIW, opsi lain adalah hanya menggabungkan antarmuka daripada membutuhkan satu objek harus mengimplementasikan dua antarmuka melalui antarmuka turunan.)

Namun, pendekatan lain yang kurang membatasi / mendikte pelaksanaan IFileAccessadalah dipasangkan dengan IMeasurerkomposisi in, sehingga salah satu dari mereka terikat dan referensi yang lain. (Ini agak meningkatkan abstraksi dari salah satu dari mereka karena sekarang mewakili pasangan juga.) Kemudian CreateDataFilebisa hanya mengambil satu dari referensi, katakanlah IFileAccess, dan masih mendapatkan yang lain sesuai kebutuhan. Implementasi Anda saat ini sebagai objek yang mengimplementasikan kedua antarmuka hanya return this;untuk referensi komposisi, di sini pengambil untuk IMeasurerdi IFileAccess.

Jika pasangan ternyata salah di beberapa titik dalam pengembangan, yang mengatakan kadang-kadang pengukur yang berbeda digunakan dengan akses file yang sama, maka Anda dapat melakukan pemasangan yang sama tetapi pada tingkat yang lebih tinggi, yang berarti antarmuka tambahan yang diperkenalkan akan bukan menjadi antarmuka turunan, melainkan antarmuka yang memiliki dua getter, memasangkan akses file dan pengukur bersama melalui komposisi daripada derivasi. Klien yang mengkonsumsi kemudian memiliki satu hal yang perlu diperhatikan selama pasangan bertahan, dan masing-masing objek untuk ditangani (untuk menyusun pasangan baru) bila perlu.


Pada catatan lain, saya mungkin bertanya siapa yang memiliki CreateDataFile, dan pertanyaannya adalah siapa pihak ketiga ini. Kami sudah memiliki beberapa klien pemakai yang memanggil CreateDataFile, objek / kelas pemilik CreateDataFile, dan IFileAccessdan IMeasurer. Kadang-kadang ketika kita mengambil konteks konteks yang lebih luas, organisasi alternatif, terkadang lebih baik, dapat muncul. Sulit dilakukan di sini karena konteksnya tidak lengkap, jadi hanya makanan untuk dipikirkan.

Erik Eidt
sumber
0

Beberapa telah mengemukakan bahwa CreateDataFilemelakukan terlalu banyak. Saya mungkin menyarankan bahwa alih-alih Boardmelakukan terlalu banyak, karena mengakses file sepertinya menjadi perhatian terpisah dari anggota dewan lainnya.

Namun, jika kami berasumsi bahwa ini bukan kesalahan, masalah yang lebih besar adalah bahwa antarmuka harus ditentukan oleh klien, dalam hal ini CreateDataFile.

The Antarmuka Pemisahan Prinsip menyatakan bahwa klien tidak harus bergantung pada lebih dari sebuah antarmuka dari apa yang dibutuhkan. Meminjam frasa dari jawaban lain ini , ini dapat diparafrasekan sebagai "sebuah antarmuka didefinisikan oleh apa yang dibutuhkan klien."

Sekarang, dimungkinkan untuk membuat antarmuka khusus klien ini menggunakan IFileAccessdan IMeasurersebagai jawaban lain menyarankan, tetapi pada akhirnya, klien ini harus memiliki antarmuka yang dibuat khusus untuk itu.

Xtros
sumber
@Downvoter - Bagaimana dengan ini tidak benar atau dapat ditingkatkan?
Xtros