Pola C # untuk menangani “fungsi bebas” dengan bersih, menghindari kelas statis “utilitas tas” ala Helper


9

Saya baru-baru ini meninjau beberapa kelas statis "utilitas tas" Helper mengambang di sekitar beberapa kode C # besar yang saya kerjakan, hal-hal yang pada dasarnya seperti cuplikan yang sangat ringkas:

// Helpers.cs

public static class Helpers
{
    public static void DoSomething() {}
    public static void DoSomethingElse() {}
}

Metode spesifik yang saya ulas adalah

  • sebagian besar tidak terkait satu sama lain,
  • tanpa negara eksplisit bertahan di doa,
  • kecil, dan
  • masing-masing dikonsumsi oleh berbagai jenis yang tidak terkait.

Sunting: Di atas tidak dimaksudkan untuk menjadi daftar masalah yang diduga. Ini adalah daftar karakteristik umum dari metode spesifik yang saya ulas. Konteksnya untuk membantu jawaban memberikan solusi yang lebih relevan.

Hanya untuk pertanyaan ini, saya akan merujuk pada metode semacam ini sebagai GLUM (metode utilitas umum ringan). Konotasi negatif "murung" sebagian dimaksudkan. Saya minta maaf jika ini dianggap sebagai permainan kata bodoh.

Bahkan mengesampingkan skeptisisme default saya sendiri tentang GLUM, saya tidak suka hal-hal berikut tentang ini:

  • Kelas statis digunakan hanya sebagai namespace.
  • Identifier kelas statis pada dasarnya tidak ada artinya.
  • Ketika GLUM baru ditambahkan, baik (a) kelas "tas" ini disentuh tanpa alasan yang baik atau (b) kelas "tas" baru dibuat (yang dengan sendirinya biasanya tidak menjadi masalah; yang buruk adalah bahwa baru kelas statis sering hanya mengulangi masalah yang tidak terkait, tetapi dengan metode yang lebih sedikit).
  • Meta-penamaan adalah tak terelakkan mengerikan, non-standar, dan biasanya secara internal tidak konsisten, apakah itu Helpers, Utilities, atau apa pun.

Apa pola yang cukup bagus & sederhana untuk refactoring ini, sebaiknya mengatasi masalah di atas, dan lebih disukai dengan sentuhan seringan mungkin?

Saya mungkin harus menekankan: Semua metode yang saya hadapi tidak berpasangan satu sama lain. Tampaknya tidak ada cara yang masuk akal untuk memecahnya menjadi tas metode kelas statis yang lebih halus namun tetap multi-anggota.


1
Tentang GLUMs ™: Saya pikir singkatan untuk "ringan" dapat dihapus, karena metode harus ringan dengan mengikuti praktik terbaik;)
Fabio

@Fabio saya setuju. Saya memasukkan istilah sebagai lelucon (mungkin tidak terlalu lucu dan mungkin membingungkan) yang berfungsi sebagai singkatan untuk penulisan pertanyaan ini. Saya pikir "ringan" tampaknya tepat di sini karena basis kode yang dimaksud berumur panjang, warisan, dan sedikit berantakan, yaitu ada banyak metode lain (biasanya GUM ini ;-) yang merupakan "kelas berat". Jika saya memiliki lebih banyak (ehem, ada) anggaran saat ini, saya pasti akan mengambil pendekatan yang lebih agresif dan mengatasi yang terakhir.
William

Jawaban:


17

Saya pikir Anda memiliki dua masalah yang berkaitan erat satu sama lain.

  • Kelas statis berisi fungsi yang tidak berhubungan secara logis
  • Nama-nama kelas statis tidak memberikan informasi bermanfaat tentang makna / konteks fungsi yang terkandung.

Masalah-masalah ini dapat diperbaiki dengan menggabungkan hanya metode yang berhubungan secara logis ke dalam satu kelas statis dan memindahkan yang lain ke dalam kelas statis mereka sendiri. Berdasarkan domain masalah Anda, Anda dan tim Anda harus memutuskan kriteria apa yang akan Anda gunakan untuk memisahkan metode ke dalam kelas statis terkait.

Kekhawatiran dan kemungkinan solusi

Metode sebagian besar tidak terkait satu sama lain - masalah. Gabungkan metode terkait di bawah satu kelas statis dan pindahkan metode yang tidak terkait ke kelas statis mereka sendiri.

Metode tanpa keadaan eksplisit bertahan di doa - bukan masalah. Metode stateless adalah fitur, bukan bug. Keadaan statis sulit untuk diuji, dan seharusnya hanya digunakan jika Anda perlu mempertahankan keadaan di seluruh metode doa, tetapi ada cara yang lebih baik untuk melakukan itu seperti mesin negara dan yieldkata kunci.

Metode kecil - bukan masalah. - Metode harus kecil.

Metode masing-masing dikonsumsi oleh berbagai jenis yang tidak terkait - tidak masalah. Anda telah membuat metode umum yang dapat digunakan kembali di banyak tempat yang tidak terkait.

Kelas statis digunakan hanya sebagai namespace - bukan masalah. Ini adalah bagaimana metode statis digunakan. Jika gaya metode panggilan ini mengganggu Anda, coba gunakan metode ekstensi atau using staticdeklarasi.

Pengidentifikasi kelas statis pada dasarnya tidak ada artinya - masalah . Ini tidak ada artinya karena pengembang memasukkan metode yang tidak terkait di dalamnya. Masukkan metode yang tidak terkait ke dalam kelas statis mereka sendiri dan beri mereka nama yang spesifik dan dapat dimengerti.

Ketika GLUM baru ditambahkan, baik (a) kelas "tas" ini disentuh (kesedihan SRP) - bukan masalah jika Anda mengikuti gagasan bahwa hanya metode yang berhubungan secara logis harus dalam satu kelas statis. SRPtidak dilanggar di sini, karena prinsip harus diterapkan untuk kelas atau metode. Tanggung jawab tunggal dari kelas statis berarti mengandung metode yang berbeda untuk satu "ide" umum. Gagasan itu dapat berupa fitur atau konversi, atau iterasi (LINQ), atau normalisasi data, atau validasi ...

atau lebih jarang (b) kelas "tas" baru dibuat (lebih banyak tas) - tidak masalah. Kelas / kelas statis / fungsi - adalah alat (pengembang) kami, yang kami gunakan untuk merancang perangkat lunak. Apakah tim Anda memiliki batasan untuk menggunakan kelas? Berapa batas maksimum untuk "lebih banyak tas"? Pemrograman adalah semua tentang konteks, jika untuk solusi dalam konteks khusus Anda, Anda berakhir dengan 200 kelas statis yang dimengerti, secara logis ditempatkan dalam ruang nama / folder dengan hierarki logis - itu bukan masalah.

Penamaan meta tidak terhindarkan mengerikan, tidak standar, dan biasanya tidak konsisten secara internal, apakah itu Pembantu, Utilitas, atau apa pun - masalah. Berikan nama yang lebih baik yang menggambarkan perilaku yang diharapkan. Simpan hanya metode terkait dalam satu kelas, yang memberikan bantuan dalam penamaan yang lebih baik


Ini bukan pelanggaran SRP, tapi cukup jelas merupakan pelanggaran Prinsip Terbuka / Tertutup. Yang mengatakan, saya secara umum menemukan Ocp lebih banyak masalah daripada nilainya
Paul

2
@ Paul, prinsip Buka / Tutup tidak dapat diterapkan untuk kelas statis. Itulah poin saya, bahwa prinsip SRP atau OCP tidak dapat diterapkan untuk kelas statis. Anda dapat menerapkannya untuk metode statis.
Fabio

Oke, ada beberapa kebingungan di sini. Daftar item pertama pertanyaan bukan daftar masalah yang dituduhkan. Ini adalah daftar karakteristik umum dari metode spesifik yang saya ulas. Konteksnya untuk membantu jawaban memberikan solusi yang lebih berlaku. Saya akan mengedit untuk menjelaskan.
William

1
Kembali "kelas statis sebagai namespace", fakta itu adalah pola umum tidak berarti itu bukan anti-pola. Metode (yang pada dasarnya adalah "fungsi bebas" untuk meminjam istilah dari C / C ++) dalam kelas statis kohesi rendah ini adalah entitas yang bermakna dalam kasus ini. Dalam konteks khusus ini, secara struktural tampaknya lebih baik untuk memiliki sub-namespace Adengan 100 kelas metode tunggal statis (dalam 100 file) daripada kelas statis Adengan 100 metode dalam satu file.
William

1
Saya melakukan beberapa pembersihan besar pada jawaban Anda, sebagian besar kosmetik. Saya tidak yakin tentang paragraf terakhir Anda; tidak ada yang khawatir tentang bagaimana mereka menguji kode yang memanggil Mathkelas statis.
Robert Harvey

5

Hanya merangkul konvensi bahwa kelas statis digunakan seperti ruang nama dalam situasi seperti ini di C #. Namespaces mendapatkan nama yang bagus, begitu juga kelas-kelas ini. The using staticfitur C # 6 membuat hidup sedikit lebih mudah, juga.

Nama-nama kelas yang berbau seperti Helperssinyal bahwa metode harus dipecah menjadi kelas statis yang lebih baik, jika memungkinkan, tetapi salah satu asumsi Anda yang ditekankan adalah bahwa metode yang Anda hadapi adalah "pairwise unrelated", yang menyiratkan pemisahan menjadi satu kelas statis baru per metode yang ada. Itu mungkin baik, jika

  • ada nama bagus untuk kelas statis baru dan
  • Anda menilai itu benar-benar bermanfaat bagi pemeliharaan proyek Anda.

Sebagai contoh buat-buat, Helpers.LoadImagemungkin menjadi sesuatu seperti FileSystemInteractions.LoadImage.

Namun, Anda bisa berakhir dengan metode-tas kelas statis. Beberapa cara ini bisa terjadi:

  • Mungkin hanya beberapa (atau tidak ada) metode asli yang memiliki nama baik untuk kelas baru mereka.
  • Mungkin kelas baru kemudian berkembang untuk memasukkan metode lain yang relevan.
  • Mungkin nama kelas aslinya tidak bau dan sebenarnya baik-baik saja.

Penting untuk diingat bahwa tas-metode metode statis ini tidak terlalu jarang dalam basis kode C # nyata. Mungkin tidak patologis memiliki beberapa yang kecil .


Jika Anda benar-benar melihat keuntungan memiliki masing-masing metode seperti "fungsi bebas" dalam file sendiri (yang baik dan berharga jika Anda dengan jujur ​​menilai bahwa itu sebenarnya menguntungkan pemeliharaan proyek Anda), Anda dapat mempertimbangkan membuat kelas statis seperti itu daripada statis. kelas parsial, mempekerjakan statis nama kelas mirip dengan bagaimana Anda akan sebuah namespace dan kemudian memakan melalui using static. Sebagai contoh:

struktur proyek dummy menggunakan pola ini

Program.cs

namespace ConsoleApp1
{
    using static Foo;
    using static Flob;

    class Program
    {
        static void Main(string[] args)
        {
            Bar();
            Baz();
            Wibble();
            Wobble();
        }
    }
}

Foo / Bar.cs

namespace ConsoleApp1
{
    static partial class Foo
    {
        public static void Bar() { }
    }
}

Foo / Baz.cs

namespace ConsoleApp1
{
    static partial class Foo
    {
        public static void Baz() { }
    }
}

Flob / Wibble.cs

namespace ConsoleApp1
{
    static partial class Flob
    {
        public static void Wibble() { }
    }
}

Flob / Goyangan.cs

namespace ConsoleApp1
{
    static partial class Flob
    {
        public static void Wobble() { }
    }
}

-6

Secara umum Anda tidak harus memiliki atau memerlukan "metode utilitas umum". Dalam logika bisnis Anda. Perhatikan baik-baik lagi dan letakkan di tempat yang cocok.

Jika Anda menulis perpustakaan matematika atau sesuatu, maka saya akan menyarankan, meskipun saya biasanya membenci mereka, Metode Ekstensi.

Anda dapat menggunakan Metode Ekstensi untuk menambahkan fungsi ke tipe data standar.

Misalnya Katakanlah saya memiliki fungsi umum MySort () bukan Helpers.MySort atau menambahkan ListOfMyThings.MySort baru saya dapat menambahkannya ke IEnumerable


Intinya adalah untuk tidak mengambil "tampilan keras" lagi. Intinya adalah memiliki pola yang dapat digunakan kembali yang dapat dengan mudah membersihkan "tas" utilitas. Saya tahu utilitas adalah bau. Pertanyaannya menyatakan ini. Tujuannya adalah untuk mengisolasikan entitas domain yang independen (tapi terlalu dekat) untuk saat ini dan semudah mungkin dalam anggaran proyek.
William

1
Jika Anda tidak memiliki "metode utilitas umum," Anda mungkin tidak cukup mengisolasi potongan-potongan logika dari sisa logika Anda. Misalnya, sejauh yang saya tahu, tidak ada tujuan umum URL path bergabung dengan fungsi .NET, jadi saya sering berakhir dengan menulis satu. Semacam itu akan lebih baik sebagai bagian dari perpustakaan standar, tetapi setiap lib standar kehilangan sesuatu . Alternatifnya, membiarkan logika diulang langsung di dalam kode Anda yang lain, membuatnya jauh lebih mudah dibaca.
jpmc26


1
@ Ewan yang bukan suatu fungsi, itu adalah tanda tangan delegasi tujuan umum ...
TheCatWhisperer

1
@ Ewan Itu adalah inti TheCatWhisperer: kelas utilitas adalah kludge karena harus memasukkan metode ke dalam kelas. Senang kamu setuju.
jpmc26
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.