【问题标题】:Java web application MVC approach needs critiqueJava Web 应用程序 MVC 方法需要批评
【发布时间】:2010-11-12 00:23:21
【问题描述】:

我有一个 servlet,它调用通用 actions 并传入一个表单和对象(取决于操作需要什么)

CommitmentServlet.java

CommitmentListDAO clDAO = new CommitmentListDAO();
CommitmentItemForm form = new CommitmentItemForm(clDAO);
CommitmentItem obj = new CommitmentItem();

actionMap.put(null, new ListAction(form);
actionMap.put("list", new ListAction(form);
actionMap.put("view", new ViewAction(form, obj)
actionMap.put("delete", new DeleteAction(form, obj);
actionMap.put("edit", new EditAction(form, obj);

ControllerAction action = (ControllerAction) actionMap.get(request.getParameter("method"));
action.service(request, response);

EditAction.java

public class EditAction implements ControllerAction {
  private Form form;
  private Object obj;

public EditAction(Form form, Object obj) {
    this.form = form;
    this.obj = obj;
}

public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    obj = form.edit(request);

    request.setAttribute("obj", obj);
    request.setAttribute("form", form);

    if (form.isSucces()) {
        RequestDispatcher view = request.getRequestDispatcher(success page);
        view.forward(request, response);
    }
    else {
        RequestDispatcher view = request.getRequestDispatcher(failure page);
        view.forward(request, response);
    }
}
}

实际的业务逻辑位于传递给通用操作的form 对象中。
通用操作允许我快速获得任何新对象的 CRUD 控制器功能。我只需要编写业务逻辑form,比如这里

CommitmentItemForm.java

public Object edit(HttpServletRequest request) {
    CommitmentItem commitmentItem = null;
    STKUser authenticatedUser = (STKUser) request.getSession().getAttribute("STKUserSession");
    String ownedByBadge = null;
    List deptSupervisorList = null;

    try {
        deptSupervisorList = STKUserDAO.getList(authenticatedUser.getDepartment()); //<--- Static call is it OK??
        commitmentItem = CommitmentListDAO.retreive(request.getParameter("commitment_id"), authenticatedUser);
        ownedByBadge = commitmentItem.getOwned_by();
    }
    catch (DAOException e) {
        setError(FORM_RESULTS, e.getMessage());
    }
    catch (ValidatorException e) {
        // ValidatorExceptions are thrown when the DAO can not find a record
        setError(FORM_RESULTS, e.getMessage());
        LOGGER.log(Level.INFO, e.getMessage(), authenticatedUser);
    }

    if (ownedByBadge != null) {
        if (ownedByBadge.equals(authenticatedUser.getBadge()) || ownedByBadge.equals(authenticatedUser.getAtaBadge())) {
        }
        else {
            setError(FORM_RESULTS, "You are not authorized to edit this data.");
            LOGGER.log(Level.INFO, "Error - You are not authorized to edit this data '" + commitmentItem.getCommitment_id() + "'", authenticatedUser);
        }
    }
    request.setAttribute("deptSupervisorList", deptSupervisorList);  // <--- Is this acceptable to do???
    return commitmentItem;
}

1) 是我设置请求属性并以非正统方法返回对象的方法吗?
2) 我正在进行静态调用以获取 deptSupervisorList。这是自找麻烦吗??
3) 在不使用框架的情况下,我的 servlet、通用操作、业务表单似乎是一种可以接受的开发 Java Web 应用程序的方法吗?

编辑: 有什么区别?

Static
deptSupervisorList = STKUserDAO.getList(authenticatedUser.getDepartment()); 

non-static
STKUserDAO userDAO = new STKUserDAO();
deptSupervisorList = userDAO.getList(authenticatedUser.getDepartment()); 


public static List getList(String dept) throws DAOException {
   ...
}

【问题讨论】:

  • 个人意见,我认为你不需要为你的Web应用程序需求实现一个框架,已经有很好的测试框架可用。同样,只是个人意见。

标签: java model-view-controller web-applications


【解决方案1】:

首先要注意几点:

  • 这是主观的
  • 我同意 SidCool 的观点,即查看一些现有的 Web 应用程序框架。如果有的话,只是想了解他们是如何做到的。
  • 我是依赖注入的忠实拥护者

回答您的问题:

  • 在请求属性中传递数据并不是很好,因为:它不是类型安全的;它有点像一个看不见的东西——如果你能在类型签名中看到输出对象总是更好的;在某些时候,您会发现自己想在请求属性中以相同的名称存储两件事
  • 依赖注入是未来的发展方向。进行静态调用很糟糕,因为:您现在将两个对象紧密耦合在一起,使得重用和测试变得更加困难
  • 我肯定会在这里看看其他一些框架。他们中的大多数倾向于有一个单独的调度 servlet,我认为您最终会编写 很多 看起来非常相似的 servlet。很多框架也会使用反射来尝试尽可能早、尽可能容易地完成 request 和 POJO 之间的转换。

其他:

  • 您的所有操作都是关闭参数,即 ?method=[list,view,delete,edit]。通常最好使用路由(例如 index.html 通常用于“列表”)。

回答来自 cmets 的反馈/问题:

在旧版本的 Java 上运行

哇,太糟糕了。不过,有些框架可以在 Java 1.4 上运行。 Spring MVC 将是我的推荐,但还有更多 here。也就是说,我建议查看其他框架的原因不仅仅是为了使用它们,而是为了从中获得灵感。编写您自己的 Web 应用程序框架实际上是一种成人礼,而且非常有趣。在如此受限的环境中编写它只会增加挑战。

我的建议:

  • 尝试最近的 Java 框架,甚至是非 Java 框架(例如 Ruby on Rails),看看有什么可能
  • 在编写您自己的唯一框架时,只需使用 1 个 servlet 并将其分派到您的各种“控制器”。这样做的原因是 Servlet 不擅长让您将整个应用程序放在一起(Spring MVC 所做的是使用 ContextListener 加载“应用程序”,然后 servlet 和过滤器从 ServletContext 中查找“应用程序”)

静态的紧耦合

紧密耦合是指两个对象在没有彼此的情况下永远无法使用。你问为什么这样不好?因为您永远不能将代码重用于其他事情(例如,如果您决定从文件中加载一些数据,引入缓存层,在不同的项目中使用它等)。最重要的是,有人会说,它很难测试。这是因为您不能仅将静态调用的对象替换为另一个对象。接口通常用于解耦对象,但实际上,您可以通过依赖注入设置对象来做到这一点(这是一种复杂的说法:将其放入构造函数或作为 setter)。

OO 和土木工程师

一切都好。我认识的一些最优秀的程序员并不是这样开始的。对我来说,默认情况下使用依赖注入模式是编写“好”代码的绝佳方式。注意:如果您查看依赖注入,则不需要它的框架。您只需要在一个地方构造所有对象,并且所有对象都应该在构造函数或 setter 中获取它们的所有依赖项。不允许使用静态方法或单例。

有什么区别

另一种“有什么区别”可以更好地说明我的意思,如下所示:

// code in your application builder
// assuming an interface called UserDAO
UserDAO userDAO = new STKUserDAO();
CommitmentItemForm form = new CommitmentItemForm(userDao);


public class CommitmentItemForm {

  private final UserDAO userDao;
  public CommitmentItemForm(UserDAO userDao) { this.userDao = userDao; }

  public Object edit(HttpServletRequest request) {
    ...
    deptSupervisorList = userDao.getList(authenticatedUser.getDepartment());
    ...
  }

}

public class CommitmentItemForm {

  public CommitmentItemForm()

  public Object edit(HttpServletRequest request) {
    ...
    deptSupervisorList = STKUserDAO.getList(authenticatedUser.getDepartment());
    ...
  }

}

静态方法肯定看起来工作量少,那为什么这么糟糕呢?本质上,这是因为在静态版本中,除了 STKUserDAO 之外,您永远无法从任何地方查找 deptSupervisorList。在非静态版本中,您可以提供 UserDAO 接口的 any 实现。这意味着您可以使用相同的 CommitmentItemForm 代码,无论是否:

  • 您在测试中执行此操作,并且您正在创建一个 UserDAO 的 Mock 版本,该版本每次都返回一个异常,以便您可以对其进行测试
  • 您发现需要从 JSON HTTP REST Web 服务或文件中检索部门列表

从 CommitmentItemForm 的签名中也很明显,它需要一个 UserDAO 才能运行(因为它在构造函数中是必需的)。

这是其中一件小事将来最好隔离。

【讨论】:

  • 我在一家大公司工作,内部网站/webapps 是高层管理人员最不关心的问题。我被困在使用 Java 1.4、JSP 1.2 Servlets 2.3 中,我已经要求升级 3 年了。直到最近,我什至没有自己的网络应用程序文件夹。我只能运行 JSP。然后我请求 IT 在 server.xml 中添加一个条目。所以我想我可以安装一个框架,如果它可以在我们的 Outdate Sun Java Web Server 6.1 SP9 上运行,因为现在我有一个 web.xml 和一个 lib 文件夹来放置 jar。这就是我开发自己的框架的原因。
  • 我不确定我明白你的意思,因为静态调用将两个对象结合在一起。 DAO 中的静态方法执行数据库查询。在我看来,静态实现它更容易,因为我不需要对象(好吧,我想我不需要)我只想要一个主管徽章的 Arraylist。顺便说一句,我是一名土木工程师,拥有 1 门 Java 课程。我仍然对 OO 概念感到有些困惑。
  • 感谢您抽出宝贵时间回复。我想我对静态调用仍然有些困惑。我在上面编辑了我的问题。通过使用静态方法,我只需要一行代码即可获取主管 ID 号的数组列表。我可以轻松地在其他地方重用这个方法调用,那么它是如何耦合的?
  • @jeff 我明天会写更多,但简短的回答是重用不是静态方法,重用是调用静态方法的对象,即 CommitmentItemForm 永远不会能够从 STKUserDAO 以外的任何地方获取数组列表。注意:需要在您的“有什么区别”中添加更多内容,但当我有更多时间时,我会在答案中添加。
  • 谢谢!我开始看到光明。现在 CommitmentItemForm 的签名与您对 UserDAO 所做的完全一样,但对 CommitmentItemDAO。我将重构我的代码以添加 UserDAO 并将 getList 方法更改为非静态方法。目前我没有 UserDAO 的接口。您为您的回复付出了很多努力,我非常感谢。
猜你喜欢
  • 2012-09-30
  • 1970-01-01
  • 2022-01-24
  • 1970-01-01
  • 1970-01-01
  • 2015-10-08
  • 2016-12-12
  • 1970-01-01
  • 2021-04-05
相关资源
最近更新 更多