डिज़ाइन पैटर्न वेब आधारित अनुप्रयोग [बंद]


359

मैं एक साधारण वेब-आधारित एप्लिकेशन डिज़ाइन कर रहा हूं। मैं इस वेब-आधारित डोमेन के लिए नया हूं। मुझे डिज़ाइन पैटर्न के बारे में आपकी सलाह की आवश्यकता है जैसे कि सर्वलेट्स के बीच जिम्मेदारी कैसे वितरित की जानी चाहिए, नए सर्वलेट बनाने के लिए मापदंड आदि।

वास्तव में, मेरे पास मेरे होम पेज पर कुछ इकाइयाँ हैं और उनमें से प्रत्येक के अनुरूप हमारे पास कुछ विकल्प हैं जैसे ऐड, एडिट और डिलीट। इससे पहले मैं add1 के लिए Servlet1 जैसे एक सर्वलेट प्रति विकल्प 1 का उपयोग कर रहा था, फिर से Unit1 को संपादित करने के लिए Servlet2 और इतने पर और इस तरह हमने बड़ी संख्या में सर्वलेट समाप्त कर दिए।

अब हम अपना डिज़ाइन बदल रहे हैं। मेरा सवाल यह है कि आप सर्वलेट की जिम्मेदारी कैसे चुनते हैं। क्या हमें प्रति इकाई एक सर्वलेट होना चाहिए जो सेवा स्तर पर सभी विकल्पों और आगे के अनुरोध को संसाधित करेगा। या हमारे पास पूरे पेज के लिए एक सर्वलेट होना चाहिए जो पूरे पेज के अनुरोध को संसाधित करेगा और फिर उसे संबंधित सेवा परत पर अग्रेषित करेगा? इसके अलावा, अनुरोध वस्तु को सेवा स्तर पर भेजा जाना चाहिए या नहीं।


8
वास्तव में आधिकारिक डिज़ाइन पैटर्न नहीं, लेकिन PRG (पोस्ट-रीडायरेक्ट-गेट) और हिजैक्स को न भूलें (पहले बिना js वाला काम करें, फिर लिंक और बटन को ajax के साथ हाईजैक करें)
नील मैकग्यूगन

जवाबों:


488

थोड़ा सभ्य वेब एप्लिकेशन में डिज़ाइन पैटर्न का मिश्रण होता है। मैं केवल सबसे महत्वपूर्ण लोगों का उल्लेख करूंगा।


मॉडल दृश्य नियंत्रक पैटर्न

जो कोर (आर्किटेक्चरल) डिज़ाइन पैटर्न आप उपयोग करना चाहते हैं, वह मॉडल-व्यू-कंट्रोलर पैटर्न हैनियंत्रक एक सर्वलेट जो (में) सीधे बनाता है द्वारा प्रतिनिधित्व किया जा रहा है / एक विशिष्ट का उपयोग करता है मॉडल और देखें अनुरोध के आधार पर। मॉडल JavaBean वर्गों द्वारा व्यक्त किया जा रहा है। यह अक्सर बिजनेस मॉडल में और अधिक विभक्त होता है जिसमें क्रिया (व्यवहार) और डेटा मॉडल होता है जिसमें डेटा (सूचना) होता है। देखें जो (करने के लिए सीधी पहुँच है JSP फ़ाइलों द्वारा प्रतिनिधित्व किया जा रहा है डाटा ) मॉडल से ईएल (अभिव्यक्ति भाषा)।

फिर, कार्यों और घटनाओं को कैसे नियंत्रित किया जाता है, इसके आधार पर भिन्नताएं हैं। लोकप्रिय हैं:

  • अनुरोध (कार्रवाई) आधारित MVC : यह लागू करने के लिए सबसे सरल है। ( व्यवसाय ) मॉडल सीधे HttpServletRequestऔर HttpServletResponseवस्तुओं के साथ काम करता है । आपको अनुरोध पैरामीटर को (ज्यादातर) स्वयं इकट्ठा करना, परिवर्तित करना और मान्य करना होगा। देखें सादे वेनिला HTML / CSS / जे एस और इसे बनाए रखने नहीं है अनुरोध भर में राज्य का प्रतिनिधित्व करती जा सकता है। यह दूसरों के बीच स्प्रिंग एमवीसी , स्ट्रट्स और स्ट्राइप्स काम करता है।

  • घटक आधारित MVC : इसे लागू करना कठिन है। लेकिन आप एक सरल मॉडल के साथ समाप्त होते हैं और देखते हैं कि सभी "कच्चे" सर्वलेट एपीआई पूरी तरह से दूर हो गए हैं। आपको अनुरोध पैरामीटर को स्वयं इकट्ठा करने, बदलने और मान्य करने की आवश्यकता नहीं होनी चाहिए। नियंत्रक इस कार्य को और सेट में एकत्र हुए, परिवर्तित और मान्य अनुरोध पैरामीटर करता है मॉडल । आपको बस उन क्रिया विधियों को परिभाषित करना है जो सीधे मॉडल के गुणों के साथ काम करती हैं। देखें JSP taglibs या XML तत्वों जो बारी में उत्पन्न HTML / CSS / जे एस के स्वाद में "घटक" का प्रतिनिधित्व करती है। दृश्य की स्थितिसत्र में बाद के अनुरोधों को बनाए रखा जाता है। यह सर्वर-साइड रूपांतरण, सत्यापन और मूल्य परिवर्तन की घटनाओं के लिए विशेष रूप से सहायक है। यह कैसे दूसरों JSF , विकेट और खेलने के बीच है! काम करता है।

एक साइड नोट के रूप में, एक होमग्रो एमवीसी फ्रेमवर्क के साथ हॉबीइंग एक बहुत अच्छा सीखने का व्यायाम है, और मैं इसे तब तक करने की सलाह देता हूं जब तक आप इसे निजी / निजी उद्देश्यों के लिए रखते हैं। लेकिन एक बार जब आप पेशेवर जाते हैं, तो यह दृढ़ता से अपने खुद को मजबूत करने के बजाय मौजूदा ढांचे को चुनने की सिफारिश की जाती है। एक मौजूदा और अच्छी तरह से विकसित ढांचे को सीखना अपने आप को एक मजबूत ढांचा विकसित करने और बनाए रखने की तुलना में लंबे समय तक कम समय लगता है।

नीचे दिए गए विस्तृत विवरण में मैं अपने आप को MVC आधारित अनुरोध करने के लिए प्रतिबंधित कर दूंगा क्योंकि इसे लागू करना आसान है।


फ्रंट कंट्रोलर पैटर्न ( मध्यस्थ पैटर्न )

सबसे पहले, नियंत्रक भाग को फ्रंट नियंत्रक पैटर्न (जो एक विशेष प्रकार का मध्यस्थ पैटर्न है ) को लागू करना चाहिए । इसमें केवल एक सर्वलेट शामिल होना चाहिए जो सभी अनुरोधों का एक केंद्रीकृत प्रवेश बिंदु प्रदान करता है। यह अनुरोध द्वारा उपलब्ध जानकारी के आधार पर मॉडल बनाना चाहिए , जैसे कि pathinfo या सर्वलेट, विधि और / या विशिष्ट पैरामीटर। बिजनेस मॉडल कहा जाता है 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

उदाहरण के लिए के रूप में उपसर्ग पैटर्न के मामले में /pages/*आप तो कर सकते थे आह्वान यूआरएल की तरह है http://example.com/pages/register , http://example.com/pages/login , आदि और प्रदान करते हैं /WEB-INF/register.jsp, /WEB-INF/login.jspउचित प्राप्त और पोस्ट कार्यों के साथ । भागों register, loginआदि तो request.getPathInfo()उपरोक्त उदाहरण में उपलब्ध हैं ।

जब आप प्रत्यय-पैटर्न का उपयोग कर रहे हों *.do, जैसे , *.htmlआदि, तब आप URL को http://example.com/register.do , http://example.com/login.do , इत्यादि की तरह लागू कर सकते हैं और आपको बदलना चाहिए इस जवाब में कोड उदाहरण (भी ActionFactory) के बजाय registerऔर loginभागों को निकालने के लिए 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)


अन्य पैटर्न

वे अब तक के महत्वपूर्ण पैटर्न थे।

आगे एक कदम पाने के लिए, आप एक वर्ग बनाने के लिए मुखौटा पैटर्न का उपयोग कर सकते हैं, Contextजो बदले में अनुरोध और प्रतिक्रिया वस्तुओं को लपेटता है और अनुरोध और प्रतिक्रिया वस्तुओं को सौंपते हुए कई सुविधा विधियां प्रदान करता है और उस तर्क को Action#execute()विधि के रूप में पारित करता है । यह कच्चे सर्वलेट एपीआई को दूर छिपाने के लिए एक अतिरिक्त सार परत जोड़ता है। फिर आपको मूल रूप से हर कार्यान्वयन में शून्य import javax.servlet.* घोषणाओं को समाप्त करना चाहिए Action। JSF के संदर्भ में, यह वही है जो FacesContextऔर ExternalContextवर्ग कर रहे हैं। आप इस जवाब में एक ठोस उदाहरण पा सकते हैं ।

फिर उस मामले के लिए राज्य पैटर्न है जिसे आप अनुरोध पैरामीटर को इकट्ठा करने, उन्हें परिवर्तित करने, उन्हें मान्य करने, मॉडल मूल्यों को अपडेट करने और कार्यों को निष्पादित करने के कार्यों को विभाजित करने के लिए एक अतिरिक्त अमूर्त परत जोड़ना चाहते हैं। जेएसएफ के संदर्भ में, यह वही है जो LifeCycleकर रहा है।

फिर उस मामले के लिए समग्र पैटर्न है जिसे आप एक घटक आधारित दृश्य बनाना चाहते हैं जिसे मॉडल के साथ जोड़ा जा सकता है और जिसका व्यवहार अनुरोध आधारित जीवनचक्र की स्थिति पर निर्भर करता है। जेएसएफ के संदर्भ में, यह वही है जो UIComponentप्रतिनिधित्व करता है।

इस तरह आप एक घटक आधारित ढांचे की ओर थोड़ा सा विकसित कर सकते हैं।


यह सभी देखें:


4
@ क्रिसमस: आप ऐसा कर सकते हैं उदाहरण के लिए एक स्टैटिक इनिशियलाइज़र ब्लॉक।
बालुसक

1
@ क्रिसमस: वैसे, यदि आप उन्हें web.xmlफिर से प्राप्त करना चाहते हैं , तो आप इसके लिए उपयोग कर सकते हैं ServletContextListener। कारखाने इसे लागू (और के रूप में पंजीकृत है <listener>में web.xml) और के दौरान भरने काम करते हैंcontextInitialized() विधि।
बालुस

3
वह कार्य करें जो "post_servlet" को इसके बजाय कार्रवाई में करना चाहिए। आपके पास एक से अधिक सर्वलेट नहीं होने चाहिए। एक्शन क्लासेस में बिजनेस स्टफ किया जाना चाहिए। यदि आप चाहते हैं कि यह एक नया अनुरोध हो, तो एक अलग दृष्टिकोण पर वापस लौटें, जो पुनर्निर्देशन का कारण बनेगा और जीईटी अनुरोध के साथ जुड़ी नई कार्रवाई में काम करेगा।
बालुसक

2
निर्भर करता है। सबसे आसान यह है कि इसे ठीक Actionउसी तरह लागू किया जाए जैसे सामान्य सर्वलेट्स के साथ होता है ( एक मूल उदाहरण के लिए सर्वलेट्स विकी भी देखें , जिसे आप कुछ Validatorइंटरफ़ेस में आगे रिफ्लेक्टर करने के लिए स्वतंत्र हैं )। लेकिन आप इसे कार्रवाई को लागू करने से पहले भी कर सकते हैं, लेकिन यह अधिक जटिल है क्योंकि इसके लिए प्रति-दृश्य आधार पर मान्यता नियमों की आवश्यकता होती है। JSF भेंट करके इस कवर किया है required="true", validator="customValidatorName"एक्सएचटीएमएल मार्कअप में, आदि।
BalusC

2
@AndreyBotalov: जेवीएफ, स्प्रिंग एमवीसी, विकेट, स्ट्रट्स 2 आदि जैसे एमवीसी फ्रेमवर्क के स्रोत कोड की जांच करें, वे सभी खुले स्रोत हैं।
बालुसक

13

पीटा-अप एमवीसी पैटर्न में, सर्वलेट "सी" - नियंत्रक है।

इसका मुख्य काम प्रारंभिक अनुरोध मूल्यांकन करना है और फिर विशिष्ट कार्यकर्ता के लिए प्रारंभिक मूल्यांकन के आधार पर प्रसंस्करण को भेजना है। कार्यकर्ता की कुछ जिम्मेदारियों में से कुछ प्रेजेंटेशन लेयर बीन्स को सेटअप करना और HTML को रेंडर करने के लिए JSP पेज पर रिक्वेस्ट फॉरवर्ड करना हो सकता है। तो, इस कारण से, आपको सेवा स्तर पर अनुरोध ऑब्जेक्ट को पास करने की आवश्यकता है।

हालांकि, मैं कच्ची Servletकक्षाएं लिखना शुरू नहीं करूंगा । वे जो काम करते हैं वह बहुत ही अनुमानित है और बॉयलरप्लेट, ऐसा कुछ है जो रूपरेखा बहुत अच्छी तरह से करता है। सौभाग्य से, कई उपलब्ध, समय-परीक्षण वाले उम्मीदवार (वर्णमाला क्रम में) हैं: अपाचे विकेट , जावा सर्वर चेहरे , कुछ नाम रखने के लिए स्प्रिंग


5

IMHO, वेब एप्लिकेशन के मामले में बहुत अंतर नहीं है यदि आप इसे जिम्मेदारी असाइनमेंट के कोण से देखते हैं। हालांकि, परत में स्पष्टता रखें। प्रस्तुति परत में प्रस्तुति के उद्देश्य के लिए विशुद्ध रूप से कुछ भी रखें, जैसे नियंत्रण और वेब नियंत्रण के लिए विशिष्ट कोड। बस अपनी संस्थाओं को व्यावसायिक परत में रखें और सभी सुविधाएँ (जैसे ऐड, एडिट, डिलीट) आदि को व्यावसायिक परत में रखें। हालाँकि उन्हें प्रस्तुति परत में संभाले जाने के लिए ब्राउज़र पर प्रस्तुत करना। .Net के लिए, ASP.NET MVC पैटर्न परतों को अलग रखने के मामले में बहुत अच्छा है। MVC पैटर्न में देखें।


क्या आप सर्वलेट में जाने के लिए थोड़ा स्पष्ट हो सकते हैं?
माविया

यदि आप MVC का उपयोग करते हैं तो सर्वलेट नियंत्रक होना चाहिए।
कांगकान

3

मैंने स्ट्रट्स ढांचे का उपयोग किया है और इसे सीखना काफी आसान है। स्ट्रट्स ढांचे का उपयोग करते समय आपकी साइट के प्रत्येक पृष्ठ में निम्नलिखित आइटम होंगे।

1) एक क्रिया जो प्रयोग की जाती है उसे हर बार HTML पृष्ठ को ताज़ा किया जाता है। जब पृष्ठ पहले लोड हो जाता है और वेब UI और व्यावसायिक परत के बीच इंटरैक्शन को संभालता है तो कार्रवाई को डेटा को पॉप्युलेट करना चाहिए। यदि आप जेस्प पेज का उपयोग कर रहे हैं तो एक उत्परिवर्तित जावा ऑब्जेक्ट को संशोधित करने के लिए जावा ऑब्जेक्ट की एक कॉपी को मूल के बजाय फॉर्म में संग्रहित किया जाना चाहिए ताकि मूल डेटा संशोधित न हो जाए जब तक कि उपयोगकर्ता पेज को बचाता नहीं है।

2) वह प्रपत्र जो क्रिया और jsp पृष्ठ के बीच डेटा स्थानांतरित करने के लिए उपयोग किया जाता है। इस ऑब्जेक्ट में गेट्टर का एक सेट शामिल होना चाहिए और उन विशेषताओं के लिए बसना चाहिए जो कि jsp फ़ाइल के लिए सुलभ होनी चाहिए। फॉर्म में डेटा को मान्य करने से पहले उसे सत्यापित करने की विधि भी होती है।

3) एक jsp पेज जो पेज के अंतिम HTML को रेंडर करने के लिए उपयोग किया जाता है। Jsp पृष्ठ HTML का एक हाइब्रिड है और विशेष स्ट्रट्स टैग का उपयोग रूप में डेटा तक पहुंचने और हेरफेर करने के लिए किया जाता है। यद्यपि स्ट्रट्स उपयोगकर्ताओं को जावा कोड को jsp फ़ाइलों में सम्मिलित करने की अनुमति देता है, आपको ऐसा करने के लिए बहुत सतर्क होना चाहिए क्योंकि यह आपके कोड को पढ़ने में अधिक कठिन बनाता है। Jsp फ़ाइलों के अंदर जावा कोड डिबग करना मुश्किल है और यूनिट परीक्षण नहीं किया जा सकता है। यदि आप पाते हैं कि jsp फाइल के अंदर जावा कोड की 4-5 से अधिक लाइनें लिखी गई हैं, तो कोड को संभवतः कार्रवाई में ले जाया जाना चाहिए।


नोट: स्ट्रट्स 2 में फॉर्म ऑब्जेक्ट को इसके बजाय एक मॉडल के रूप में संदर्भित किया जाता है, लेकिन उसी तरह काम करता है जैसा मैंने अपने मूल उत्तर में वर्णित किया था।
EsotericNonsense

3

BalusC उत्कृष्ट उत्तर वेब अनुप्रयोगों के लिए अधिकांश पैटर्न को कवर करता है।

कुछ एप्लिकेशन को चेन-ऑफ-रिस्पॉन्सिबिलिटी_पैटर्न की आवश्यकता हो सकती है

ऑब्जेक्ट-ओरिएंटेड डिज़ाइन में, चेन-ऑफ़-जिम्मेदारी पैटर्न एक डिज़ाइन पैटर्न है जिसमें कमांड ऑब्जेक्ट्स का स्रोत और प्रोसेसिंग ऑब्जेक्ट्स की एक श्रृंखला होती है। प्रत्येक प्रोसेसिंग ऑब्जेक्ट में लॉजिक होता है जो कमांड ऑब्जेक्ट्स के प्रकार को परिभाषित करता है जिसे वह संभाल सकता है; बाकी श्रृंखला में अगली प्रसंस्करण वस्तु के लिए पारित किए जाते हैं।

इस पैटर्न का उपयोग करने के लिए केस का उपयोग करें:

जब हैंडलर अनुरोध (कमांड) को संसाधित करने के लिए अज्ञात है और यह अनुरोध कई ऑब्जेक्ट्स को भेजा जा सकता है। आम तौर पर आप उत्तराधिकारी को ऑब्जेक्ट पर सेट करते हैं । यदि वर्तमान ऑब्जेक्ट अनुरोध को संभाल नहीं सकता है या अनुरोध को आंशिक रूप से संसाधित कर सकता है और उत्तराधिकारी ऑब्जेक्ट को उसी अनुरोध को अग्रेषित कर सकता है ।

उपयोगी एसई प्रश्न / लेख:

मैं कभी एक डेकोरेटर पर ज़िम्मेदारी की श्रृंखला का उपयोग क्यों करूंगा?

जिम्मेदारी की श्रृंखला के लिए सामान्य उपयोग?

ऑड - ईवन से चेन-ऑफ-जिम्मेदारी-पैटर्न

sourcemaking से chain_of_responsibility

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.