Tasarım Desenleri web tabanlı uygulamalar [kapalı]


359

Basit bir web tabanlı uygulama tasarlıyorum. Bu web tabanlı alanda yeniyim. Sorumluluğun Servlet'ler arasında nasıl dağıtılması gerektiği, yeni Servlet yapma kriterleri vb.

Aslında, ana sayfamda birkaç öğe var ve her birine karşılık gelen ekleme, düzenleme ve silme gibi birkaç seçeneğimiz var. Daha önce varlık1 eklemek için Servlet1, düzenleme varlık1 için Servlet2 gibi seçenekler başına bir Servlet kullanıyordum ve bu şekilde çok sayıda sunucu uygulamasına sahip olduk.

Şimdi tasarımımızı değiştiriyoruz. Sorum şu, bir sunucu uygulamasının sorumluluğunu tam olarak nasıl seçtiğinizi. Varlık başına tek bir Sunucu uygulamamız varsa, tüm seçeneklerini işleyecek ve talebi hizmet katmanına iletecektir. Yoksa tüm sayfa isteğini işleyecek ve ardından ilgili hizmet katmanına iletecek tüm sayfa için bir sunucu uygulamamız mı olmalı? Ayrıca, istek nesnesi hizmet katmanına yönlendirilip yönlendirilmemelidir.


8
Gerçekten resmi tasarım desenleri, ancak PRG (post-yönlendirme-get) ve Hijax (ilk, sonra ajax ile bağlantıları ve düğmeleri kaçırmak hiçbir js ile yapmak çalışması) unutmayın
Neil McGuigan

Yanıtlar:


489

Biraz iyi bir web uygulaması tasarım desenlerinin bir karışımından oluşur. Sadece en önemlilerinden bahsedeceğim.


Model Görünümü Denetleyici deseni

Kullanmak istediğiniz çekirdek (mimari) tasarım deseni Model-Görünüm-Denetleyici desenidir . Denetleyici (in) doğrudan oluşturan bir Servlet tarafından temsil edilecek / spesifik kullanır Modeli ve Görünüm isteğine dayanarak. Model Javabean sınıfları tarafından temsil edilmesi. Bu genellikle eylemleri (davranış) içeren İş Modeli ve verileri (bilgileri) içeren Veri Modeli'nde daha da bölünebilir . Görünüm (doğrudan erişime sahip JSP dosyaları tarafından temsil edilecek veriler ) Model EL (Expression Dil) tarafından.

Ardından, eylemlerin ve olayların nasıl ele alındığına bağlı olarak farklılıklar vardır. Popüler olanlar:

  • İstek (işlem) tabanlı MVC : uygulanması en basit olanıdır. ( İş ) Modeli doğrudan HttpServletRequestve HttpServletResponsenesnelerle çalışır . İstek parametrelerini (çoğunlukla) kendiniz toplamanız, dönüştürmeniz ve doğrulamanız gerekir. Görünüm istekleri karşısında düz vanilya HTML / CSS / JS ve tutmaz devlet tarafından temsil edilebilir. Diğerleri arasında Bahar MVC , Struts ve Stripes çalışır.

  • Bileşen tabanlı MVC : uygulanması daha zordur. Ancak sonuçta daha basit bir model ve görünüm elde edersiniz; burada tüm "raw" Servlet API'si tamamen kaldırılır. İstek parametrelerini kendiniz toplamanız, dönüştürmeniz ve doğrulamanız gerekmez. Kontrolör bu görevi yapar ve toplanan, dönüştürülmüş ve valide istek parametreleri ayarlar Modeli . Tek yapmanız gereken, doğrudan model özellikleriyle çalışan eylem yöntemlerini tanımlamaktır. Görünüm JSP taglibs veya sırayla / JS HTML / CSS oluşturur XML öğelerinin lezzet "bileşenleri" ile temsil edilir. Devlet Görünüm sonraki isteği oturumda korunur. Bu, özellikle sunucu tarafı dönüştürme, doğrulama ve değer değiştirme olayları için yararlıdır. Diğerleri arasında JSF , Wicket ve Play! İşler.

Bir yan not olarak, ev yapımı bir MVC çerçevesiyle hobi yapmak çok güzel bir öğrenme egzersizidir ve kişisel / özel amaçlar için sakladığınız sürece bunu tavsiye ederim. Ancak profesyonelleştikten sonra, kendi çerçevenizi yeniden keşfetmek yerine mevcut bir çerçeveyi seçmeniz şiddetle tavsiye edilir. Mevcut ve iyi gelişmiş bir çerçeveyi öğrenmek, sağlam bir çerçeveyi kendiniz geliştirmekten ve sürdürmekten daha uzun zaman alır.

Aşağıdaki ayrıntılı açıklamada, uygulanması daha kolay olduğu için kendimi temelli MVC istemeyle sınırlayacağım.


Ön Kumanda düzeni ( Arabulucu modeli )

İlk olarak, Kontrolör kısmı, Ön Kontrol modelini (özel bir tür Arabulucu modeli ) uygulamalıdır. Tüm isteklerin merkezi bir giriş noktasını sağlayan tek bir sunucu uygulamasından oluşmalıdır. OluşturmalıModeli , pathinfo veya servletpath, yöntem ve / veya belirli parametreler gibi istek tarafından sağlanan bilgilere dayanarak . İş Modeli olarak adlandırılan Actionaşağıda HttpServletörnek.

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

Eylemin yürütülmesi, görünümü bulmak için bazı tanımlayıcılar döndürmelidir. En basit olanı, JSP'nin dosya adı olarak kullanmak olacaktır. Belli bir bölüme bu sunucu uygulamasını Harita url-patterniçinde web.xmlörneğin /pages/*,*.do hatta sadece *.html.

Örneğin önek kalıpları olması durumunda, http://example.com/pages/register , http://example.com/pages/login/pages/* gibi URL'leri çağırabilirsiniz. , vb ve sağlamak /WEB-INF/register.jsp, /WEB-INF/login.jspuygun GET ve POST eylemlerle . Parçalar register, loginvb. Daha sonra request.getPathInfo()yukarıdaki örnekte olduğu gibi kullanılabilir .

, Vb. Gibi ek kalıpları kullanırken *.do, http://example.com/register.do , http://example.com/login.do , vb. *.htmlGibi URL'leri çağırabilirsiniz . kodları bu cevapta (ayrıca ) kodlamak yerine ve parçaları .ActionFactoryregisterloginrequest.getServletPath()


Strateji modeli

Actionİzlemelidir Strateji deseni . Soyut yöntemin aktarılmış argümanlarına dayanarak işi yapması gereken bir soyut / arayüz tipi olarak tanımlanması gerekir (bu, komut model Anahtar / arabirimi göre işi, ki burada, uygulamanın oluşturulması sırasında aktarılan argümanlar ).

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

Gibi Exceptionözel bir istisna ile daha belirgin hale getirmek isteyebilirsinizActionException . Bu sadece temel bir başlama örneği, gerisi tamamen size kalmış.

Aşağıda, LoginAction(adından da anlaşılacağı gibi) kullanıcının oturum açtığı bir örnek bulunmaktadır . UserKendisi sırayla bir olduğunu Veri Modeli . Görünüm varlığının farkındadır 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.
        }
    }

}

Fabrika yöntemi desen

ActionFactoryİzlemelidir Fabrika yöntemi desen . Temel olarak, bir soyut / arayüz türünün somut bir uygulamasını döndüren yaratıcı bir yöntem sağlamalıdır. Bu durumda, Actionistek tarafından sağlanan bilgilere dayanarak arabirimin bir uygulamasını döndürmelidir . Örneğin, yöntem ve pathinfo (pathinfo, sorgu dizesi hariç istek URL'sindeki bağlam ve sunucu uygulaması yolundan sonraki bölümdür).

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

Buna actionskarşılık, Map<String, Action>bilinen tüm eylemleri tutan bir statik / uygulama çapında olmalıdır . Bu haritayı nasıl dolduracağınız size kalmış. hardcoding:

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

Veya sınıf yolundaki bir özellikler / XML yapılandırma dosyasına dayalı olarak yapılandırılabilir: (sözde)

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

Veya belirli bir arabirimi ve / veya ek açıklamayı uygulayan sınıflar için sınıf yolundaki bir taramayı temel alarak: (sözde)

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

ActionEşleme olmaması durumunda "hiçbir şey yapma" oluşturmayı unutmayın . Örneğin doğrudan o request.getPathInfo().substring(1)zaman dönmesine izin verin .


Diğer desenler

Bunlar şimdiye kadarki önemli kalıplardı.

Bir adım daha ileri gitmek için , istek ve yanıt nesnelerini saran ve istek ve yanıt nesnelerine yetki veren ve bunun yerine yönteme argüman olarak ileten çeşitli kolaylık yöntemleri sunan bir sınıf oluşturmak için Cephe desenini kullanabilirsiniz . Bu, ham Servlet API'sını gizlemek için ekstra bir soyut katman ekler. Daha sonra temel olarak her uygulamada sıfır bildirim ile sonuçlanmalısınız . JSF açısından, ve sınıfları bunu yapıyor. Somut bir örnek bulabilirsinizContextAction#execute() import javax.servlet.*ActionFacesContextExternalContextBu cevapta .

Ardından , istek parametrelerini toplama, dönüştürme, doğrulama, model değerlerini güncelleme ve eylemleri yürütme görevlerini bölmek için fazladan bir soyutlama katmanı eklemek istediğiniz durum için Devlet modeli vardır . JSF açısından, bu neLifeCycle yapıyor.

Ardından , modele eklenebilen ve davranışı istek tabanlı yaşam döngüsünün durumuna bağlı olan bileşen tabanlı bir görünüm oluşturmak istediğiniz durum için Bileşik desen vardır . JSF açısından, bu neUIComponent temsil budur.

Bu şekilde, bileşen tabanlı bir çerçeveye doğru yavaş yavaş gelişebilirsiniz.


Ayrıca bakınız:


4
@masato: Bunu örneğin statik bir başlatıcı bloğunda yapabilirsiniz.
BalusC

1
@masato: Bu arada, onları almak web.xmlisterseniz, bunun için bir kullanabilirsiniz ServletContextListener. Fabrikadan uygulamayı yaptırın (ve olduğu gibi <listener>kaydedin web.xml) ve contextInitialized()yöntem sırasında doldurma işini yapın .
BalusC

3
Bunun yerine eylemde "post_servlet" in yapması gereken işi yapın. Birden fazla sunucu uygulamanız olmamalıdır. İş dersleri eylem sınıflarında yapılmalıdır. Yeni bir istek olmasını istiyorsanız, yönlendirmeye neden olacak farklı bir görünüme dönün ve işi GET isteğiyle ilişkili yeni eylemde yapın.
BalusC

2
Bağlı olmak. En kolayı, Actionuygulamada normal sunucu uygulamalarıyla aynı şekilde yapılmasıdır ( temel bir örnek için ayrıca bazı uygulamalara yeniden bakabileceğiniz ücretsiz bir örnek için sunucu uygulaması wiki'ye bakın Validator). Ancak eylemi başlatmadan önce de yapabilirsiniz, ancak bu, görüntüleme kurallarına göre doğrulama kurallarının bilinmesini gerektirdiğinden daha karmaşıktır. MTU arz yoluyla bu kapsamış required="true", validator="customValidatorName"XHTML işaretlemesindeki vb.
BalusC

2
@AndreyBotalov: JSF, Spring MVC, Wicket, Struts2, vb. Gibi MVC çerçevelerinin kaynak kodunu kontrol edin.
BalusC

13

Dövülmüş MVC modelinde, Servlet "C" denetleyicisidir.

Ana işi, ilk talep değerlendirmesini yapmak ve daha sonra ilk değerlendirmeye dayalı olarak işlemi belirli bir çalışana göndermektir. Çalışanın sorumluluklarından biri, bazı sunum katmanı çekirdekleri oluşturmak ve HTML oluşturma isteğini JSP sayfasına iletmek olabilir. Bu nedenle, yalnızca bu nedenle, istek nesnesini hizmet katmanına geçirmeniz gerekir.

Yine de ham Servletdersler yazmaya başlamam . Yaptıkları iş çok öngörülebilir ve ortak bir çerçevedir, bu da çerçevenin çok iyi yaptığı bir şeydir. Neyse ki, (alfabetik sırayla) birçok mevcuttur, zaman içinde test edilmiş aday var: Apache Wicket , Java Server Faces , Bahar birkaç isim.


5

IMHO, web uygulamasında sorumluluk atama açısından bakarsanız fazla bir fark yoktur. Ancak, katmandaki netliği koruyun. Web denetimlerine özgü denetim ve kod gibi, yalnızca sunum katmanındaki sunum amacıyla her şeyi saklayın. Varlıklarınızı iş katmanında ve iş katmanındaki tüm özellikleri (ekleme, düzenleme, silme vb.) Tutun. Ancak bunları sunum katmanında işlenecek tarayıcıya oluşturma. .Net için, ASP.NET MVC deseni katmanları ayrı tutmak açısından çok iyidir. MVC modeline bakın.


Servlet'te ne yapması gerektiği konusunda biraz açık olabilir misiniz?
mawia

MVC kullanıyorsanız sunucu uygulaması denetleyici olmalıdır.
Kangkan

3

Destekleri kullandım çerçevesini öğrenmeyi oldukça kolay buldum. Destek çerçevesi kullanıldığında, sitenizin her sayfasında aşağıdaki öğeler bulunur.

1) HTML sayfası her yenilendiğinde kullanılan bir eylem çağrılır. Sayfa ilk kez yüklendiğinde ve web kullanıcı arayüzü ile iş katmanı arasındaki etkileşimleri gerçekleştirdiğinde, eylemin formdaki verileri doldurması gerekir. Değişken bir java nesnesini değiştirmek için jsp sayfasını kullanıyorsanız, kullanıcı sayfayı kaydetmedikçe orijinal verilerin değiştirilmemesi için java nesnesinin bir kopyasının orijinal yerine formda depolanması gerekir.

2) Eylem ve jsp sayfası arasında veri aktarmak için kullanılan form. Bu nesne, jsp dosyası tarafından erişilebilir olması gereken öznitelikler için bir alıcı ve ayarlayıcılardan oluşmalıdır. Formda ayrıca verilerin kalıcı hale getirilmeden önce doğrulanması için bir yöntem de bulunur.

3) Sayfanın nihai HTML'sini oluşturmak için kullanılan bir jsp sayfası. Jsp sayfası, formdaki verilere erişmek ve işlemek için kullanılan bir HTML ve özel struts etiketlerinin bir birleşimidir. Struts, kullanıcıların jsp dosyalarına Java kodu eklemesine izin verse de, kodunuzu okumayı zorlaştırdığı için bunu yaparken çok dikkatli olmalısınız. Jsp dosyalarının içindeki Java kodu hata ayıklamak zordur ve birim test edilemez. Kendinizi bir jsp dosyasının içine 4-5 satırdan fazla java kodu yazıyorsanız, kod muhtemelen eyleme taşınmalıdır.


Not: Payandalar 2'de Form nesnesi yerine Model olarak adlandırılır, ancak orijinal yanıtımda açıkladığım şekilde çalışır.
EsotericNonsense

3

BalusC mükemmel yanıtı, web uygulamaları için desenlerin çoğunu kapsar.

Bazı başvurular Sorumluluk Zinciri_ desenini gerektirebilir

Nesneye yönelik tasarımda, sorumluluk zinciri deseni, bir komut nesneleri kaynağı ve bir dizi işleme nesnesinden oluşan bir tasarım modelidir. Her işleme nesnesi, işleyebileceği komut nesnesi türlerini tanımlayan mantık içerir; geri kalanı zincirdeki bir sonraki işleme nesnesine geçirilir.

Bu deseni kullanmak için büyük / küçük harf kullanın:

Bir isteği işlemek için işleyici (komut) bilinmiyor ve bu istek birden çok nesneye gönderilebilir. Genellikle halefi nesnelere ayarlarsınız . Geçerli nesne isteği işleyemiyorsa veya isteği kısmen işleyemiyorsa ve aynı isteği halefine iletiyorsa nesneye .

Yararlı SE soruları / makaleleri:

Neden bir Dekoratöre Karşı Sorumluluk Zinciri kullanayım?

Sorumluluk zinciri için ortak kullanımlar?

Zincir-of-the sorumluluğu-desenoodesign'dan

kaynak yapımından kaynaklanan sorumluluk

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.