Pola Desain aplikasi berbasis web [ditutup]

359

Saya merancang aplikasi berbasis web yang sederhana. Saya baru mengenal domain berbasis web ini. Saya membutuhkan saran Anda mengenai pola desain seperti bagaimana tanggung jawab harus didistribusikan di antara Servlet, kriteria untuk membuat Servlet baru, dll.

Sebenarnya, saya memiliki beberapa entitas di halaman rumah saya dan sesuai dengan masing-masing entitas, kami memiliki beberapa opsi seperti menambah, mengedit, dan menghapus. Sebelumnya saya menggunakan satu Servlet per opsi seperti Servlet1 untuk add entity1, Servlet2 untuk mengedit entitas1 dan seterusnya dan dengan cara ini kami akhirnya memiliki sejumlah besar servlet.

Sekarang kami mengubah desain kami. Pertanyaan saya adalah bagaimana Anda secara tepat memilih bagaimana Anda memilih tanggung jawab servlet. Haruskah kita memiliki satu Servlet per entitas yang akan memproses semua opsi itu dan meneruskan permintaan ke lapisan layanan. Atau haruskah kita memiliki satu servlet untuk seluruh halaman yang akan memproses permintaan seluruh halaman dan kemudian meneruskannya ke lapisan layanan yang sesuai? Juga, haruskah objek permintaan diteruskan ke lapisan layanan atau tidak.

mawia
sumber
8
Bukan pola desain yang benar-benar resmi, tetapi jangan lupa PRG (post-redirect-get) dan Hijax (buat pekerjaan tanpa js terlebih dahulu, lalu bajak tautan dan tombol dengan ajax)
Neil McGuigan

Jawaban:

488

Aplikasi web yang sedikit baik terdiri dari campuran pola desain. Saya hanya akan menyebutkan yang paling penting.


Pola Pengendali Tampilan Model

Pola desain inti (arsitektur) yang ingin Anda gunakan adalah pola Model-View-Controller . The Pengendali harus diwakili oleh Servlet yang (di) langsung menciptakan / menggunakan spesifik Model dan View berdasarkan permintaan. The Model yang akan diwakili oleh kelas JavaBean. Ini sering dibagi lagi dalam Model Bisnis yang berisi tindakan (perilaku) dan Model Data yang berisi data (informasi). The View adalah untuk diwakili oleh file JSP yang memiliki akses langsung ke ( data ) Model oleh EL (Expression Language).

Lalu, ada variasi berdasarkan bagaimana tindakan dan peristiwa ditangani. Yang populer adalah:

  • Permintaan (tindakan) berdasarkan MVC : ini adalah yang paling sederhana untuk diterapkan. The ( Bisnis ) Model bekerja secara langsung dengan HttpServletRequestdan HttpServletResponsebenda-benda. Anda harus mengumpulkan, mengonversi, dan memvalidasi parameter permintaan (kebanyakan) sendiri. The View dapat diwakili oleh plain vanilla HTML / CSS / JS dan tidak mempertahankan negara di seluruh permintaan. Beginilah cara kerja Spring MVC , Struts and Stripes .

  • MVC berbasis komponen : ini lebih sulit untuk diterapkan. Tetapi Anda berakhir dengan model yang lebih sederhana dan melihat di mana semua API Servlet "mentah" diabstraksi sepenuhnya. Anda seharusnya tidak perlu mengumpulkan, mengonversi, dan memvalidasi sendiri parameter permintaan. The Pengendali melakukan tugas ini dan set berkumpul, parameter permintaan dikonversi dan divalidasi dalam Model . Yang perlu Anda lakukan adalah mendefinisikan metode tindakan yang bekerja langsung dengan properti model. The View diwakili oleh "komponen" di rasa taglibs JSP atau elemen XML yang pada gilirannya menghasilkan HTML / CSS / JS. Keadaan Tampilanuntuk permintaan selanjutnya dipertahankan dalam sesi. Ini sangat membantu untuk konversi sisi server, validasi, dan perubahan nilai. Ini adalah bagaimana antara lain JSF , Wicket and Play! bekerja.

Sebagai catatan tambahan, bermain-main dengan kerangka kerja MVC buatan sendiri adalah latihan pembelajaran yang sangat menyenangkan, dan saya merekomendasikannya selama Anda menyimpannya untuk keperluan pribadi / pribadi. Tetapi begitu Anda menjadi profesional, maka sangat disarankan untuk memilih kerangka kerja yang ada daripada menciptakan kembali kerangka kerja Anda sendiri. Mempelajari kerangka kerja yang sudah ada dan dikembangkan dengan baik membutuhkan waktu lebih lama daripada mengembangkan dan mempertahankan kerangka kerja yang kokoh.

Dalam penjelasan rinci di bawah ini saya akan membatasi diri untuk meminta MVC berbasis karena itu lebih mudah untuk diterapkan.


Pola Pengontrol Depan ( pola Mediator )

Pertama, bagian Pengendali harus menerapkan pola Pengontrol Depan (yang merupakan jenis khusus dari pola Mediator ). Ini harus terdiri dari hanya satu servlet yang menyediakan titik masuk terpusat dari semua permintaan. Ini harus membuat Model berdasarkan informasi yang tersedia oleh permintaan, seperti pathinfo atau servletpath, metode dan / atau parameter tertentu. The Model Bisnis disebut Actiondi bawah ini HttpServletcontoh.

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

Menjalankan tindakan harus mengembalikan beberapa pengidentifikasi untuk menemukan tampilan. Paling sederhana adalah menggunakannya sebagai nama file dari JSP. Peta servlet ini pada spesifik url-patterndi web.xml, misalnya /pages/*, *.doatau bahkan hanya *.html.

Dalam kasus pola awalan seperti misalnya /pages/*Anda kemudian dapat memanggil URL seperti http://example.com/pages/register , http://example.com/pages/login , dll dan berikan /WEB-INF/register.jsp, /WEB-INF/login.jspdengan tindakan GET dan POST yang sesuai . Bagian-bagian register,, logindll kemudian tersedia request.getPathInfo()seperti pada contoh di atas.

Saat Anda menggunakan pola sufiks seperti *.do,, *.htmldll, maka Anda dapat memunculkan URL seperti http://example.com/register.do , http://example.com/login.do , dll dan Anda harus mengubah contoh kode dalam jawaban ini (juga the ActionFactory) untuk mengekstrak bagian register- loginbagian dan request.getServletPath()sebagai gantinya.


Pola strategi

The Actionharus mengikuti pola Strategi . Itu perlu didefinisikan sebagai tipe abstrak / antarmuka yang harus melakukan pekerjaan berdasarkan argumen yang dilewatkan dari metode abstrak (ini adalah perbedaan dengan pola Command , di mana jenis abstrak / antarmuka harus melakukan pekerjaan berdasarkan pada argumen yang telah diteruskan selama penciptaan implementasi).

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

Anda mungkin ingin membuat yang Exceptionlebih spesifik dengan pengecualian khusus seperti ActionException. Ini hanya contoh awal kickoff, sisanya terserah Anda.

Berikut adalah contoh dari LoginActionyang (seperti namanya) mencatat pengguna. Itu Usersendiri pada gilirannya Model Data . The View menyadari kehadiran 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.
        }
    }

}

Pola metode pabrik

The ActionFactoryharus mengikuti pola metode Factory . Pada dasarnya, ini harus menyediakan metode kreatif yang mengembalikan implementasi konkret dari tipe abstrak / antarmuka. Dalam hal ini, harus mengembalikan implementasi Actionantarmuka berdasarkan informasi yang disediakan oleh permintaan. Misalnya, metode dan pathinfo (pathinfo adalah bagian setelah konteks dan jalur servlet di URL permintaan, tidak termasuk string kueri).

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

Pada actionsgilirannya harus berupa static / applicationwide Map<String, Action>yang menampung semua tindakan yang diketahui. Terserah Anda bagaimana mengisi peta ini. Hardcoding:

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

Atau dapat dikonfigurasi berdasarkan file konfigurasi properti / XML di classpath: (pseudo)

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

Atau secara dinamis didasarkan pada pemindaian di classpath untuk kelas yang mengimplementasikan antarmuka dan / atau anotasi tertentu: (pseudo)

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

Perlu diingat untuk membuat "tidak melakukan apa-apa" Actionuntuk kasus ini tidak ada pemetaan. Biarkan misalnya mengembalikan langsung request.getPathInfo().substring(1)itu.


Pola lainnya

Itu adalah pola penting sejauh ini.

Untuk melangkah lebih jauh, Anda bisa menggunakan pola Facade untuk membuat Contextkelas yang pada gilirannya membungkus objek permintaan dan respons dan menawarkan beberapa metode kenyamanan mendelegasikan ke objek permintaan dan respons dan meneruskannya sebagai argumen ke dalam Action#execute()metode sebagai gantinya. Ini menambahkan lapisan abstrak ekstra untuk menyembunyikan API Servlet mentah. Anda pada dasarnya harus berakhir dengan nol import javax.servlet.* deklarasi dalam setiap Actionimplementasi. Dalam istilah JSF, inilah yang dilakukan oleh FacesContextdan ExternalContextkelas. Anda dapat menemukan contoh nyata dalam jawaban ini .

Lalu ada pola Status untuk kasus yang ingin Anda tambahkan lapisan abstraksi ekstra untuk membagi tugas mengumpulkan parameter permintaan, mengonversinya, memvalidasinya, memperbarui nilai-nilai model dan menjalankan tindakan. Dalam istilah JSF, inilah yang LifeCycledilakukannya.

Lalu ada pola Komposit untuk kasus yang ingin Anda buat tampilan berbasis komponen yang dapat dilampirkan dengan model dan yang perilakunya tergantung pada keadaan siklus hidup berdasarkan permintaan. Dalam istilah JSF, inilah yang UIComponentdiwakilinya.

Dengan cara ini Anda dapat berevolusi sedikit demi sedikit menuju kerangka kerja berbasis komponen.


Lihat juga:

BalusC
sumber
4
@masato: Anda bisa melakukan ini misalnya blok inisialisasi statis.
BalusC
1
@masato: omong-omong, jika Anda ingin mengambilnya dari web.xml, maka Anda bisa menggunakan ServletContextListenerini. Mintalah pabrik mengimplementasikannya (dan daftar seperti <listener>di web.xml) dan melakukan pekerjaan pengisian selama contextInitialized()metode.
BalusC
3
Lakukan pekerjaan yang harus dilakukan "post_servlet" dalam tindakan. Anda seharusnya tidak memiliki lebih dari satu servlet. Hal-hal bisnis harus dilakukan dalam kelas tindakan. Jika Anda menginginkannya menjadi permintaan baru, maka kembali ke tampilan lain yang akan menyebabkan pengalihan dan lakukan pekerjaan dalam tindakan baru yang terkait dengan permintaan GET.
BalusC
2
Tergantung. Cara termudah adalah melakukannya dengan benar dalam Actionimplementasi dengan cara yang sama seperti pada servlets normal (lihat juga servlets wiki untuk contoh dasar, yang Anda bebas untuk refactor lebih jauh ke beberapa Validatorantarmuka). Tapi Anda juga bisa melakukannya sebelum menjalankan tindakan, tetapi ini lebih kompleks karena memerlukan aturan validasi yang diketahui berdasarkan per-view. JSF telah menutupi ini dengan korban required="true", validator="customValidatorName", dll di markup XHTML.
BalusC
2
@AndreyBotalov: periksa kode sumber kerangka MVC seperti JSF, Spring MVC, Wicket, Struts2, dll. Semuanya adalah open source.
BalusC
13

Dalam pola MVC yang terpukul, Servlet adalah "C" - controller.

Tugas utamanya adalah melakukan evaluasi permintaan awal dan kemudian mengirimkan pemrosesan berdasarkan evaluasi awal kepada pekerja tertentu. Salah satu tanggung jawab pekerja mungkin adalah menyiapkan beberapa lapisan kacang presentasi dan meneruskan permintaan ke halaman JSP untuk membuat HTML. Jadi, untuk alasan ini saja, Anda perlu meneruskan objek permintaan ke lapisan layanan.

Saya tidak akan mulai menulis Servletkelas mentah . Pekerjaan yang mereka lakukan sangat mudah diprediksi dan dipanaskan, sesuatu yang kerangka kerjanya sangat baik. Untungnya, ada banyak kandidat yang telah teruji oleh waktu (sesuai urutan abjad): Apache Wicket , Java Server Faces , Spring untuk beberapa nama.

Alexander Pogrebnyak
sumber
5

IMHO, tidak ada banyak perbedaan dalam hal aplikasi web jika Anda melihatnya dari sudut penugasan tanggung jawab. Namun, pertahankan kejelasan di layer. Simpan segala sesuatu murni untuk tujuan presentasi di lapisan presentasi, seperti kontrol dan kode khusus untuk kontrol web. Simpan saja entitas Anda di lapisan bisnis dan semua fitur (seperti menambah, mengedit, menghapus) dll di lapisan bisnis. Namun merendernya ke browser untuk ditangani di lapisan presentasi. Untuk .Net, pola ASP.NET MVC sangat baik dalam hal menjaga lapisan dipisahkan. Lihatlah pola MVC.

Kangkan
sumber
dapatkah Anda sedikit eksplisit dalam apa yang harus di servlet?
mawia
Servlet harus menjadi pengontrol jika Anda menggunakan MVC.
Kangkan
3

Saya telah menggunakan kerangka kerja struts dan merasa cukup mudah dipelajari. Saat menggunakan struts framework, setiap halaman situs Anda akan memiliki item berikut.

1) Suatu tindakan yang digunakan dipanggil setiap kali halaman HTML di-refresh. Tindakan harus mengisi data dalam bentuk ketika halaman pertama kali dimuat dan menangani interaksi antara UI web dan lapisan bisnis. Jika Anda menggunakan halaman jsp untuk memodifikasi objek java yang bisa diubah, salinan dari objek java harus disimpan dalam bentuk daripada yang asli sehingga data asli tidak bisa dimodifikasi kecuali pengguna menyimpan halaman.

2) Formulir yang digunakan untuk mentransfer data antara tindakan dan halaman jsp. Objek ini harus terdiri dari satu set pengambil dan setter untuk atribut yang harus dapat diakses ke file jsp. Formulir ini juga memiliki metode untuk memvalidasi data sebelum tetap ada.

3) Halaman jsp yang digunakan untuk membuat HTML akhir halaman. Halaman jsp adalah gabungan dari tag HTML dan struts khusus yang digunakan untuk mengakses dan memanipulasi data dalam formulir. Meskipun struts memungkinkan pengguna untuk memasukkan kode Java ke file jsp, Anda harus sangat berhati-hati melakukan hal itu karena membuat kode Anda lebih sulit dibaca. Kode Java di dalam file jsp sulit untuk di-debug dan tidak dapat diuji unit. Jika Anda menemukan diri Anda menulis lebih dari 4-5 baris kode java di dalam file jsp, kode tersebut mungkin harus dipindahkan ke tindakan.

Omong kosong Esoterik
sumber
Catatan: Dalam struts 2 objek Formulir disebut sebagai Model tetapi bekerja dengan cara yang sama seperti yang saya jelaskan dalam jawaban asli saya.
EsotericNonsense
3

BalusC jawaban yang sangat baik mencakup sebagian besar pola untuk aplikasi web.

Beberapa aplikasi mungkin memerlukan Chain-of-responsibility_pattern

Dalam desain berorientasi objek, pola rantai tanggung jawab adalah pola desain yang terdiri dari sumber objek perintah dan serangkaian objek pemrosesan. Setiap objek pemrosesan berisi logika yang mendefinisikan jenis objek perintah yang dapat ditangani; sisanya diteruskan ke objek pemrosesan berikutnya dalam rantai.

Gunakan case untuk menggunakan pola ini:

Ketika pawang memproses permintaan (perintah) tidak diketahui dan permintaan ini dapat dikirim ke beberapa objek. Umumnya Anda menetapkan penerus ke objek. Jika objek saat ini tidak dapat menangani permintaan atau memproses permintaan secara parsial dan meneruskan permintaan yang sama ke objek penerus .

Pertanyaan / artikel SE yang bermanfaat:

Mengapa saya akan menggunakan Rantai Tanggung Jawab atas Dekorator?

Penggunaan umum untuk rantai tanggung jawab?

rantai-of-tanggung jawab-pola dari oodesign

chain_of_responsibility dari pembuatan sumber

Ravindra babu
sumber