Java - Apakah ide buruk untuk memiliki kelas yang sepenuhnya statis?

16

Saya sedang mengerjakan proyek solo yang lebih besar dan sekarang, dan saya memiliki beberapa kelas di mana saya tidak melihat alasan untuk membuat instance.

Kelas dadu saya sekarang, misalnya, menyimpan semua datanya secara statis dan semua metodenya juga statis. Saya tidak perlu menginisialisasi karena ketika saya ingin melempar dadu dan mendapatkan nilai baru, saya hanya menggunakan Dice.roll().

Saya memiliki beberapa kelas serupa yang hanya memiliki satu fungsi utama seperti ini dan saya akan mulai bekerja pada semacam kelas "pengontrol" yang akan bertanggung jawab atas semua acara (seperti ketika seorang pemain bergerak, dan giliran apa saat ini) Yaitu) dan saya telah menemukan bahwa saya dapat mengikuti ide yang sama untuk kelas ini. Saya tidak pernah berencana membuat beberapa objek untuk kelas-kelas khusus ini, jadi apakah itu ide yang buruk untuk membuat mereka sepenuhnya statis?

Saya bertanya-tanya apakah ini dianggap "praktik buruk" ketika datang ke Jawa. Dari apa yang saya lihat, komunitas sepertinya agak terpecah pada topik ini? Bagaimanapun, saya akan senang berdiskusi tentang hal ini dan tautan ke sumber daya juga akan bagus!

HexTeke
sumber
1
Jika program Anda sepenuhnya prosedural. Mengapa Anda memilih Java?
Laiv
12
Coba tulis unit test untuk kelas-kelas ini, dan Anda akan mencari tahu mengapa orang tidak menyukai metode statis yang mengakses keadaan statis.
Joeri Sebrechts
@Laiv Saya masih agak baru dalam pemrograman, sekitar satu tahun C ++, saya mengambil kelas Java semester ini dan saya mulai lebih menyukai Java, terutama perpustakaan grafis.
HexTeke
5
Mengenai kelas statis. Jika metode statis murni (tahan tanpa status, minta argumen input dan tipe pengembalian). Tidak ada yang perlu dikhawatirkan
Laiv

Jawaban:

20

Tidak ada yang salah dengan kelas statis yang benar-benar statis . Artinya, tidak ada keadaan internal untuk dibicarakan yang akan menyebabkan output dari metode berubah.

Jika Dice.roll()hanya mengembalikan nomor acak baru dari 1 ke 6, itu tidak mengubah keadaan. Memang, Anda mungkin berbagi Randomcontoh, tapi saya tidak akan menganggap bahwa perubahan status secara definisi, output akan selalu baik, acak. Ini juga thread-safe sehingga tidak ada masalah di sini.

Anda akan sering melihat "Pembantu" akhir atau kelas utilitas lain yang memiliki konstruktor pribadi dan anggota statis. Konstruktor pribadi tidak mengandung logika dan berfungsi hanya untuk mencegah seseorang membuat instance kelas. Pengubah akhir hanya membawa pulang ide ini bahwa ini bukan kelas yang ingin Anda dapatkan. Ini hanyalah kelas utilitas. Jika dilakukan dengan benar, seharusnya tidak ada anggota tunggal atau anggota kelas lain yang tidak statis dan final.

Selama Anda mengikuti pedoman ini dan Anda tidak membuat lajang, sama sekali tidak ada yang salah dengan ini. Anda menyebutkan kelas controller, dan ini hampir pasti akan memerlukan perubahan keadaan, jadi saya akan menyarankan agar tidak menggunakan metode statis saja. Anda dapat sangat bergantung pada kelas utilitas statis, tetapi Anda tidak dapat membuatnya menjadi kelas utilitas statis.


Apa yang dianggap sebagai perubahan status untuk suatu kelas? Baiklah, mari kita singkirkan angka acak selama sedetik, karena mereka tidak ditentukan oleh definisi dan karenanya nilai pengembalian sering berubah.

Fungsi murni adalah fungsi yang deterministik, artinya, untuk input yang diberikan, Anda akan mendapatkan satu dan tepat satu output. Anda ingin metode statis menjadi fungsi murni. Di Jawa ada cara untuk mengubah perilaku metode statis untuk mempertahankan keadaan, tetapi mereka hampir tidak pernah merupakan ide yang baik. Ketika Anda mendeklarasikan suatu metode sebagai statis , programmer umumnya akan langsung menganggap bahwa itu adalah fungsi murni. Menyimpang dari perilaku yang diharapkan adalah bagaimana Anda cenderung membuat bug di program Anda, secara umum dan harus dihindari.

Singleton adalah kelas yang berisi metode statis tentang kebalikan dari "fungsi murni" yang Anda bisa. Seorang anggota pribadi statis tunggal disimpan secara internal ke kelas yang digunakan untuk memastikan ada satu contoh. Ini bukan praktik terbaik dan dapat membuat Anda kesulitan nanti karena sejumlah alasan. Untuk mengetahui apa yang sedang kita bicarakan, berikut adalah contoh sederhana dari singleton:

// DON'T DO THIS!
class Singleton {
  private String name; 
  private static Singleton instance = null;

  private Singleton(String name) {
    this.name = name;
  }

  public static Singleton getInstance() {
    if(instance == null) {
      instance = new Singleton("George");
    }
    return instance;
  }

  public getName() {
    return name;
  }
}

assert Singleton.getInstance().getName() == "George"
Neil
sumber
7
Jika saya ingin menguji apa yang terjadi ketika enam ganda digulung, saya terjebak di sini karena Anda memiliki kelas statis dengan generator nomor acak statis. Jadi tidak, Dice.roll()bukan pengecualian yang valid untuk aturan "no global state".
David Arno
1
@HexTeke Jawaban yang diperbarui.
Neil
1
@ Davidvid Benar, tapi saya kira pada saat itu kami benar-benar dalam masalah jika kami menguji satu panggilan random.nextInt(6) + 1. ;)
Neil
3
@ Neil, permintaan maaf, saya tidak menjelaskan diri saya dengan baik. Kami tidak menguji urutan nomor acak, kami memengaruhi urutan itu untuk membantu pengujian lain. Jika kita menguji bahwa misalnya RollAndMove()menggulung lagi ketika kita memasok enam ganda, maka cara termudah, paling kuat untuk melakukannya adalah dengan mengejek salah satu Diceatau nomor acak generator. Ergo, Dicetidak ingin menjadi kelas statis menggunakan generator acak statis.
David Arno
12
Namun pembaruan Anda mengenai masalah ini: metode statis harus bersifat deterministik; mereka seharusnya tidak memiliki efek samping.
David Arno
9

Untuk memberikan contoh keterbatasan statickelas, bagaimana jika beberapa gamer Anda ingin mendapatkan sedikit bonus pada gulungan mereka? Dan mereka bersedia membayar mahal! :-)

Ya, Anda bisa menambahkan parameter lain, jadi Dice.roll(bonus),

Kemudian Anda membutuhkan D20s.

Dice.roll(bonus, sides)

Ya, tetapi beberapa pemain memiliki prestasi "sangat mampu" sehingga mereka tidak akan pernah bisa "gagal" (roll 1).

Dice.roll(bonus, sides, isFumbleAllowed).

Ini semakin berantakan, bukan?

pengguna949300
sumber
ini tampaknya ortogonal untuk pertanyaan, semakin berantakan apakah ini metode statis atau metode normal
jk.
4
@ Jk, saya pikir saya mengerti maksudnya. Tidak masuk akal untuk memiliki kelas statis Dice ketika Anda berpikir dalam lebih banyak jenis dadu. Dalam hal ini, kita dapat memiliki objek dadu yang berbeda, memodelkannya dengan praktik OOP yang baik.
Dherik
1
@TimothyTruckle Saya tidak membantah itu, semua yang saya katakan adalah bahwa jawaban ini tidak benar-benar menjawab pertanyaan, karena creep lingkup seperti ini tidak ada hubungannya dengan metode yang statis atau tidak.
nvoigt
2
@ nvoigt "Saya tidak membantah itu" - Yah, saya lakukan. Fitur yang paling kuat dari bahasa OO adalah polimorfisme . Dan akses statis secara efektif mencegah kita menggunakannya sama sekali. Dan Anda benar: new Dice().roll(...)harus dianggap sebagai akses statis juga Dice.roll(...). Kami hanya mendapat manfaat jika kami menyuntikkan ketergantungan itu.
Timothy Truckle
2
@Jk perbedaannya adalah bahwa statickekacauan terjadi dalam setiap panggilan, dan pengetahuan yang diperlukan diperlukan dalam setiap panggilan, sehingga akan tersebar secara global ke seluruh aplikasi Anda. Dalam struktur OOP, hanya konstruktor / pabrik yang perlu berantakan, dan detail yang mati dirangkum. Setelah itu, gunakan polimorfisme dan panggil saja roll(). Saya mungkin mengedit jawaban ini untuk menjelaskan.
user949300
3

Dalam kasus tertentu dari kelas Dice saya pikir menggunakan metode instance daripada statika akan membuat pengujian secara signifikan lebih mudah.

Jika Anda ingin menguji sesuatu yang menggunakan instance Dice (misalnya kelas Game), maka dari pengujian Anda, Anda dapat menyuntikkan beberapa bentuk uji ganda Dice yang selalu mengembalikan urutan nilai yang tetap. Tes Anda dapat memeriksa apakah permainan memiliki hasil yang tepat untuk gulungan dadu tersebut.

Saya bukan pengembang Java, tetapi saya pikir akan jauh lebih sulit untuk melakukannya dengan kelas Dice yang sepenuhnya statis. Lihat /programming/4482315/why-does-mockito-not-mock-static-methods

bdsl
sumber
Hanya untuk catatan: Di Jawa kita memiliki PowerMock untuk menggantikan dependensi statis dengan memanipulasi kode byte. Tetapi menggunakannya hanyalah penyerahan pada desain yang buruk ...
Timothy Truckle
0

Ini sebenarnya dikenal sebagai pola Monostate , di mana setiap instance (dan bahkan "no-instance") berbagi status mereka. Setiap anggota adalah anggota kelas (yaitu, tidak ada anggota contoh). Mereka biasanya digunakan untuk mengimplementasikan kelas "toolkit" yang menggabungkan satu set metode dan konstanta yang terkait dengan satu tanggung jawab atau persyaratan tetapi tidak memerlukan status untuk bekerja (mereka murni fungsional). Bahkan, Java dilengkapi dengan beberapa dari mereka yang dibundel (misalnya, Matematika ).

Agak di luar topik: Saya jarang setuju dengan penamaan kata kunci di VisualBasic, tetapi dalam kasus ini, saya pikir sharedtentu saja lebih jelas dan semantik lebih baik ( dibagikan di antara kelas itu sendiri dan semua contohnya) daripada static(tetap setelah siklus hidup dari ruang lingkup yang dideklarasikan dalam).

Jesus Alonso Abad
sumber