Bagaimana cara membagi kelas yang besar dan sangat erat?

14

Saya memiliki beberapa kelas besar lebih dari 2k baris kode (dan terus bertambah) yang saya ingin refactor jika mungkin, untuk memiliki beberapa desain yang lebih ringan dan bersih.

Alasannya begitu besar terutama karena kelas-kelas ini menangani satu set peta yang sebagian besar metode perlu akses, dan metode-metode tersebut sangat terhubung satu sama lain.

Saya akan memberikan contoh yang sangat konkret: Saya memiliki kelas yang disebut Serveryang menangani pesan masuk. Ini memiliki metode seperti joinChatroom, searchUsers, sendPrivateMessage, dll Semua metode ini memanipulasi peta seperti users, chatrooms, servers, ...

Mungkin akan menyenangkan jika saya dapat memiliki kelas yang menangani pesan tentang Ruang Chat, yang lain menangani semua tentang Pengguna, dll. Tetapi masalah utama di sini adalah bahwa saya perlu menggunakan semua peta di sebagian besar metode. Itu sebabnya untuk saat ini mereka semua menempel di Serverkelas karena mereka semua bergantung pada peta umum ini dan metode yang sangat terhubung satu sama lain.

Saya perlu membuat ruang obrolan kelas, tetapi dengan referensi ke masing-masing objek lainnya. Pengguna kelas lagi dengan referensi ke semua objek lain, dll.

Saya merasa akan melakukan sesuatu yang salah.

Matius
sumber
Jika Anda ingin membuat kelas seperti Ruang Pengguna dan Ruang Obrolan, apakah kelas-kelas ini hanya perlu referensi ke struktur data umum atau akankah mereka saling referensi?
Ada beberapa jawaban yang memuaskan di sini, Anda harus memilih satu.
jeremyjjbrown
@ jeremyjjbrown pertanyaan telah dipindahkan dan saya kehilangan itu. Memilih jawaban, thx.
Matius

Jawaban:

10

Dari uraian Anda, saya akan menebak bahwa peta Anda murni kantong data, dengan semua logika dalam Servermetode. Dengan mendorong semua logika ruang obrolan ke kelas yang terpisah, Anda masih terjebak dengan peta yang berisi data.

Sebagai gantinya, cobalah memodelkan masing-masing ruang obrolan, pengguna, dll sebagai objek. Dengan begitu, Anda hanya akan membagikan objek tertentu yang diperlukan untuk metode tertentu, alih-alih peta data yang sangat besar.

Sebagai contoh:

public class User {
  private String name;
  ...

  public void sendMessage(String message) {
    ...
  }
}

public class Chatroom {
  // users in this chatroom
  private Collection<User> users;

  public void add(User user) {
    users.add(user);
  }

  public void sendMessage(String msg) {
    for (User user : users)
      user.sendMessage(msg);
  }
}

public class Server {
  // all users on the server
  private Collection<User> users;

  // all chatrooms on the server
  private Collection<Chatroom> chatrooms;

  /* methods to handle incoming messages */
}

Sekarang mudah untuk memanggil beberapa metode khusus untuk menangani pesan:

Ingin bergabung dengan ruang obrolan?

chatroom.add(user);

Ingin mengirim pesan pribadi?

user.sendMessage(msg);

Ingin mengirim pesan publik?

chatroom.sendMessage(msg);
casablanca
sumber
5

Anda harus dapat membuat kelas yang menampung setiap koleksi. Meskipun Serverakan memerlukan referensi ke masing-masing koleksi ini, hanya perlu jumlah minimal logika yang tidak akan melibatkan mengakses atau mempertahankan koleksi yang mendasarinya. Ini akan membuat lebih jelas apa yang sedang dilakukan Server, dan memisahkan bagaimana melakukannya.

Peter Lawrey
sumber
4

Ketika saya telah melihat kelas-kelas besar seperti ini saya telah menemukan bahwa sering ada kelas (atau lebih) di sana berusaha keluar. Jika Anda mengetahui metode yang menurut Anda mungkin tidak terkait dengan kelas ini maka buatlah statis. Kompiler kemudian akan memberi tahu Anda metode lain yang dipanggil metode ini. Java akan bersikeras mereka juga statis. Anda membuatnya statis. Sekali lagi kompiler akan memberi tahu Anda tentang metode apa pun yang dipanggil. Anda terus melakukan ini berulang-ulang sampai Anda tidak lagi mengalami kegagalan kompilasi. Maka Anda memiliki banyak metode statis di kelas besar Anda. Anda sekarang dapat menarik ini ke kelas baru dan membuat metode ini tidak statis. Anda kemudian dapat memanggil kelas baru ini dari kelas besar asli Anda (yang sekarang seharusnya mengandung lebih sedikit baris)

Anda kemudian dapat mengulangi prosesnya sampai Anda puas dengan desain kelas.

Buku Martin Fowler adalah bacaan yang sangat bagus sehingga saya akan merekomendasikan ini juga karena ada kalanya Anda tidak dapat menggunakan trik statis ini.

RNJ
sumber
1
Buku Martin Fowler ini martinfowler.com/books/refactoring.html
Arul
1

Karena sebagian besar kode Anda ada, saya sarankan menggunakan kelas pembantu untuk memindahkan metode Anda. Itu akan membantu refactoring mudah. Jadi, kelas server Anda masih akan berisi peta di dalamnya. Tetapi menggunakan kelas pembantu mengatakan ChatroomHelper dengan metode seperti bergabung (Peta ruang obrolan, pengguna String), Daftar getUsers (Peta ruang obrolan), Peta getChatroom (Pengguna tali).

Kelas server akan mengadakan instance dari ChatroomHelper, UserHelper dll sehingga memindahkan metode ke kelas pembantu logisnya. Dengan ini, Anda dapat membiarkan metode publik di server tetap utuh, jadi setiap penelepon tidak perlu berubah.

techoma soma
sumber
1

Untuk menambah jawaban mendalam casablanca - jika beberapa kelas perlu melakukan hal-hal dasar yang sama dengan beberapa jenis entitas (menambahkan pengguna ke koleksi, menangani pesan dll), proses-proses tersebut harus tetap terpisah juga.

Ada beberapa cara untuk melakukan ini - dengan warisan atau komposisi. Untuk pewarisan, Anda dapat menggunakan kelas dasar abstrak dengan metode konkret yang menyediakan bidang dan fungsionalitas untuk menangani pengguna atau pesan misalnya, dan memiliki entitas seperti chatroomdan user(atau lainnya) memperluas kelas-kelas tersebut.

Karena berbagai alasan , itu adalah aturan umum yang baik untuk menggunakan komposisi di atas warisan. Anda dapat menggunakan komposisi untuk melakukan ini dengan berbagai cara. Karena menangani pengguna atau pesan adalah fungsi utama tanggung jawab kelas, dapat dikatakan bahwa injeksi konstruktor paling tepat. Dengan cara ini, ketergantungannya transparan dan objek tidak dapat dibuat tanpa memiliki fungsionalitas yang diperlukan. Jika cara pengguna atau pesan ditangani cenderung berubah atau diperluas, Anda harus mempertimbangkan untuk menggunakan sesuatu seperti pola strategi .

Dalam kedua kasus, pastikan untuk kode ke antarmuka, bukan kelas konkret untuk fleksibilitas.

Semua yang dikatakan, selalu mempertimbangkan biaya kompleksitas tambahan saat menggunakan pola seperti itu - jika Anda tidak akan membutuhkannya, jangan kode itu. Jika Anda tahu Anda kemungkinan besar tidak akan mengubah cara penanganan pengguna / pesan dilakukan, Anda tidak perlu kompleksitas struktural dari pola strategi - tetapi untuk memisahkan masalah dan menghindari pengulangan, Anda harus tetap menceraikan fungsi umum dari contoh konkret yang menggunakannya - dan, jika tidak ada alasan utama yang bertentangan ada, buat pengguna fungsi-fungsi penanganan seperti itu (ruang obrolan, pengguna) dengan objek yang melakukan penanganan.

Jadi, untuk meringkas:

  1. Seperti yang ditulis casablanca: Pisahkan dan enkapsulasi ruang obrolan, pengguna, dll.
  2. Pisahkan fungsionalitas umum
  3. Pertimbangkan memisahkan fungsi individu untuk menceraikan representasi-data (serta akses dan mutasi) dari fungsionalitas yang lebih kompleks daripada contoh individual data atau agregatnya (misalnya, sesuatu seperti searchUsersbisa masuk dalam kelas koleksi, atau repositori / identitas-peta) )
Michael Bauer
sumber
0

Dengar, saya pikir pertanyaan Anda terlalu umum untuk dijawab karena kami tidak benar-benar memiliki deskripsi lengkap masalah sehingga menawarkan desain yang bagus dengan sedikit pengetahuan tidak mungkin dilakukan. Saya dapat, sebagai contoh, mengatasi salah satu kekhawatiran Anda tentang kemungkinan kesia-siaan desain yang lebih baik.

Anda mengatakan bahwa kelas Server Anda dan kelas Chatroom di masa depan Anda membagikan data tentang pengguna, tetapi data ini harus berbeda. Server mungkin memiliki satu set pengguna yang terhubung dengannya, sementara Chatroom, yang itu sendiri milik Server, memiliki set pengguna yang berbeda, sub-set set pertama, hanya pengguna yang saat ini masuk ke Chatroom tertentu.

Ini bukan informasi yang sama, bahkan jika tipe datanya identik.
Ada banyak banyak keuntungan dari desain yang bagus.

Saya belum membaca buku yang disebutkan sebelumnya oleh Fowler, tetapi saya telah membaca hal-hal lain oleh Folwer dan direkomendasikan kepada saya oleh orang-orang yang saya percayai, jadi saya merasa cukup nyaman untuk setuju dengan yang lain.

Shrulik
sumber
0

Kebutuhan untuk mengakses peta tidak membenarkan kelas mega. Anda harus memisahkan logika di beberapa kelas, dan setiap kelas harus memiliki metode getMap sehingga kelas lain dapat mengakses peta.

Tulains Córdova
sumber
0

Saya akan menggunakan jawaban yang sama yang saya berikan di tempat lain: mengambil kelas monolitik dan membagi tanggung jawabnya di antara kelas-kelas lain. DCI dan Pola Pengunjung memberikan opsi yang baik untuk melakukannya.

Mario T. Lanza
sumber
-1

Dalam hal metrik perangkat lunak, kelas besar adalah tas. Ada makalah tanpa batas yang membuktikan pernyataan ini. Mengapa demikian ? karena kelas besar lebih sulit untuk dipahami daripada kelas kecil dan perlu lebih banyak waktu untuk dimodifikasi. Apalagi kelas-kelas besar sangat sulit ketika Anda melakukan pengujian. Dan kelas-kelas besar sangat sulit bagi Anda ketika Anda ingin menggunakannya kembali karena sangat mungkin berisi hal-hal yang tidak diinginkan.

cat_minhv0
sumber