Các mẫu thiết kế ứng dụng dựa trên web [đã đóng]


359

Tôi đang thiết kế một ứng dụng dựa trên web đơn giản. Tôi chưa quen với miền dựa trên web này. Tôi cần lời khuyên của bạn về các mẫu thiết kế như cách phân phối trách nhiệm giữa các Servlet, tiêu chí để tạo Servlet mới, v.v.

Trên thực tế, tôi có một vài thực thể trên trang chủ của mình và tương ứng với mỗi một trong số chúng, chúng tôi có một vài tùy chọn như thêm, chỉnh sửa và xóa. Trước đây tôi đã sử dụng một Servlet cho mỗi tùy chọn như Servlet1 để thêm thực thể1, Servlet2 để chỉnh sửa thực thể1, v.v. và theo cách này, cuối cùng chúng tôi đã có một số lượng lớn các máy chủ.

Bây giờ chúng tôi đang thay đổi thiết kế của chúng tôi. Câu hỏi của tôi là làm thế nào bạn chọn chính xác cách bạn chọn trách nhiệm của một servlet. Chúng ta nên có một Servlet cho mỗi thực thể sẽ xử lý tất cả các tùy chọn của nó và chuyển tiếp yêu cầu đến lớp dịch vụ. Hoặc chúng ta nên có một servlet cho toàn bộ trang sẽ xử lý toàn bộ yêu cầu của trang và sau đó chuyển tiếp nó đến lớp dịch vụ tương ứng? Ngoài ra, đối tượng yêu cầu có nên chuyển tiếp đến lớp dịch vụ hay không.


8
Không thực sự là mẫu thiết kế chính thức, nhưng đừng quên PRG (post-redirect-get) và Hijax (làm việc không có js trước, sau đó chiếm quyền điều khiển các liên kết và nút bằng ajax)
Neil McGuigan

Câu trả lời:


488

Một ứng dụng web phong nha bao gồm một hỗn hợp các mẫu thiết kế. Tôi sẽ chỉ đề cập đến những người quan trọng nhất.


Mẫu điều khiển xem mô hình

Mẫu thiết kế (kiến trúc) cốt lõi mà bạn muốn sử dụng là mẫu Model-View-Controller . Bộ điều khiển sẽ được đại diện bởi một Servlet mà (trực tiếp) tạo / sử dụng một Mô hìnhChế độ xem cụ thể dựa trên yêu cầu. Các mẫu phải được đại diện bởi các lớp JavaBean. Điều này thường được chia thêm trong Mô hình nghiệp vụ có chứa các hành động (hành vi) và Mô hình dữ liệu chứa dữ liệu (thông tin). Các Xem là để được đại diện bởi các file JSP mà có thể truy cập trực tiếp đến ( dữ liệu ) Mẫu bởi EL (Expression Language).

Sau đó, có các biến thể dựa trên cách xử lý các hành động và sự kiện. Những cái phổ biến là:

  • Yêu cầu (hành động) dựa trên MVC : đây là cách đơn giản nhất để thực hiện. Mô hình ( Kinh doanh ) làm việc trực tiếp với và các đối tượng. Bạn phải tự mình thu thập, chuyển đổi và xác thực các tham số yêu cầu (chủ yếu). Các Xem thể được đại diện bởi vani đồng bằng HTML / CSS / JS và nó không duy trì trạng thái trên yêu cầu. Đây là cách mà những người khác Spring MVC , StrutsStripes hoạt động.HttpServletRequestHttpServletResponse

  • Thành phần dựa trên MVC : điều này khó thực hiện hơn. Nhưng bạn kết thúc với một mô hình đơn giản hơn và xem trong đó tất cả API Servlet "thô" được trừu tượng hóa hoàn toàn. Bạn không cần phải thu thập, chuyển đổi và xác thực các tham số yêu cầu. Bộ điều khiển thực hiện nhiệm vụ này và đặt các tham số yêu cầu được thu thập, chuyển đổi và xác nhận trong Mô hình . Tất cả những gì bạn cần làm là xác định các phương thức hành động hoạt động trực tiếp với các thuộc tính mô hình. Các Xem được đại diện bởi "linh kiện" trong hương vị của taglibs JSP hoặc các yếu tố XML mà lần lượt tạo ra HTML / CSS / JS. Trạng thái của Chế độ xemcho các yêu cầu tiếp theo được duy trì trong phiên. Điều này đặc biệt hữu ích cho các sự kiện chuyển đổi, xác nhận và thay đổi giá trị phía máy chủ. Đây là cách mà những người khác JSF , WicketPlay! làm.

Như một lưu ý phụ, sở thích xung quanh với khung MVC tại nhà là một bài tập học tập rất hay và tôi khuyên bạn nên sử dụng nó miễn là bạn giữ nó cho mục đích cá nhân / riêng tư. Nhưng một khi bạn trở nên chuyên nghiệp, thì chúng tôi khuyên bạn nên chọn một khung hiện có thay vì phát minh lại khung của riêng bạn. Học một khung hiện có và được phát triển tốt sẽ mất ít thời gian hơn so với việc tự mình phát triển và duy trì một khung mạnh mẽ.

Trong phần giải thích chi tiết dưới đây, tôi sẽ hạn chế yêu cầu MVC dựa trên vì điều đó dễ thực hiện hơn.


Mẫu điều khiển trước ( Mẫu hòa giải )

Đầu tiên, phần Bộ điều khiển phải triển khai mẫu Bộ điều khiển phía trước (là một loại mẫu của Người hòa giải chuyên biệt ). Nó chỉ bao gồm một servlet duy nhất cung cấp một điểm nhập tập trung của tất cả các yêu cầu. Nó sẽ tạo Mô hình dựa trên thông tin có sẵn theo yêu cầu, chẳng hạn như pathinfo hoặc servletpath, phương thức và / hoặc các tham số cụ thể. Các mô hình kinh doanh được gọi là Actionở dưới đây HttpServletchẳng hạn.

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    try {
        Action action = ActionFactory.getAction(request);
        String view = action.execute(request, response);

        if (view.equals(request.getPathInfo().substring(1)) {
            request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response);
        }
        else {
            response.sendRedirect(view); // We'd like to fire redirect in case of a view change as result of the action (PRG pattern).
        }
    }
    catch (Exception e) {
        throw new ServletException("Executing action failed.", e);
    }
}

Thực hiện hành động sẽ trả về một số định danh để xác định vị trí xem. Đơn giản nhất là sử dụng nó làm tên tệp của JSP. Ánh xạ servlet này trên một cụ thể url-patterntrong web.xml, ví dụ /pages/*, *.dohoặc thậm chí chỉ *.html.

Trong trường hợp mẫu tiền tố như ví dụ, /pages/*bạn có thể gọi URL như http://example.com/pages/register , http://example.com/pages/login , v.v. và cung cấp /WEB-INF/register.jsp, /WEB-INF/login.jspvới các hành động GET và POST thích hợp . Các phần register, loginvv sau đó có sẵn request.getPathInfo()như trong ví dụ trên.

Khi bạn đang sử dụng các mẫu hậu tố như *.do, *.htmlv.v., thì bạn có thể gọi URL như http://example.com/register.do , http://example.com/login.do , v.v. và bạn nên thay đổi ví dụ mã trong câu trả lời này (cũng là ActionFactory) để trích xuất registerlogincác bộ phận bằng cách request.getServletPath()thay thế.


Mô hình chiến lược

Các Actionnên làm theo các mô hình chiến lược . Nó cần được định nghĩa là một loại trừu tượng / giao diện, sẽ thực hiện công việc dựa trên các đối số được truyền vào của phương thức trừu tượng (đây là sự khác biệt với mẫu Lệnh , trong đó loại trừu tượng / giao diện sẽ thực hiện công việc dựa trên các đối số được thông qua trong quá trình tạo ra việc thực hiện).

public interface Action {
    public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

Bạn có thể muốn làm cho Exceptioncụ thể hơn với một ngoại lệ tùy chỉnh như ActionException. Đó chỉ là một ví dụ khởi động cơ bản, phần còn lại tùy thuộc vào bạn.

Đây là một ví dụ về một LoginActioncái mà (như tên của nó nói) đăng nhập vào người dùng. Chính Usernó là một Mô hình Dữ liệu . The View nhận thức được sự hiện diện của User.

public class LoginAction implements Action {

    public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        User user = userDAO.find(username, password);

        if (user != null) {
            request.getSession().setAttribute("user", user); // Login user.
            return "home"; // Redirect to home page.
        }
        else {
            request.setAttribute("error", "Unknown username/password. Please retry."); // Store error message in request scope.
            return "login"; // Go back to redisplay login form with error.
        }
    }

}

Mô hình phương pháp nhà máy

Các ActionFactorynên làm theo các phương pháp mô hình Nhà máy . Về cơ bản, nó sẽ cung cấp một phương thức sáng tạo trả về một triển khai cụ thể của kiểu trừu tượng / giao diện. Trong trường hợp này, nó sẽ trả về việc thực hiện Actiongiao diện dựa trên thông tin được cung cấp bởi yêu cầu. Ví dụ: phương thứcpathinfo (pathinfo là phần nằm sau đường dẫn ngữ cảnh và servlet trong URL yêu cầu, không bao gồm chuỗi truy vấn).

public static Action getAction(HttpServletRequest request) {
    return actions.get(request.getMethod() + request.getPathInfo());
}

Các actionslần lượt nên có một số tĩnh / applicationwide Map<String, Action>nắm giữ tất cả các hành động được biết đến. Tùy thuộc vào bạn làm thế nào để điền vào bản đồ này. Mã hóa cứng:

actions.put("POST/register", new RegisterAction());
actions.put("POST/login", new LoginAction());
actions.put("GET/logout", new LogoutAction());
// ...

Hoặc có thể định cấu hình dựa trên tệp cấu hình thuộc tính / XML trong đường dẫn lớp: (giả)

for (Entry entry : configuration) {
    actions.put(entry.getKey(), Class.forName(entry.getValue()).newInstance());
}

Hoặc tự động dựa trên quá trình quét trong đường dẫn lớp cho các lớp thực hiện một giao diện và / hoặc chú thích nhất định: (giả)

for (ClassFile classFile : classpath) {
    if (classFile.isInstanceOf(Action.class)) {
       actions.put(classFile.getAnnotation("mapping"), classFile.newInstance());
    }
}

Hãy ghi nhớ để tạo "không làm gì" Actioncho trường hợp không có ánh xạ. Let it ví dụ trở lại trực tiếp request.getPathInfo().substring(1)sau đó.


Các mẫu khác

Đó là những mẫu quan trọng cho đến nay.

Để tiến thêm một bước, bạn có thể sử dụng mẫu Mặt tiền để tạo một Contextlớp lần lượt bao bọc các đối tượng yêu cầu và phản hồi và đưa ra một số phương thức tiện lợi ủy thác cho các đối tượng yêu cầu và phản hồi và chuyển đối số đó thành đối số vào Action#execute()phương thức. Điều này thêm một lớp trừu tượng bổ sung để ẩn API Servlet thô đi. Về cơ bản, bạn nên kết thúc với khai báo bằng không import javax.servlet.* trong mỗi lần Actionthực hiện. Theo thuật ngữ của JSF, đây là những gì FacesContextExternalContextcác lớp đang làm. Bạn có thể tìm thấy một ví dụ cụ thể trong câu trả lời này .

Sau đó, có mẫu Trạng thái cho trường hợp bạn muốn thêm một lớp trừu tượng bổ sung để phân chia các nhiệm vụ thu thập các tham số yêu cầu, chuyển đổi chúng, xác thực chúng, cập nhật giá trị mô hình và thực hiện các hành động. Theo thuật ngữ của JSF, đây là những gì LifeCycleđang làm.

Sau đó, có mẫu Tổng hợp cho trường hợp bạn muốn tạo chế độ xem dựa trên thành phần có thể được đính kèm với mô hình và hành vi của chúng phụ thuộc vào trạng thái của vòng đời dựa trên yêu cầu. Theo thuật ngữ của JSF, đây là những gì UIComponentđại diện.

Bằng cách này, bạn có thể phát triển từng chút một theo khung dựa trên thành phần.


Xem thêm:


4
@masato: Bạn có thể làm điều này trong ví dụ khối khởi tạo tĩnh.
BalusC

1
@masato: nhân tiện, nếu bạn muốn lấy chúng từ web.xmlđó, thì bạn có thể sử dụng một ServletContextListenercái này. Có nhà máy thực hiện nó (và đăng ký như <listener>trong web.xml) và thực hiện công việc điền trong contextInitialized()phương pháp.
BalusC

3
Thay vào đó hãy thực hiện công việc mà "post_servlet" sẽ thực hiện trong hành động. Bạn không nên có nhiều hơn một servlet. Công cụ kinh doanh nên được thực hiện trong các lớp hành động. Nếu bạn muốn nó là một yêu cầu mới, thì hãy quay lại một chế độ xem khác sẽ gây ra chuyển hướng và thực hiện công việc trong hành động mới liên quan đến yêu cầu GET.
BalusC

2
Phụ thuộc. Dễ nhất là chỉ cần thực hiện ngay trong cách Actiontriển khai giống như với các máy chủ thông thường (xem thêm wiki máy chủ để biết ví dụ cơ bản, bạn có thể tự do cấu trúc lại vào một số Validatorgiao diện). Nhưng bạn cũng có thể làm điều đó trước khi gọi hành động, nhưng điều này phức tạp hơn vì nó yêu cầu các quy tắc xác thực được biết đến trên cơ sở mỗi lượt xem. JSF đã bao phủ này bằng cách cung cấp required="true", validator="customValidatorName", vv trong đánh dấu XHTML.
BalusC

2
@AndreyBotalov: kiểm tra mã nguồn của các khung MVC như JSF, Spring MVC, Wicket, Struts2, v.v ... Chúng đều là nguồn mở.
BalusC

13

Trong mẫu MVC được đánh bại, Servlet là "C" - bộ điều khiển.

Công việc chính của nó là thực hiện đánh giá yêu cầu ban đầu và sau đó gửi xử lý dựa trên đánh giá ban đầu cho công nhân cụ thể. Một trong những trách nhiệm của công nhân có thể là thiết lập một số bean lớp trình bày và chuyển yêu cầu đến trang JSP để hiển thị HTML. Vì vậy, vì lý do này một mình, bạn cần chuyển đối tượng yêu cầu đến lớp dịch vụ.

Tôi sẽ không, mặc dù, bắt đầu viết Servletcác lớp thô . Công việc họ làm là rất dễ đoán và sôi nổi, một cái gì đó mà khung làm rất tốt. May mắn thay, có rất nhiều ứng cử viên đã được kiểm tra thời gian (theo thứ tự bảng chữ cái): Apache Wicket , Java Server Faces , Spring để kể tên một số.


5

IMHO, không có nhiều khác biệt trong trường hợp ứng dụng web nếu bạn nhìn nó từ góc độ phân công trách nhiệm. Tuy nhiên, giữ sự rõ ràng trong lớp. Giữ bất cứ thứ gì hoàn toàn cho mục đích trình bày trong lớp trình bày, như điều khiển và mã cụ thể cho các điều khiển web. Chỉ cần giữ các thực thể của bạn trong lớp nghiệp vụ và tất cả các tính năng (như thêm, chỉnh sửa, xóa), v.v. trong lớp nghiệp vụ. Tuy nhiên, hiển thị chúng lên trình duyệt sẽ được xử lý trong lớp trình bày. Đối với .Net, mẫu ASP.NET MVC rất tốt trong việc giữ các lớp tách biệt. Nhìn vào mô hình MVC.


bạn có thể đi một chút rõ ràng trong những gì nên đi trong servlet?
mawia

Servlet phải là bộ điều khiển nếu bạn sử dụng MVC.
Kangkan

3

Tôi đã sử dụng khung struts và thấy nó khá dễ học. Khi sử dụng khung thanh chống, mỗi trang trên trang web của bạn sẽ có các mục sau.

1) Một hành động được sử dụng được gọi mỗi khi trang HTML được làm mới. Hành động sẽ điền dữ liệu vào biểu mẫu khi trang được tải lần đầu tiên và xử lý các tương tác giữa giao diện người dùng web và lớp doanh nghiệp. Nếu bạn đang sử dụng trang jsp để sửa đổi một đối tượng java có thể thay đổi, một bản sao của đối tượng java sẽ được lưu trữ ở dạng chứ không phải bản gốc để dữ liệu gốc không bị sửa đổi trừ khi người dùng lưu trang.

2) Biểu mẫu được sử dụng để truyền dữ liệu giữa hành động và trang jsp. Đối tượng này phải bao gồm một tập hợp getter và setters cho các thuộc tính cần truy cập vào tệp jsp. Biểu mẫu cũng có một phương thức để xác thực dữ liệu trước khi nó được duy trì.

3) Một trang jsp được sử dụng để hiển thị HTML cuối cùng của trang. Trang jsp là sự kết hợp của HTML và các thẻ struts đặc biệt được sử dụng để truy cập và thao tác dữ liệu trong biểu mẫu. Mặc dù struts cho phép người dùng chèn mã Java vào các tệp jsp, bạn nên rất thận trọng khi làm điều đó vì nó làm cho mã của bạn khó đọc hơn. Mã Java bên trong các tệp jsp rất khó gỡ lỗi và không thể được kiểm tra đơn vị. Nếu bạn thấy mình viết hơn 4-5 dòng mã java trong tệp jsp, mã có thể sẽ được chuyển sang hành động.


Lưu ý: Trong struts 2, đối tượng Form được gọi là Model thay vào đó nhưng hoạt động giống như tôi đã mô tả trong câu trả lời ban đầu của mình.
EsotericNonsense

3

Câu trả lời tuyệt vời của BalusC bao gồm hầu hết các mẫu cho các ứng dụng web.

Một số ứng dụng có thể yêu cầu Chain-of-trách nhiệm_ mô hình

Trong thiết kế hướng đối tượng, mẫu trách nhiệm chuỗi là mẫu thiết kế bao gồm nguồn đối tượng lệnh và một loạt các đối tượng xử lý. Mỗi đối tượng xử lý chứa logic xác định các loại đối tượng lệnh mà nó có thể xử lý; phần còn lại được chuyển đến đối tượng xử lý tiếp theo trong chuỗi.

Ca sử dụng để sử dụng mẫu này:

Khi xử lý để xử lý một yêu cầu (lệnh) không xác định và yêu cầu này có thể được gửi đến nhiều đối tượng. Nói chung, bạn đặt kế cho đối tượng. Nếu đối tượng hiện tại không thể xử lý yêu cầu hoặc xử lý yêu cầu một phần và chuyển tiếp cùng yêu cầu đến đối tượng kế nhiệm .

Câu hỏi / bài viết hữu ích về SE:

Tại sao tôi lại sử dụng Chuỗi trách nhiệm đối với Người trang trí?

Tập quán chung cho chuỗi trách nhiệm?

mô hình chuỗi trách nhiệm từ oodesign

chain_of_responsibility từ sourcemaking

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.