Java - 这是一个糟糕的设计模式吗?

3
在我们的应用程序中,我看到了这样的代码编写方式:

User.java(用户实体)

public class User
{
  protected String firstName;
  protected String lastName;

 ...
   getters/setters (regular POJO)
}

UserSearchCommand
{
   protected List<User> users;
   protected int currentPage;
   protected int sortColumnIndex;
   protected SortOder sortOrder;

   // the current user we're editing, if at all
   protected User user;

   public String getFirstName()
   {return(user.getFirstName());}

   public String getLastName()
   {return(user.getLastName());}

}

根据我的经验,这个模式或反模式看起来很糟糕。首先,我们将几个关注点混合在一起。虽然它们都与用户相关,但偏离了典型的POJO设计。如果我们要采取这种方法,那么难道不应该这样做吗?

UserSearchCommand
{
   protected List<User> users;
   protected int currentPage;
   protected int sortColumnIndex;
   protected SortOder sortOrder;

   // the current user we're editing, if at all
   protected User user;

   public User getUser()
   {return(user);}

}

我们只需返回用户对象,然后可以随意调用它上面的任何方法?由于这与典型的bean开发非常不同,因此JSR 303的bean验证对于这种模型不起作用,我们必须为每个bean编写验证器。

还有其他人认为这种设计模式有什么问题,还是我作为开发人员太过挑剔?

沃尔特


2
也许你有一个更根本的问题:为什么要在你的“搜索”对象中编辑用户? - Juliet
1
@Walter White:但是在这个人们都沉迷于“最终状态”(包括Joshua Bloch)并且到处都在谈论“不可变性”和“有效不可变性”,甚至整个编程语言都围绕着不可变性的概念设计的时代,一个通过setter方法可变的POJO本身的概念难道不是一种非常糟糕的代码味道和反模式吗? ;) - SyntaxT3rr0r
朱丽叶,我同意你的评论,那就是我说的。我不认为我们应该混淆这些问题。他们的评论是这样说的,这样更容易理解。WizardOfOdds,我部分地同意你的评论,但这仍然混淆了问题。 - Walter White
5个回答

3

那么使用接口的第三种选择怎么样?

UserSearchCommand
{
  protected List<User> users;
  protected int currentPage;
  protected int sortColumnIndex;
  protected SortOder sortOrder;

  // the current user we're editing, if at all
  protected User user;

  public I_UserNameDetails getUser()
  {
    return((I_UserNameDetails)user);
  }
}

现在你可以通过接口实现抽象并防止用户对象被修改。

这就是我会做的。接口正是为这些事情设计的。事实上,如果你已经在担心模式和反模式,类根本不应该暴露出来。相反应该暴露接口。尽可能少,但是必要的接口要尽可能多。 - Armen Michaeli

3
在返回用户对象时,您允许UserSearchCommand在现有数据上编写新信息,这可能不是我们想要允许的,因为搜索应该只允许读取数据。此外,您所做的是使使用UserSearchCommand的人必须了解User类中的方法/属性/成员,而这不是第一个实现中的情况。

2

1
迪米特法则建议采用第二个例子,对吧?如果你正在编辑一个用户,只与该用户交互,而不是通过UserSearchCommand进行操作。 - Walter White

0

虽然Sjoerd和JB提出了有效的观点,但根据您对SearchCommand的使用方式,我会为第二个示例提出以下论点。如果您在第一个示例中定义的操作不影响UserSearchCommand的行为,那么通过定义getFirstName()等,您实际上只是复制代码,这可能会导致可维护性问题,例如,如果以后将中间名添加到用户类中怎么办?那么您不仅需要将其添加到用户中,还需要在UserSearchCommand中添加访问器。如果对用户进行某些操作会修改搜索的行为,则可以通过调用方通过搜索命令访问用户,这可能是一个有效的论点,但也可以通过诸如PropertyListeners之类的机制实现。

正如您所指出的,第一个示例混合了信息,并且在面向对象编程的角度来看,似乎有些违反直觉。如果这是限制对某些属性的访问权限的问题,则可能是更改访问修饰符的问题,或者创建公开您认为安全的内容的接口和公开适当包/受保护属性的实现类。除非您的UserSearchCommand名称不正确,否则我希望它执行涉及搜索用户的操作,然后将这些用户作为一个单元(而不是用户的各个属性)提供。也就是说,搜索命令应执行与搜索相关的操作,而用户应包含有关用户的信息。

当然这是一个风格问题,但我会投票支持你的第二个例子。


这就是我要说的,为什么要复制代码?这没有意义。我的理解是它被开发出来并不是因为容易,而是为了让其他东西适用于这个人的控制器之类的东西。不,我们不会做任何这样的事情。 - Walter White

0

我同意你的观点。我也看到过这种技术,但不明白它的意义:这只是复制了大量的代码,为什么要这样做呢?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接