Apakah struktur kode ini bermanfaat?


8

Saya baru-baru ini dilemparkan ke proyek aplikasi web Java, dan saya telah menemukan sejumlah kelas yang mengikuti jenis format ini:

public class MyThingy {
   private final int p1;
   private final String p2;
   

   public MyThingy (int p1, String p2, …) {
      this.p1 = p1;
      this.p2 = p2;
      
   }

   public static void doSomething(int p1, String p2, …) throws Throwable     {
      final MyThingy myThingy = new MyThingy(p1, p2, …);
      myThingy.execute();
   }

   private void execute() throws Throwable {
      //do stuff 
   }
}

Sepertinya ini bisa diselesaikan dengan kode berikut, yang bagi saya tampaknya lebih mudah dibaca.

public class MyThingy {

   public static void doSomething (int p1, String p2, …) throws Throwable {
      //do stuff 
   }

}

Satu-satunya keuntungan yang bisa saya lihat dari melakukannya dengan cara pertama, adalah bahwa jika Anda harus memecah eksekusi () menjadi bagian-bagian yang lebih kecil, mereka semua dapat berbagi parameter awal tanpa harus secara eksplisit membagikannya. Tapi ini mungkin hanya menguntungkan pemodal malas, karena menjadi sulit bagi pembaca untuk mengetahui metode mana yang membutuhkan parameter mana dan kapan nilainya dapat diubah (mirip dengan variabel global.)

Apakah ada sesuatu yang saya lewatkan? Threading, kinerja?

Sunting: Saya seharusnya telah menyebutkan, meskipun konstruktor bersifat publik, tidak disebut. Satu-satunya penggunaan adalah seperti ini:

MyThingy.doSomething(p1, p2...);

Selain dari itu sendiri yang bermasalah untuk pengujian, saya tidak bisa melihat alasan untuk tidak menempatkan logika eksekusi () langsung ke doSomething (). Bahkan jika kita harus menyingkirkan fungsi statis, konstruktor masih tidak masuk akal bagi saya; Saya pikir parameter harus diteruskan langsung ke metode yang akan menggunakannya.


Saya tidak mengira standar perusahaan mengatakan sesuatu tentang menghindari fungsi statis? Atau, pengujian otomatis seperti apa yang dilakukan dengan kode ini? Mengejek menjadi hampir mustahil ketika fungsi statis diperkenalkan.
KChaloux

3
Sama seperti catatan: throws Throwableadalah praktik yang buruk, itu harus setidaknya throws Exception, atau sesuatu yang lebih spesifik, jika memungkinkan. Dan, karena praktik buruk biasanya muncul bersamaan, saya akan mengatakan bahwa templat kode ini hanyalah praktik buruk lainnya.
scriptin

Metode statis hanyalah pembantu untuk melakukan new(...)+ execute()dalam satu panggilan.
Kasey Speakman

Anda bisa mengubahnya: ini adalah solusi yang jelek. Akan lebih baik untuk memisahkan pembuatan objek dari konsumsi.
Thomas Junk

1
Juga perhatikan bahwa penulis jelas dimaksudkan agar objek yang dibangun tidak berubah (data adalah final). Metode statis adalah satu-satunya API ke objek karena semua anggota bersifat pribadi. Hanya dalam kode yang telah Anda bagikan, saya tidak melihat manfaatnya dengan membuat executemetode statis dengan mengambil p1, p2, ....
Kasey Speakman

Jawaban:


2

Saya pikir intuisi Anda tentang masalah ini benar.

  • Apa yang kita miliki di sini adalah metode polos (dalam hal ini, statis) yang implementasinya telah "dipecah" menjadi sebuah kelas.
  • Teknik "metode sebagai kelas" mengurangi jumlah variabel yang harus Anda berikan secara eksplisit, sementara pada kenyataannya mengaburkan bagaimana data mengalir menggunakan apa yang, dalam semangat, variabel "global".
  • Tidak satu pun dari ini yang benar-benar dalam semangat OO, karena contohnya adalah dibuang dan keadaannya fana.
  • Namun, ini adalah pola umum, terutama dalam buku-buku Jawa pengantar dan mungkin ada di benak desainer Jawa (yang memperkenalkan banyak cara untuk mendeklarasikan kelas, tampaknya untuk membantu penggunaan ini).

Jadi, haruskah Anda menggunakannya ?

  • Tentu, terkadang.

Bahkan, yang dipermasalahkan bukan hanya "metode sebagai kelas" murni. Di kelas normal, Anda mungkin tergoda untuk membuat bidang yang tidak memiliki pemanggilan metode publik di sela-sela. Mereka hanya digunakan untuk menghindari melewati parameter antara metode pribadi. Di masa lalu, saya mencoba untuk menghindarinya sama sekali, tetapi kemudian menyadari bahwa daftar parameter panjang juga menyedot.

Saya mencoba untuk menghindari "kelas metode" murni karena saya pikir mereka datang dengan banyak beban mental (begitu banyak potensi potensial). (Saya juga bersandar pada bidang sekali pakai di kelas reguler.) Tetapi jika ada satu ton variabel untuk dibagikan, kemenangan dalam kebersihan kode mungkin sepadan.

Saya mencoba menggunakan kelas ketika ada poin untuk itu, dan sebenarnya biasanya mudah untuk membayangkannya. Mungkin di masa depan Anda akan memperluas kelas dengan implementasi tambahan. Mungkin instance dapat dianggap sebagai objek fungsi yang ingin Anda inisialisasi dan diedarkan. Dan kemudian, kelas tidak lagi menjadi "metode kelas" pseudo-OO.


1
Saya suka apa yang Anda sarankan di paragraf terakhir di sini. Jika metode execute () bersifat publik, saya dapat membuat (dan menginisialisasi) objek, kemudian memiliki sesuatu yang mengeksekusinya nanti. Itu mungkin masuk akal, dan juga akan meningkatkan testabilitas. Dalam hal ini sebenarnya fungsi statis yang tidak tersedia.
Mishelle

Ini adalah jawaban yang ditunggu-tunggu oleh pertanyaan ini. +1
cbojar

6

Tipuan ekstra melalui panggilan metode statis memisahkan kekhawatiran tentang bagaimana suatu objek dibuat dari kode yang menggunakan objek. Apa yang Anda miliki di sini adalah sesuatu yang sangat mirip dengan metode pabrik sederhana (itu sebenarnya tidak mengembalikan objek, tetapi tipuan pembuatan objek adalah sama).

Ini bisa berguna jika Anda perlu mengontrol tipe objek yang dibuat. Dalam contoh sederhana ini tidak ada keputusan untuk dibuat, tetapi akan mudah untuk menambahkan logika itu.

Apa arti kode statis untuk penelepon adalah "Saya perlu melakukan sesuatu dengan keadaan ini tetapi tidak tahu bagaimana mendelegasikan." Metode statis kemudian dapat mendelegasikan ke implementasi yang tepat berdasarkan objek yang disediakan.

Mungkin ketika menjalankan dalam unit test ada objek tiruan yang digunakan. Mungkin berdasarkan parameter objek yang berbeda dapat ditukar dalam mengimplementasikan algoritma yang berbeda untuk melakukan apa pun yang perlu dilakukan. Dengan melokalkan kode itu ke satu lokasi, keputusan dapat dibuat di satu tempat dan bukan di banyak tempat.


Mengingat bagaimana pertanyaan Anda diucapkan dan fakta bahwa tidak ada keputusan konstruksi dibuat dalam metode statis, saya merasa ini mungkin hanya kasus over-engineering. Sebagian besar kode tidak perlu didelegasikan ke pabrik. Fakta bahwa Anda terus menemukan kode seperti ini yang tidak membuat keputusan seperti yang saya harapkan metode seperti pabrik statis lakukan memberitahu saya seseorang membaca buku GoF dan mengamuk.


1
Di mana Anda melihat metode Pabrik? Tidak ada contoh OP yang mengembalikan apapun.
Robert Harvey

@RobertHarvey: Ini adalah metode pabrik yang efektif. Hanya karena tidak terpapar ke ruang info bukan berarti itu bukan pabrik!
Lightness Races in Orbit

1
@ RobertTarvey Saya pikir itu adalah masalah pilihan kata. Berperilaku serupa dengan metode pabrik, tetapi tidak benar-benar mengembalikan apa pun. Dan lagi, saya tidak pernah mengklaim itu, hanya bahwa itu didelegasikan, yang masih merupakan pernyataan yang akurat.

Saya setuju dengan @RobertHarvey. Objek yang dibangun dan digunakan sepenuhnya dalam suatu metode adalah detail implementasi, bukan produk pabrik.
cbojar

Menarik. Saya memodifikasi terminologi dalam jawaban saya, tetapi poin yang saya buat identik. Aneh bagaimana itu terjadi ...

0

Nah, jika contoh kedua Anda (menunjukkan kode lengkap ) terlihat seperti ini:

public static void doSomething(int p1, String p2, …) throws Throwable     {
      final MyThingy myThingy = new MyThingy(p1, p2, …);
      myThingy.execute();
   }

maka Anda masih akan membutuhkan konstruktor

public MyThingy (int p1, String p2, …) {
      this.p1 = p1;
      this.p2 = p2;
      
   }

yang pada gilirannya mengatur

   private final int p1;
   private final String p2;

dan sekarang Anda kembali ke tempat Anda mulai.

Anda bisa, tentu saja, hanya menghilangkan konstruktor dan pergi dengan getter dan setter sebagai gantinya

private final int p1;
private final String p2;

public void SetP1(int p1)
{
    this.p1 = p1;
}

public int GetP1()
{
    return p1;
}
// repeat for p2

... tetapi ini akan menyulitkan pemanggilan metode statis Anda, karena Anda sekarang akan memerlukan beberapa baris kode untuk memanggil setter terlebih dahulu sebelum memanggil metode statis Anda, di mana satu baris yang menggunakan panggilan konstruktor akan cukup.

Tentu saja, ini hanya berfungsi jika variabel anggota Anda tidak final. Karena itu, Anda masih memerlukan konstruktor.


Tidak masuk akal untuk memiliki setter publik pada finalvariabel. Ini hanya dapat diinisialisasi sekali.
Kasey Speakman

1
@KaseySpeakman: Ergo, Anda masih membutuhkan konstruktor.
Robert Harvey

Saya tidak sepenuhnya yakin bahwa jawaban ini berbicara pada pertanyaan ...
cbojar

Terlebih lagi, itu tidak valid (memiliki kesalahan kompilasi): ideone.com/0nDRgY --- nilai akhir harus dapat dibuktikan tidak dapat diselesaikan di tempat lain. Memiliki setter membuat itu tidak mungkin, dan dengan demikian kesalahan kompilasi.

@MichaelT: Saya sudah mengatakan itu dalam jawaban saya (pada paragraf terakhir).
Robert Harvey

0

Biasanya sebuah kelas memiliki banyak metode instance, dan begitu objek dibuat, implementasi eksekusi dapat mengambil keuntungan dari semua metode instance ini. Jika Anda menolak untuk membuat objek, semua metode instance ini tidak berguna.


Saya pikir OP mengatakan bahwa tidak ada metode contoh lain di samping execute. Jadi, bertanya mengapa tidak menjelaskan semuanya.
cbojar

Ya, itulah pertanyaan saya!
Mishelle
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.