Haruskah metode yang bukan "fungsi murni" dan yang berinteraksi dengan API atau perangkat keras eksternal bersifat statis?

8

Ketika membaca tentang kapan membuat metode statis atau tidak, saya telah melihat prinsip umum, seperti dirangkum oleh posting ini , bahwa metode seharusnya hanya statis jika tidak mengubah keadaan dan hasilnya hanya bergantung pada parameter yang disediakan untuk Itu. Namun, jawaban yang paling banyak dipilih dalam posting ini menyatakan bahwa metode statis harus digunakan kapan pun memungkinkan. Banyak jawaban dalam posting ini mengatakan bahwa seseorang harus melakukan apa pun yang paling masuk akal.

Dalam kasus saya, saya memiliki ~ 15 metode dalam kelas utilitas umum karena mereka digunakan di banyak tempat dan tidak masuk akal sebagai anggota model atau model tampilan apa pun (menggunakan C # MVVM). Ada metode batal yang berinteraksi dengan berbagai komponen perangkat keras menggunakan paket mereka (misalnya Instrumen Nasional, klien OPC, dll.). Ada juga metode yang mengambil beberapa input, melakukan GET atau PUT ke API kami, dan kemudian mengembalikan respons - metode ini menggunakan HttpClient atau yang serupa. Metode ini bukan operator sederhana pada input, suka Math.Sqrt(), dan tidak mengubah negara.

Jadi, haruskah metode seperti itu (dan, dalam hal ini, seluruh kelas) menjadi statis? Ada keuntungan nyata memiliki kelas statis dengan metode statis: aman dan cepat karena Anda tidak harus membuat objek. Selain itu, masing-masing metode statis ini menggunakan lebih banyak metode dan kelas statis untuk interaksi API dan perangkat keras. Satu-satunya downside yang saya lihat adalah bahwa saya harus menulis tes unit dengan kerangka kerja berbayar, seperti TypeMock Isolator. Biaya tidak menjadi masalah jika jawabannya adalah mengejek mereka menggunakan sesuatu seperti TypeMock Isolator atau layanan berbayar serupa, jadi jika itu adalah konsensus, itu tidak masalah. Saya hanya ingin membuat keputusan yang akan skala baik dan dan meninggalkan sedikit hutang teknis saat kami membawa pengembang baru dan sebagai proyek kami tumbuh.

Beri tahu saya jika saya perlu memberikan lebih banyak informasi untuk membuatnya lebih jelas!

adjordan
sumber
3
"berinteraksi dengan berbagai komponen perangkat keras", "Ada juga metode yang mengambil beberapa input, melakukan GET atau PUT ke API kami, dan kemudian mengembalikan respons" -> ini adalah contoh anak poster berinteraksi dengan negara
Caleth
2
Untuk memperluas komentar @ Caleth: Ingat bahwa "negara" tidak hanya berlaku untuk yang dan nol dalam memori. Jika Anda memanggil fungsi yang menggerakkan lengan robot, Anda telah mengubah status --- di dunia nyata --- bersama dengan semua konsekuensi dari melakukannya.
Greg Burghardt
1
"Aman dan cepat karena Anda tidak harus membuat objek". Dalam kasus API jaringan, Anda tentu membuat banyak objek di dalam panggilan . Dan, bagaimanapun juga, waktu untuk membuat objek Anda kecil dibandingkan dengan latensi jaringan.
user949300
Apa yang akan Anda lakukan ketika Anda memiliki lebih dari satu komponen perangkat keras yang sama? Salin / tempel semua metode?
user253751

Jawaban:

15

Jadi, haruskah metode seperti itu (dan, dalam hal ini, seluruh kelas) menjadi statis?

Atau setidaknya, Anda tidak boleh menggunakannya secara langsung sebagai statika.

Jika Anda bekerja dengan berbagai komponen perangkat keras, Anda sangat sangat cenderung ingin mengejek mereka, menggunakan yang baru, dan memilih mana yang Anda gunakan. Itu semua berteriak karena memiliki antarmuka (atau abstraksi lainnya) sehingga sisa kode Anda dapat segera mengabaikan bahwa ada beberapa komponen perangkat keras sama sekali. Dan statika di sebagian besar bahasa bermain sangat buruk dengan antarmuka (dan mekanisme abstraksi lainnya).

Telastyn
sumber
1
Atau setidaknya, adalah umum untuk memiliki koleksi metode statis yang membuat panggilan aktual yang berbicara dengan hal-hal eksternal, tetapi kemudian membangun kelas API stateful di atasnya, dan mengekspos kelas itu sebagai cara untuk menyelesaikan sesuatu, dan itu kelas yang diejek daripada antarmuka yang mendasarinya, yaitu pola fasad.
whatsisname
Terima kasih balasannya! Ini tampaknya menjadi konsensus, jadi saya akan mematuhi prinsip ini ke depan.
adjordan
4

Saran-saran yang saling bertentangan yang Anda baca semuanya masuk akal dengan cara tertentu, tetapi mereka semua membuat asumsi (berbeda) atau kalimat ambigu. Ini adalah salah satu dari jenis "mengatakan hal yang sama dengan kata-kata yang berbeda".

suatu metode hanya boleh statis jika tidak mengubah keadaan

Perhatikan ambiguitas "modifikasi suatu negara". Contoh berikut melanggar aturan ini (secara harfiah), tetapi ia mempertahankan semangat (figuratif) dari aturan tersebut:

public static void SetUserNameToBob(User u)
{
    u.Name = "Bob";
}

Ini mengubah keadaan Userobjek, sehingga sebenarnya "memodifikasi keadaan".

Namun, metode ini tidak bergantung pada keadaan internal tertentu untuk memutuskan aliran logisnya sendiri (misalnya u.Name = currentlySelectedDefaultName()akan menjadi pelanggaran karena nama yang dipilih adalah keadaan yang dipilih). Dan saya pikir itulah yang dimaksud: tidak ada keadaan internal yang dimodifikasi.

[suatu metode hanya boleh statis jika] hasilnya hanya bergantung pada parameter yang disediakan untuk itu

Lihatlah item sebelumnya, yang ini cukup banyak mengatakan hal yang sama. Artinya adalah ini:

public static string currentlySelectedDefaultName; 
public static void SetUserNameToBob(User u)
{
    u.Name = currentlySelectedDefaultName;
}

tidak boleh statis, karena nama "default saat ini" adalah keadaan, dan oleh karena itu seharusnya tidak menjadi variabel global / metode.

Pikirkan apa yang terjadi jika dua utas terpisah berjalan: salah satunya ingin default ke "Bob", yang lain ingin default ke "Jim". Mereka akan berakhir memperebutkan nilai, yang bertanggung jawab untuk menciptakan masalah debugging yang besar dan perilaku yang tidak terduga.
Namun, jika setiap utas memiliki DefaultNameSetterobjeknya sendiri , maka mereka tidak berebut sumber daya yang sama.

Namun, jawaban yang paling banyak dipilih dalam posting ini menyatakan bahwa metode statis harus digunakan kapan pun memungkinkan.

Ini semacam menegakkan aturan dengan mencoba / gagal. Bisakah kita atur metode ini menjadi statis?

  • Ya =>Bagus! Biarkan seperti itu!
  • Tidak =>Ini membuktikan bahwa kode mengandalkan nilai non-statis di suatu tempat dan karenanya tidak boleh dibuat statis.

Mengutip Jeff Goldblum di Jurassic Park secara tidak langsung , Anda tidak boleh berdebat perlunya melakukan sesuatu hanya dengan membuktikan bahwa itu bisa dilakukan.

Sekali lagi, pendekatan itu tidak selalu (atau selalu) salah, tetapi secara membabi buta mengasumsikan bahwa metode-metode yang telah ditulis sebagai negara-agnostik mungkin secara logis, yang memang tidak demikian.

Bahkan jika Anda berlangganan pendekatan metodologis ini, Anda masih bisa menerapkannya ketika sebuah proyek tidak lagi dalam pengembangan. Jika proyek masih dalam pengembangan, metode saat ini dapat menjadi penampung untuk implementasi di masa depan. Mungkin dimungkinkan untuk membuat Foo()statis hari ini, tetapi tidak besok, jika logika yang bergantung pada negara belum diimplementasikan.

Banyak jawaban dalam posting ini mengatakan bahwa seseorang harus melakukan apa pun yang paling masuk akal.

Yah, mereka tidak salah; tetapi bukankah ini hanya ungkapan ulang kecil dari mengatakan "lakukan hal yang benar"? Itu bukan saran yang sangat membantu, kecuali Anda sudah tahu kapan harus menggunakan statika dan kapan harus menghindarinya. Ini tangkapan 22.


Jadi kapan sebaiknya Anda menggunakan statika?

Seperti yang telah Anda perhatikan, tidak semua orang setuju dengan aturan tersebut, atau setidaknya tentang bagaimana cara mengucapkan aturan tersebut. Saya akan menambahkan upaya lain di sini, tetapi perlu diketahui bahwa ini secara efektif membuat standar lain :

masukkan deskripsi gambar di sini

Ingatlah itu.

Statika adalah kebenaran universal.

Itulah tujuan dari namespace global: hal-hal yang benar di seluruh lapisan aplikasi.

Ada argumen lereng yang licin di sini. Beberapa contoh:

var myConfigKey = ConfigurationManager.AppSettings["myConfigKey"];

Ini adalah contoh yang sangat jelas. Konfigurasi aplikasi secara inheren menyiratkan bahwa konfigurasi bersifat global untuk aplikasi, dan oleh karena itu diperlukan metode statistik.

bool datesOverlap = DateHelper.HasOverlap(myDateA_Start, myDateA_End, myDateB_Start, myDateB_End);

Metode ini secara universal benar. Tidak peduli tanggal mana yang Anda bandingkan. Metode ini tidak peduli tentang arti tanggal. Apakah itu tanggal kerja, tanggal kontrak, ... tidak masalah dengan algoritma metode.

Perhatikan kesamaan semantik antara kontekstual dan didorong oleh negara . Kedua jenis mengacu pada keadaan "tidak universal". Ini membuktikan bahwa perbedaan kontekstual karena itu tergantung pada negara dan tidak cocok untuk dibuat statis.

var newPersonObject = Person.Create();

Ini adalah kebenaran universal. Proses pembuatan yang sama digunakan di seluruh aplikasi.

Namun, garis ini bisa kabur:

var newManager = Person.CreateManager();
var newJanitor = Person.CreateJanitor();

Dari perspektif teknis, tidak ada yang berubah. Manajer (dan petugas kebersihan) dibuat dengan cara yang sama di seluruh aplikasi. Namun, ini secara halus telah menciptakan negara (manajer / petugas kebersihan), yang perlahan tapi pasti mengurangi betapa universal kebenaran sebenarnya.

Bisakah itu dilakukan? Dari sudut pandang teknis; ya .
Haruskah itu dilakukan? Itu masalah apakah Anda memperdebatkan prinsip murni, atau mempertimbangkan bahwa kompromi perlu dilakukan agar tidak sia-sia berjuang untuk kesempurnaan logis. Jadi saya akan mengatakan jika itu tidak menciptakan masalah yang lebih besar daripada masalah yang ingin dipecahkan .

Ketika opsi berkembang (manajer, petugas kebersihan, akuntan, tenaga penjualan, ...), masalah bertambah. Untuk masalah yang cukup besar, pola pabrik diinginkan.

Jika Anda hanya memiliki dua opsi dan Anda tidak memiliki alasan untuk mencurigai bahwa daftar opsi akan bertambah, Anda dapat berdebat bahwa metode pembuatan statis sudah mencukupi. Beberapa mungkin tidak setuju, dan saya juga mengerti maksud mereka. Tetapi saya cenderung praktis dan tidak terlalu perfeksionis dalam pendekatan saya.

Flater
sumber
3

Namun, jawaban yang paling banyak dipilih dalam posting ini menyatakan bahwa metode statis harus digunakan kapan pun memungkinkan ...

Metode statis yang mempengaruhi keadaan dimana mereka digabungkan secara langsung adalah ide yang sangat buruk. Mereka mengarah ke negara global - "aksi seram di kejauhan" - masalah, kode spageti dan sejenisnya. Mereka sulit untuk menguji, men-debug dan memelihara. Bacaan saya tentang jawaban yang paling banyak dipilih tidak mendorong semua ini, jadi semuanya baik-baik saja.

Jadi, haruskah metode seperti itu (dan, dalam hal ini, seluruh kelas) menjadi statis?

Itu tergantung. Mari kita ambil contoh " metode kosong yang berinteraksi dengan berbagai komponen perangkat keras menggunakan paket mereka ". Metode-metode itu bisa statis, tetapi hanya jika mereka dipisahkan dari paket-paket itu. Salah satu cara untuk mencapainya adalah dengan mengirimkan paket-paket itu sebagai parameter. Ingin menguji metode ini tanpa perangkat keras? Mudah, mengirimkan paket tiruan, diejek, yang merekam tindakan, daripada mengakses perangkat keras melalui parameter. Hal yang sama berlaku untuk metode HTTP; selama cara aktual mengakses API dilewatkan sebagai parameter, juga.

Tetapi, apakah Anda benar-benar mencapai sesuatu dengan cara ini melalui pembuatan instance dan menyuntikkan paket-paket itu, kelas HTTP dll pada saat konstruksi? Mungkin tidak. Tentu, itu adalah metode statis, jadi Anda tidak perlu contoh. Tetapi Anda memiliki kerumitan tambahan yang diperlukan untuk mengekspos semua paket itu, dll, sepanjang kode untuk meneruskannya sebagai parameter. Seringkali jauh lebih mudah untuk membuat satu instance dan menyebarkannya.

David Arno
sumber
Anda mengatakan untuk membagikan satu instance dari kelas "UtilityMethods.cs" apa pun yang saya buat? Saat ini saya menggunakan injeksi ketergantungan dengan SimpleIoC, yang menurut saya adalah ide yang sama.
adjordan
2
@adjordan, memang. Apakah Anda menggunakan DI murni (orang miskin) atau kerangka kerja IoC untuk membantu dengan injeksi terserah Anda. Prinsipnya tetap sama: lebih mudah untuk hanya mengoper turunan dari IUtilityMethodsdaripada melewati beberapa instance yang perlu dilewatkan ke fungsi statis.
David Arno
1

Apa yang saya rekomendasikan dan apa yang saya gunakan dalam tim saya adalah dengan menggunakan metode / kelas statis jika ada kebutuhan nyata untuk itu. Seharusnya bukan opsi pertama untuk menggunakan metode / kelas statis. Itu harus menjadi alasan yang sangat bagus untuk itu. Banyak pengembang melihat opsi statis lebih cepat dan karenanya lebih murah. Tapi itu mungkin awalnya. Biaya untuk membangun Unit Test lebih rumit dan kurang memiliki kemampuan untuk menggunakan kontrak (antarmuka) dengan beberapa implementasi. Untuk metode / kelas yang berinteraksi dengan perangkat keras. Ini seharusnya tidak statis. Pertama dan untuk sebagian besar, tidak ada alasan. Kedua, diperlukan untuk mengejek kelas-kelas ini dalam unit test untuk kode pengujian yang lebih sederhana. Ketiga, adalah mungkin untuk mengganti implementasi di masa depan.

Arafat Hegazy
sumber
0

Fungsi statis lebih menantang ketika Anda ingin memungkinkan beberapa akses secara paralel.

Misalnya, sangat masuk akal, dan kemungkinan ide yang bagus, untuk memungkinkan beberapa panggilan HTTP untuk dieksekusi pada saat yang sama. Dengan objek instan, Anda dapat dengan mudah mempertahankan status berbeda untuk panggilan tersebut - header, kode hasil, buffer, dll ...

Dengan metode statis tunggal, Anda dapat membuat instance internal untuk mempertahankan status itu. Atau Anda menyinkronkan semuanya (Saya menggunakan terminologi Java, YMMV), yang rawan kesalahan. Atau gunakan antrian. Bagaimanapun juga, tanpa gerak kaki yang mewah, Anda kehilangan kemampuan untuk berjalan secara paralel. Jika satu panggilan HTTP macet karena kesalahan jaringan, yang lain harus menunggu.

Sama untuk perangkat keras Anda - dalam beberapa kasus mungkin masuk akal untuk mengizinkan akses secara paralel.

Untuk alasan ini, saya tidak setuju dengan jawaban berusia 8 tahun yang Anda kutip.

pengguna949300
sumber
4
Fungsi statis seharusnya tidak mengandung keadaan sama sekali.
Pieter B
1
@Pieter B Tentu saja (selain mungkin counter). Dalam contoh HTTP, itu harus membuat objek MyHTTPHelper per panggilan untuk mempertahankan status. Pada titik itu mungkin lebih mudah untuk membuat penelepon asli membuat objek, tidak menggunakan metode statis sama sekali.
user949300