如何拆分紧密联系的大型课程?


14

我有一些巨大的类,它们包含超过2k行代码(并且还在不断增长),如果可能的话,我希望对其进行重构,以实现更加轻巧和简洁的设计。

它之所以如此之大,主要是因为这些类处理大多数方法都需要访问的一组映射,并且方法之间相互联系紧密。

我将举一个非常具体的示例:我有一个名为的类Server来处理传入消息。它有类似的方法joinChatroomsearchUserssendPrivateMessage等所有这些方法操纵映射,如userschatroomsservers,...

如果我可以有一个类来处理有关Chatrooms的消息,另一个可以处理所有有关Users的消息,等等,这也许会很好,但是这里的主要问题是我需要在大多数方法中使用所有地图。因此,Server由于它们都依赖于这些通用映射,并且方法之间相互联系紧密,因此它们现在都属于该类。

我需要创建一个类Chatrooms,但要引用其他每个对象。类的用户再次引用所有其他对象,等等。

我觉得我做错了什么。


如果您要创建诸如User和Chatroom之类的类,这些类是否仅需要引用公共数据结构,或者它们相互引用?

这里有几个令人满意的答案,您应该选择一个。
jeremyjjbrown 2014年

@jeremyjjbrown这个问题已经提出,我丢了。选择一个答案,谢谢。
马修

Answers:


10

根据您的描述,我想您的地图纯粹是数据包,其中包含Server方法中的所有逻辑。通过将所有聊天室逻辑推到一个单独的类中,您仍然会陷入包含数据的地图的困境。

而是尝试将各个聊天室,用户等建模为对象。这样,您将只传递某种方法所需的特定对象,而不是庞大的数据映射。

例如:

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

现在很容易调用一些特定的方法来处理消息:

要加入聊天室吗?

chatroom.add(user);

要发送私人消息吗?

user.sendMessage(msg);

要发送公开消息吗?

chatroom.sendMessage(msg);

5

您应该能够创建一个包含每个集合的类。虽然Server将需要引用这些集合中的每一个,但仅需要最少的逻辑量,而不会涉及访问或维护基础集合。这将使服务器正在做的事情变得更加明显,并分离出服务器的工作方式。


4

当我看到这样的大型班级时,我发现那里经常有一个班级(或更多班级)试图脱身。如果您知道可能与该类无关的方法,请使其变为静态。然后,编译器将告诉您该方法调用的其他方法。Java也会坚持认为它们也是静态的。您将它们设为静态。再次,编译器将告诉您它调用的任何方法。您将一遍又一遍地执行此操作,直到没有更多的编译失败为止。然后,您的大类中就有大量静态方法。现在,您可以将它们拉到一个新类中,并使方法变为非静态。然后,您可以从原来的大类(现在应该包含更少的行)中调用此新类

然后,您可以重复该过程,直到对类设计满意为止。

马丁·福勒(Martin Fowler)的书非常好读,因此我也推荐这样做,因为有时您不能使用此静态技巧。


1
这本马丁·福勒(Martin Fowler)的书 martinfowler.com/books/refactoring.html
阿鲁(Arul)

1

由于您的大多数代码都已存在,因此我建议您使用辅助类来移出您的方法。这将有助于轻松进行重构。因此,您的服务器类仍将包含映射。但是它使用了一个名为ChatroomHelper的帮助器类,该类具有诸如join(Map chatrooms,String user),List getUsers(Map chatrooms),Map getChatrooms(String user)之类的方法。

服务器类将包含ChatroomHelper,UserHelper等的实例,从而将方法移至其逻辑帮助器类。这样您就可以保留服务器中的公共方法完整,因此任何调用者都不需要更改。


1

为了增加casablanca的有见地的答案 -如果多个类需要对某种类型的实体执行相同的基本操作(将用户添加到集合,处理消息等),则这些过程也应保持分开。

有多种方法可以做到这一点-通过继承或组合。对于继承,您可以将抽象基类与具体方法一起使用,这些具体方法提供例如用于处理用户或消息的字段和功能,并具有诸如chatroomuser(或任何其他)实体来扩展这些类。

由于各种原因,使用组合而不是继承是一个好的通用规则。您可以使用合成以多种方式执行此操作。由于处理用户或消息是类职责的核心功能,因此可以说构造函数注入是最合适的。这样,依赖关系是透明的,并且没有必需的功能就无法创建对象。如果处理用户或消息的方式可能改变或扩展,则应考虑使用诸如strategy pattern之类的方法

在这两种情况下,请务必针对接口而不是针对灵活性的具体类进行编码。

话虽如此,在使用这种模式时,请务必考虑增加复杂性的代价-如果您不需要它,请不要对其进行编码。如果您知道您极有可能不打算更改用户/消息的处理方式,则不需要策略模式的结构复杂性,但是为了分开关注点并避免重复,您仍然应该将通用功能离婚从使用它的具体实例开始,如果没有相反的压倒性原因,则将具有这种处理功能的用户(聊天室,用户)与一个进行处理的对象组成。

因此,总结一下:

  1. 正如casablanca所说:分隔并封装聊天室,用户等。
  2. 单独的常用功能
  3. 考虑将单个功能与数据的单个实例或其集合上的更复杂的功能相分离,以使数据表示(以及访问和变异)脱离(例如,searchUsers可能在集合类或存储库/身份映射中使用) )

0

看,我认为您的问题太笼统了,因为我们实际上没有完整地描述问题,因此,以很少的知识提供良好的设计是不可能的。举例来说,我可以解决您对更好设计可能徒劳无益的担忧。

您说服务器类和将来的聊天室类共享有关用户的数据,但是这些数据应该不同。服务器可能有一组与之连接的用户,而聊天室本身属于服务器,则具有一组不同的用户,即第一组的子集,只有当前登录到特定聊天室的用户。

即使数据类型相同,此信息也不相同。
好的设计有很多优点。

我还没有读过Fowler的上述著作,但是我读了Folwer的其他著作,并且由我信任的人向我推荐,所以我很舒服地同意其他人的看法。




-1

就软件指标而言,大类是包。有无数文件证明了这一说法。这是为什么 ?因为大班比小班难理解,并且需要更多时间进行修改。而且,在进行测试时,大类是如此困难。当您想重用它时,大类对您来说非常困难,因为它很可能包含不需要的东西。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.