Làm thế nào để phân chia các lớp lớn, kết hợp chặt chẽ?


14

Tôi có một số lớp lớn hơn 2k dòng mã (và đang phát triển) mà tôi muốn cấu trúc lại nếu có thể, để có một số thiết kế gọn gàng và nhẹ nhàng hơn.

Lý do nó lớn như vậy chủ yếu là vì các lớp này xử lý một tập hợp các bản đồ mà hầu hết các phương thức cần truy cập và các phương thức rất được kết nối với nhau.

Tôi sẽ đưa ra một ví dụ rất cụ thể: Tôi có một lớp được gọi là Serverxử lý các tin nhắn đến. Nó có các phương pháp như joinChatroom, searchUsers, sendPrivateMessage, vv Tất cả những phương pháp này thao tác các bản đồ như users, chatrooms, servers, ...

Có lẽ sẽ rất tuyệt nếu tôi có thể có một lớp xử lý các thông báo liên quan đến Chatroom, một cách xử lý khác về Người dùng, v.v., nhưng vấn đề chính ở đây là tôi cần sử dụng tất cả các bản đồ trong hầu hết các phương pháp. Đó là lý do tại sao bây giờ tất cả họ đều gắn bó trong Serverlớp vì tất cả họ đều dựa vào các bản đồ chung này và các phương thức rất kết nối với nhau.

Tôi sẽ cần phải tạo một Chatroom lớp, nhưng với một tham chiếu đến từng đối tượng khác. Một người dùng lớp một lần nữa với một tham chiếu đến tất cả các đối tượng khác, v.v.

Tôi cảm thấy như mình sẽ làm điều gì đó sai.


Nếu bạn tạo các lớp như Người dùng và Phòng chat, các lớp này chỉ cần một tham chiếu đến cấu trúc dữ liệu chung hay chúng sẽ tham chiếu lẫn nhau?

Có một số câu trả lời thỏa đáng ở đây, bạn nên chọn một.
jeremyjjbrown

@jeremyjjbrown câu hỏi đã được di chuyển và tôi đã mất nó. Chọn một câu trả lời, thx.
Matthew

Câu trả lời:


10

Từ mô tả của bạn, tôi đoán rằng bản đồ của bạn hoàn toàn là túi dữ liệu, với tất cả logic trong các Serverphương thức. Bằng cách đẩy tất cả logic phòng chat vào một lớp riêng biệt, bạn vẫn bị mắc kẹt với các bản đồ chứa dữ liệu.

Thay vào đó, hãy thử mô hình các phòng chat cá nhân, người dùng, v.v. làm đối tượng. Bằng cách đó, bạn sẽ chỉ đi xung quanh các đối tượng cụ thể cần thiết cho một phương thức nhất định, thay vì các bản đồ dữ liệu khổng lồ.

Ví dụ:

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 */
}

Bây giờ thật dễ dàng để gọi một vài phương thức cụ thể để xử lý tin nhắn:

Bạn muốn tham gia một phòng chat?

chatroom.add(user);

Bạn muốn gửi một tin nhắn riêng tư?

user.sendMessage(msg);

Bạn muốn gửi một tin nhắn công cộng?

chatroom.sendMessage(msg);

5

Bạn sẽ có thể tạo một lớp chứa mỗi bộ sưu tập. Mặc dù Serversẽ cần một tham chiếu đến mỗi bộ sưu tập này nhưng nó chỉ cần số lượng logic tối thiểu không liên quan đến việc truy cập hoặc duy trì các bộ sưu tập cơ bản. Điều này sẽ làm rõ hơn chính xác những gì Máy chủ đang làm và tách biệt cách thức thực hiện.


4

Khi tôi thấy những lớp học lớn như thế này tôi đã thấy rằng thường có một lớp (hoặc nhiều hơn) ở đó cố gắng thoát ra. Nếu bạn biết một phương thức mà bạn nghĩ có thể không liên quan đến lớp này thì hãy làm cho nó tĩnh. Trình biên dịch sau đó sẽ cho bạn biết các phương thức khác mà methd này gọi. Java sẽ nhấn mạnh rằng họ cũng tĩnh. Bạn làm cho chúng tĩnh. Một lần nữa trình biên dịch sẽ cho bạn biết bất kỳ phương thức nào mà nó gọi. Bạn tiếp tục làm điều này nhiều lần cho đến khi bạn không còn thất bại biên dịch nữa. Sau đó, bạn có một tải các phương thức tĩnh trong lớp lớn của bạn. Bây giờ bạn có thể kéo chúng ra một lớp mới và làm cho phương thức không tĩnh. Sau đó, bạn có thể gọi lớp mới này từ lớp lớn ban đầu của mình (hiện tại sẽ chứa ít dòng hơn)

Sau đó, bạn có thể lặp lại quy trình cho đến khi bạn hài lòng với thiết kế lớp.

Cuốn sách của Martin Fowler là một cuốn sách rất hay, vì vậy tôi cũng muốn giới thiệu điều này vì có những lúc bạn không thể sử dụng thủ thuật tĩnh này.


1
Cuốn sách này của Martin Fowler martinfowler.com/books/refactoring.html
Arul

1

Vì hầu hết mã của bạn đã tồn tại, tôi sẽ khuyên bạn nên sử dụng các lớp của trình trợ giúp để loại bỏ các phương thức của bạn. Điều đó sẽ giúp tái cấu trúc dễ dàng. Vì vậy, lớp máy chủ của bạn vẫn sẽ chứa các bản đồ trong đó. Nhưng nó sử dụng một lớp của trình trợ giúp cho biết ChatroomHelper với các phương thức như tham gia (Chatroom Map, String user), List getUsers (Map chatroom), Map getChatroom (String user).

Lớp máy chủ sẽ giữ một thể hiện của ChatroomHelper, UserHelper, v.v. do đó di chuyển các phương thức sang các lớp trình trợ giúp logic của nó. Nếu bạn có thể giữ nguyên các phương thức công khai trong máy chủ, vì vậy bất kỳ người gọi nào cũng không cần phải thay đổi.


1

Để thêm vào câu trả lời sâu sắc của casifer - nếu nhiều lớp cần thực hiện những điều cơ bản giống nhau với một số loại thực thể (thêm người dùng vào bộ sưu tập, xử lý tin nhắn, v.v.), các quy trình đó cũng phải được tách riêng.

Có nhiều cách để làm điều này - bằng cách kế thừa hoặc thành phần. Để kế thừa, bạn có thể sử dụng các lớp cơ sở trừu tượng với các phương thức cụ thể cung cấp các trường và chức năng để xử lý người dùng hoặc tin nhắn chẳng hạn, và có các thực thể như chatroomuser(hoặc bất kỳ loại nào khác) mở rộng các lớp đó.

Vì nhiều lý do , đây là một quy tắc chung tốt để sử dụng thành phần so với thừa kế. Bạn có thể sử dụng thành phần để làm điều này theo nhiều cách khác nhau. Vì việc xử lý người dùng hoặc tin nhắn là các chức năng tập trung vào trách nhiệm của các lớp, nên có thể lập luận rằng việc tiêm constructor là phù hợp nhất. Theo cách này, sự phụ thuộc là trong suốt và một đối tượng không thể được tạo mà không có chức năng cần thiết. Nếu cách xử lý của người dùng hoặc tin nhắn có thể thay đổi hoặc được mở rộng, bạn nên xem xét sử dụng một cái gì đó giống như mẫu chiến lược .

Trong cả hai trường hợp, hãy chắc chắn mã theo các giao diện, không phải các lớp cụ thể để linh hoạt.

Tất cả những gì đang được nói, luôn luôn xem xét chi phí tăng thêm độ phức tạp khi sử dụng các mẫu như vậy - nếu bạn sẽ không cần nó, đừng mã hóa nó. Nếu bạn biết rằng rất có thể bạn sẽ không thay đổi cách xử lý người dùng / tin nhắn được thực hiện, bạn không cần sự phức tạp về cấu trúc của mẫu chiến lược - nhưng để tách mối quan tâm và tránh lặp lại, bạn vẫn nên ly dị chức năng chung từ các trường hợp cụ thể sử dụng nó - và, nếu không có lý do ghi đè nào ngược lại, hãy kết hợp những người sử dụng chức năng xử lý đó (phòng chat, người dùng) với một đối tượng thực hiện việc xử lý.

Vì vậy, để tổng hợp:

  1. Như casifer đã viết: Tách biệt và đóng gói các phòng chat, người dùng, v.v.
  2. Chức năng chung riêng biệt
  3. Xem xét tách chức năng cá nhân để phân tách dữ liệu đại diện (cũng như truy cập và đột biến) khỏi chức năng phức tạp hơn so với các trường hợp riêng lẻ của dữ liệu hoặc tổng hợp của chúng (ví dụ: một cái gì đó giống như searchUserscó thể đi vào lớp tập hợp hoặc kho lưu trữ / bản đồ nhận dạng )

0

Hãy nhìn xem, tôi nghĩ rằng câu hỏi của bạn quá chung chung để trả lời vì chúng tôi không thực sự có một mô tả đầy đủ về vấn đề nên việc đưa ra một thiết kế tốt với rất ít kiến ​​thức là không thể. Tôi có thể, chỉ là một ví dụ, giải quyết một trong những mối quan tâm của bạn về khả năng vô ích của thiết kế tốt hơn.

Bạn nói rằng lớp Máy chủ của bạn và lớp Chatroom trong tương lai của bạn chia sẻ dữ liệu về người dùng, nhưng dữ liệu này sẽ khác nhau. Máy chủ có thể có một nhóm người dùng được kết nối với nó, trong khi Chatroom, thuộc về Máy chủ, có một nhóm người dùng khác, một nhóm phụ của nhóm đầu tiên, chỉ những người dùng hiện đang đăng nhập vào một Chatroom cụ thể.

Đây không phải là thông tin giống nhau, ngay cả khi các loại dữ liệu giống hệt nhau.
Có rất nhiều lợi thế cho một thiết kế tốt.

Tôi chưa đọc cuốn sách đã nói ở trên của Fowler, nhưng tôi đã đọc những thứ khác của Folwer và nó được đề xuất cho tôi bởi những người tôi tin tưởng, vì vậy tôi cảm thấy đủ thoải mái để đồng tình với những người khác.


0

Sự cần thiết phải truy cập vào các bản đồ không chứng minh được lớp lớn. Bạn phải tách logic trong một số lớp và mỗi lớp phải có phương thức getMap để các lớp khác có thể truy cập vào bản đồ.


0

Tôi sẽ sử dụng cùng một câu trả lời tôi đã cung cấp ở nơi khác: tham gia lớp học nguyên khối và phân chia trách nhiệm của nó cho các lớp khác. Cả DCI và Mẫu khách truy cập đều cung cấp các tùy chọn tốt để làm như vậy.


-1

Về mặt số liệu phần mềm, lớp lớn là túi. Có những giấy tờ không giới hạn chứng minh tuyên bố này. Tại sao vậy ? bởi vì các lớp lớn khó hiểu hơn các lớp nhỏ và nó cần nhiều thời gian hơn để sửa đổi. Hơn nữa, các lớp lớn rất khó khăn khi bạn làm bài kiểm tra. Và các lớp lớn rất khó cho bạn khi bạn muốn sử dụng lại vì rất có thể nó chứa những thứ không mong muốn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.