디자인 패턴 웹 기반 애플리케이션 [닫기]


359

간단한 웹 기반 응용 프로그램을 디자인하고 있습니다. 이 웹 기반 도메인을 처음 사용합니다. 서블릿간에 책임을 분산시키는 방법, 새 서블릿을 만드는 기준 등과 같은 디자인 패턴에 대한 조언이 필요했습니다.

실제로 내 홈페이지에는 엔티티가 거의 없으며 각 엔티티에 추가, 편집 및 삭제와 같은 옵션이 거의 없습니다. 이전에는 Entitylet을 추가하기 위해 Servlet1, entity1을 편집하기 위해 Servlet2와 같은 옵션 당 하나의 Servlet을 사용했습니다.

이제 우리는 디자인을 바꾸고 있습니다. 내 질문은 서블릿의 책임을 어떻게 선택하는지 정확하게 선택하는 것입니다. 엔티티마다 하나의 서블릿이 있어야 모든 옵션을 처리하고 요청을 서비스 계층으로 전달합니다. 또는 전체 페이지 요청을 처리 한 다음 해당 서비스 계층으로 전달할 전체 페이지에 대해 하나의 서블릿이 있어야합니까? 또한 요청 오브젝트가 서비스 계층으로 전달되는지 여부


8
공식적인 디자인 패턴은 아니지만 PRG (post-redirect-get)와 Hijax를 잊지 마십시오 (먼저 js로 작업 한 다음 ajax로 링크와 버튼을 가로 채십시오)
Neil McGuigan

답변:


488

알맞은 웹 응용 프로그램은 여러 가지 디자인 패턴으로 구성됩니다. 가장 중요한 것만 언급하겠습니다.


모델 뷰 컨트롤러 패턴

사용하려는 핵심 (아키텍처) 디자인 패턴은 Model-View-Controller pattern 입니다. 컨트롤러 (투입)을 직접 생성하는 서블릿에 의해 표현 될 / 특정 사용 모델보기 요청에 기반. 모델은 자바 빈즈 클래스에 의해 표현 될 것입니다. 이것은 종종 행동 (행동)을 포함하는 비즈니스 모델 과 데이터 (정보)를 포함하는 데이터 모델로 더 나눌 수 있습니다. 보기 제 (에 직접 액세스 할 수있는 JSP 파일로 표현되어야하는 데이터 ) 모델 EL (표현 언어)로합니다.

그런 다음 작업 및 이벤트 처리 방법에 따라 변형이 있습니다. 인기있는 것은 :

  • 요청 (액션) 기반 MVC : 구현이 가장 간단합니다. ( 비즈니스 ) 모델 로 직접 작동 HttpServletRequestHttpServletResponse객체. 요청 매개 변수 (대부분)를 직접 수집, 변환 및 검증해야합니다. 보기는 요청에서 일반 바닐라 HTML / CSS / JS하고 유지하지 않는 국가에 의해 표현 될 수있다. 이것이 Spring MVC , Struts and Stripes의 작동 방식입니다.

  • 컴포넌트 기반 MVC : 구현하기가 어렵습니다. 그러나 모든 "원시"서블릿 API가 완전히 추상화 된 더 간단한 모델과보기로 끝납니다. 요청 매개 변수를 직접 수집, 변환 및 유효성 검증 할 필요가 없습니다. 컨트롤러는 이 작업과 설정합니다의 수집, 변환 및 검증 요청 매개 변수 않는 모델 . 모델 속성과 직접 작동하는 동작 방법을 정의하기 만하면됩니다. 보기는 JSP의 태그 라이브러리 또는 차례로 / JS를 HTML / CSS를 생성하는 XML 요소의 맛에서 "구성 요소"로 표시됩니다. 의 상태후속 요청은 세션에서 유지 보수됩니다. 이는 서버 측 변환, 유효성 검사 및 값 변경 이벤트에 특히 유용합니다. 이것은 JSF , WicketPlay 중 다른 방법입니다 ! 공장.

참고로, 자체 개발 한 MVC 프레임 워크를 사용하여 취미 생활을하는 것은 매우 훌륭한 학습 연습이며 개인 / 개인 목적으로 유지하는 한 권장합니다. 그러나 전문가가되면 자신의 것을 재창조하기보다는 기존 프레임 워크를 선택하는 것이 좋습니다. 기존의 잘 개발 된 프레임 워크를 학습하면 강력한 프레임 워크를 직접 개발하고 유지 관리하는 것보다 시간이 덜 걸립니다.

아래의 자세한 설명에서는 구현하기가 쉽기 때문에 MVC를 요청하도록 제한합니다.


전면 컨트롤러 패턴 ( 중개자 패턴 )

먼저 컨트롤러 부분은 전면 컨트롤러 패턴 (특수한 종류의 중재자 패턴 )을 구현해야합니다 . 모든 요청의 중앙 집중식 진입 점을 제공하는 단일 서블릿으로 만 구성되어야합니다. 요청에 의해 제공되는 정보 (예 : 경로 정보 또는 서블릿 경로, 방법 및 / 또는 특정 매개 변수)를 기반으로 모델을 작성해야합니다 . 비즈니스 모델이 라고 Action아래에 HttpServlet예.

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);
    }
}

작업을 실행하면 일부 식별자가 반환되어 뷰를 찾습니다. JSP의 파일 이름으로 사용하는 것이 가장 간단합니다. 특정에이 서블릿을지도 url-pattern에서 web.xml예를 들면 /pages/*, *.do또는 단지 *.html.

예를 들어 접두사 패턴의 경우 http://example.com/pages/register/pages/* 와 같은 URL을 호출 할 수 있습니다 , http://example.com/pages/login 등 및 제공 /WEB-INF/register.jsp, /WEB-INF/login.jsp적절한 GET 및 POST 작업으로 . 부품 register, login등에 의해 후 가능한 request.getPathInfo()상기 예에서와 같이.

사용중인 경우 접미사 패턴이 좋아 *.do, *.html등, 다음 수 다음 URL의 같은 호출 http://example.com/register.do , http://example.com/login.do 등하고 변경해야 이 답변의 코드 예제 ( 및 ActionFactory)를 대신 하여 registerlogin부분 을 추출하십시오 request.getServletPath().


전략 패턴

Action추적해야 전략 패턴을 . 전달 된 것을 기반으로 작업을 수행해야하는 추상 / 인터페이스 유형으로 정의해야합니다 .추상 메소드 인수를 명령 패턴 과의 차이점입니다) . 구현을 생성 하는 동안 전달되는 인수 ).

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

Exception과 같은 맞춤 예외 를 사용하여 더 구체적으로 만들 수 있습니다 ActionException. 기본 킥오프 예일 뿐이며 나머지는 모두 귀하에게 달려 있습니다.

다음 LoginAction은 이름으로 사용자가 로그인 하는 예입니다 . 그 User자체가 데이터 모델 입니다. 보기 의 존재를 알고있다 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.
        }
    }

}

팩토리 메소드 패턴

ActionFactory추적해야 하는 팩토리 메소드 패턴을 . 기본적으로 추상 / 인터페이스 유형의 구체적인 구현을 반환하는 생성 방법을 제공해야합니다. 이 경우 Action요청에서 제공 한 정보를 기반으로 인터페이스 구현을 반환해야합니다 . 예를 들어, 방법의 PathInfo 합니다 (의 PathInfo 쿼리 스트링 제외 요청 URL의 컨텍스트 및 서블릿 경로 후의 부분이다).

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

그만큼 actions차례에 약간의 정적 / applicationwide해야 Map<String, Action>알려진 모든 조치를 보유하고 있습니다. 이지도를 작성하는 방법은 귀하에게 달려 있습니다. 하드 코딩 :

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

또는 클래스 경로의 속성 / XML 구성 파일을 기반으로 구성 가능 : (의사)

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

또는 특정 인터페이스 및 / 또는 주석을 구현하는 클래스에 대한 클래스 경로의 스캔을 동적으로 기반으로합니다 (의사)

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

"아무것도하지 말 것"을 명심하십시오 Action맵핑이없는 경우 을 작성하십시오. 예를 들어 request.getPathInfo().substring(1)그때 직접 반환하자 .


다른 패턴

그것들은 지금까지 중요한 패턴이었습니다.

한 단계 더 나아가 려면 Facade 패턴 을 사용하여 Context클래스 를 작성 하여 요청 및 응답 오브젝트를 랩핑하고 요청 및 응답 오브젝트에 위임하는 몇 가지 편리한 메소드를 제공하고 Action#execute()대신 메소드에 인수로 전달하십시오 . 이는 원시 서블릿 API를 숨기도록 추가 추상 계층을 추가합니다. 그런 다음 기본적으로 모든 구현 에서 0 import javax.servlet.* 선언 으로 끝나야합니다 Action. JSF 용어로 이것이 클래스 FacesContextExternalContext클래스가하는 일입니다. 이 답변 에서 구체적인 예를 찾을 수 있습니다 .

그런 다음 요청 매개 변수 수집, 변환, 유효성 검증, 모델 값 업데이트 및 조치 실행 태스크를 분할하기 위해 추가 추상화 계층을 추가하려는 경우 의 상태 패턴 이 있습니다. JSF 용어로, 이것은LifeCycle 하는 일입니다.

그런 다음 모델에 첨부 할 수 있고 동작이 요청 기반 수명주기의 상태에 따라 달라지는 컴포넌트 기반보기를 작성하려는 경우에 대한 복합 패턴 이 있습니다. JSF 용어로, 이것은UIComponent 대표하는 것입니다.

이 방법으로 구성 요소 기반 프레임 워크를 향해 조금씩 진화 할 수 있습니다.


또한보십시오:


4
@masato : 예를 들어 정적 초기화 블록에서이 작업을 수행 할 수 있습니다.
BalusC

1
@masato : 그런데,에서 그들을 검색 web.xml하려면 이것을 ServletContextListener위해 사용할 수 있습니다 . 공장이 그것을 구현 (및 등록을 가지고 <listener>있는 web.xml)과 동안 충전 작업을 수행 contextInitialized()하는 방법.
BalusC

3
대신 "post_servlet"이 수행해야하는 작업을 수행하십시오. 하나 이상의 서블릿이 없어야합니다. 비즈니스는 액션 클래스에서 수행해야합니다. 새 요청이 되려면 다른보기로 돌아가서 리디렉션을 유발하고 GET 요청과 관련된 새 작업에서 작업을 수행하십시오.
BalusC

2
다릅니다. 가장 쉬운 Action방법은 일반적인 서블릿과 동일한 방식으로 구현에서 올바르게 수행하는 것입니다 ( 기본 예제 는 서블릿 위키 참조 Validator). 그러나 조치를 호출하기 전에이를 수행 할 수도 있지만보기마다 알려진 유효성 검증 규칙이 필요하므로 더 복잡합니다. JSF는 제공하여이 덮여있다 required="true", validator="customValidatorName"XHTML의 마크 업 등.
BalusC

2
@AndreyBotalov : JSF, Spring MVC, Wicket, Struts2 등과 같은 MVC 프레임 워크의 소스 코드를 확인하십시오. 모두 오픈 소스입니다.
BalusC

13

구타 MVC 패턴에서 서블릿은 "C"-컨트롤러입니다.

주요 작업은 초기 요청 평가를 수행 한 다음 초기 평가를 기반으로 처리를 특정 작업자에게 발송하는 것입니다. 작업자의 책임 중 하나는 프리젠 테이션 계층 Bean을 설정하고 HTML을 렌더링하기 위해 요청을 JSP 페이지에 전달하는 것입니다. 따라서 이런 이유로 요청 개체를 서비스 계층에 전달해야합니다.

그러나 나는 원시 Servlet클래스를 작성하기 시작하지 않을 것 입니다. 그들이하는 일은 매우 예측 가능하고 보일러 플레이트입니다. 다행스럽게도 Apache Wicket , Java Server Faces , Spring 등 몇 가지 예 를 들어 알파벳순으로 후보를 테스트 할 수 있습니다.


5

IMHO, 웹 응용 프로그램의 경우 책임 각도 지정에서 볼 때 큰 차이가 없습니다. 그러나 레이어의 선명도를 유지하십시오. 웹 컨트롤과 관련된 컨트롤 및 코드와 같이 프리젠 테이션 레이어에 프레젠테이션 용으로 만 사용하십시오. 비즈니스 계층에 엔터티를 유지하고 비즈니스 계층에 모든 기능 (추가, 편집, 삭제 등)을 유지하십시오. 그러나 프리젠 테이션 레이어에서 처리 할 수 ​​있도록 브라우저로 렌더링합니다. .Net의 경우 ASP.NET MVC 패턴은 레이어를 분리 된 상태로 유지하는면에서 매우 좋습니다. MVC 패턴을 살펴보십시오.


서블릿에 무엇이 들어가야하는지 조금 명시 적으로 나타낼 수 있습니까?
mawia

MVC를 사용하는 경우 서블릿이 컨트롤러 여야합니다.
Kangkan

3

스트럿 을 사용했습니다 프레임 워크를하고 매우 쉽게 배울 찾을 수 있습니다. struts 프레임 워크를 사용할 때 사이트의 각 페이지에는 다음 항목이 있습니다.

1) 사용되는 액션은 HTML 페이지를 새로 고칠 때마다 호출됩니다. 작업은 페이지가 처음로드 될 때 양식으로 데이터를 채우고 웹 UI와 비즈니스 계층 간의 상호 작용을 처리해야합니다. jsp 페이지를 사용하여 가변 Java 오브젝트를 수정하는 경우, 사용자가 페이지를 저장하지 않으면 원래 데이터가 수정되지 않도록 Java 오브젝트의 사본을 원본이 아닌 양식으로 저장해야합니다.

2) 조치와 jsp 페이지간에 데이터를 전송하는 데 사용되는 양식. 이 객체는 jsp 파일에 액세스 할 수 있어야하는 속성에 대한 getter 및 setter 세트로 구성되어야합니다. 이 양식에는 데이터가 유지되기 전에 데이터를 확인하는 방법도 있습니다.

3) 페이지의 최종 HTML을 렌더링하는 데 사용되는 jsp 페이지. jsp 페이지는 양식의 데이터에 액세스하고 조작하는 데 사용되는 HTML과 특수 스트럿 태그의 하이브리드입니다. struts를 사용하면 사용자가 Java 코드를 jsp 파일에 삽입 할 수 있지만 코드를 읽기가 더 어려워 지므로주의해야합니다. jsp 파일 내부의 Java 코드는 디버깅하기 어렵고 단위 테스트를 수행 할 수 없습니다. jsp 파일에 4-5 줄 이상의 Java 코드를 작성하는 경우 코드를 조치로 이동해야합니다.


참고 : Struts 2에서 Form 객체는 대신 Model이라고하지만 원래 답변에서 설명한 것과 같은 방식으로 작동합니다.
EsotericNonsense

3

BalusC 우수 답변은 웹 응용 프로그램의 패턴 대부분을 다룹니다.

일부 응용 프로그램에는 책임 체인 _ 패턴 이 필요할 수 있습니다.

객체 지향 디자인에서 책임 사슬 패턴은 명령 객체의 소스와 일련의 처리 객체로 구성된 디자인 패턴입니다. 각 처리 오브젝트에는 처리 할 수있는 명령 오브젝트 유형을 정의하는 논리가 포함됩니다. 나머지는 체인의 다음 처리 오브젝트로 전달됩니다.

이 패턴을 사용하는 유스 케이스 :

요청을 처리하기위한 핸들러 (명령)를 알 수없고이 요청을 여러 객체로 보낼 수 있습니다. 일반적으로 설정 후임자 를 객체로 합니다. 현재 객체가 요청을 처리 할 수 ​​없거나 요청을 부분적으로 처리하고 동일한 요청을 후속 객체에 전달 합니다.

유용한 SE 질문 / 문서 :

왜 데코레이터에 대한 책임 체인을 사용해야합니까?

책임 사슬에 대한 일반적인 사용법?

책임 사슬 패턴oodesign의

sourcemaking 의 chain_of_responsibility

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.