Bagaimana cara menentukan apa yang harus mendapatkan controller masing-masing?


10

Saya menggunakan pola MVC di aplikasi web saya yang dibuat dengan PHP.

Saya selalu berjuang untuk menentukan apakah saya memerlukan pengontrol khusus untuk serangkaian tindakan atau jika saya harus menempatkannya di dalam pengontrol yang sudah ada.

Apakah ada aturan praktis yang baik untuk diikuti saat membuat pengontrol?

Misalnya saya dapat memiliki:

AuthenticationController dengan tindakan:

  • index() untuk menampilkan formulir login.
  • submit() untuk menangani pengiriman formulir.
  • logout(), Cukup jelas.

ATAU

LoginController dengan tindakan:

  • index() untuk menampilkan formulir login.
  • submit() untuk menangani pengiriman formulir.

LogoutController dengan aksi:

  • index() untuk menangani keluar.

ATAU

AccountController dengan tindakan:

  • loginGet() untuk menampilkan formulir login.
  • loginPost() untuk menangani pengiriman formulir login.
  • logoutGet() untuk menangani keluar.
  • registerGet() untuk menampilkan formulir pendaftaran.
  • registerPost() untuk menangani pengiriman formulir.

    Dan tindakan lain apa pun yang terlibat dengan akun.


Mungkin kita lihat desain RESTful. Itu tidak menyelesaikan setiap masalah seperti ini, tetapi memberi Anda arah yang sangat baik bagaimana memikirkannya.
thorsten müller

Jawaban:


3

Untuk menemukan pengelompokan yang tepat untuk pengontrol, pikirkan pengujiannya .

(Bahkan jika Anda tidak benar-benar melakukan pengujian apa pun, memikirkan bagaimana Anda akan menguji pengontrol Anda akan memberi Anda wawasan yang sangat bagus tentang cara menyusun strukturnya.)

An AuthenticationControllertidak dapat diuji dengan sendirinya, karena hanya berisi fungsionalitas untuk masuk dan keluar, tetapi kode pengujian Anda harus entah bagaimana membuat akun palsu untuk tujuan pengujian sebelum dapat menguji login yang berhasil. Anda dapat melewati subsistem yang diuji dan langsung menuju ke model Anda untuk membuat akun pengujian, tetapi kemudian Anda akan memiliki tes rapuh di tangan Anda: jika model berubah, Anda harus memodifikasi tidak hanya kode yang menguji model, tetapi juga kode yang menguji pengontrol, meskipun antarmuka dan perilaku pengontrol tetap tidak berubah. Itu tidak masuk akal.

A LoginControllertidak cocok karena alasan yang sama: Anda tidak dapat mengujinya tanpa membuat akun terlebih dahulu, dan ada lebih banyak hal yang tidak dapat Anda uji, seperti misalnya mencegah duplikat login tetapi kemudian mengizinkan pengguna untuk login setelah logout. (Karena pengontrol ini tidak memiliki fungsi logout.)

Sebuah AccountControllerakan memberikan semua yang Anda butuhkan untuk melakukan pengujian: Anda dapat membuat akun pengujian dan kemudian mencoba masuk, Anda dapat menghapus akun dan kemudian memastikan Anda tidak bisa masuk lagi, Anda dapat mengubah kata sandi dan memastikan bahwa kata sandi yang tepat harus digunakan untuk login, dll.

Untuk menyimpulkan: untuk menulis bahkan test suite terkecil, Anda harus membuat semua fungsi yang AccountControllertersedia untuk itu. Membagi lagi menjadi pengontrol yang lebih kecil tampaknya menghasilkan pengontrol cacat dengan fungsi yang tidak memadai untuk pengujian yang tepat. Ini adalah indikasi yang sangat baik bahwa fungsi AccountControlleradalah subdivisi terkecil yang masuk akal.

Dan secara umum, pendekatan "pikirkan pengujian" akan bekerja tidak hanya dalam skenario khusus ini, tetapi dalam skenario serupa yang Anda temui di masa depan.


1

Jawabannya tidak begitu jelas

Tolong izinkan saya mengklarifikasi beberapa hal sebelum saya akan membuat pernyataan menjawab. Pertama-tama:

Apa pengontrolnya?

Pengontrol adalah bagian dari sistem yang mengontrol permintaan - setelah pengiriman. Dengan demikian, kita dapat mendefinisikannya sebagai serangkaian tindakan yang terkait dengan ... apa?

Apa ruang lingkup pengontrol?

Dan itu kurang lebih bagian ketika kita akan memiliki jawaban. Bagaimana menurut anda? Apakah pengontrol hal-hal (misalnya Akun) atau pengontrol tindakan? Tentu saja itu adalah pengontrol dari beberapa model atau beberapa hal yang lebih abstrak yang memberikan tindakan di atasnya.

Jawabannya adalah...

AuthenticationController dengan tindakan:

  • index () untuk menampilkan formulir login.
  • kirim () untuk menangani pengiriman formulir.
  • logout (), cukup jelas.

Nah, otentikasi adalah suatu proses. Jangan seperti itu.

LoginController dengan aksi:

  • index () untuk menampilkan formulir login.
  • kirim () untuk menangani pengiriman formulir.

Sama disini. Login - aksi. Lebih baik tidak membuat action controller (Anda tidak memiliki model yang berhubungan dengannya).

AccountController dengan tindakan:

  • loginGet () untuk menampilkan formulir login.
  • loginPost () untuk menangani pengiriman formulir login.
  • logoutGet () untuk menangani keluar.
  • registerGet () untuk menampilkan formulir pendaftaran.
  • registerPost () untuk menangani pengiriman formulir.

Cukup bagus, tetapi saya tidak yakin bahwa membangun pengontrol tingkat rendah (pengontrol adalah abstraksi itu sendiri) layak untuk dibawa. Lagi pula, membuat metode dengan * Dapatkan atau * Posting tidak jelas.

Ada saran?

Ya, pertimbangkan:

AccountController:

  • masuk (ModelModel)
  • logout (AccountModel)
  • daftar (Model Akun)
  • indeks()

Dan model terkait dengan itu, ofc kelas Akun. Ini akan memberi Anda kesempatan untuk memindahkan pasangan model-controller Anda di tempat lain (jika perlu) dan membuat kode yang jelas (jelas apa login()artinya metode ini). Stincking to model sangat terkenal terutama dengan aplikasi CRUD dan mungkin itu cara untuk Anda.


1

Pengontrol biasanya dibuat untuk sumber daya tertentu (kelas entitas, tabel dalam database), tetapi juga dapat dibuat untuk mengelompokkan tindakan bersama yang bertanggung jawab dengan bagian tertentu dari aplikasi. Dalam contoh Anda, itu akan menjadi pengontrol yang menangani keamanan untuk aplikasi:

class SecurityController
{
    // can handle both the login page display and
    // the login page submission
    login(); 

    logout();

    register();

    // optional: confirm account after registration
    confirm();

    // displays the forgot password page
    forgotPassword();

    // displays the reset password page
    // and handle the form submission
    resetPassword();
}

Catatan : jangan letakkan tindakan terkait keamanan dan tindakan profil pengguna di pengontrol yang sama; mungkin masuk akal karena terkait dengan pengguna, tetapi yang satu harus menangani otentikasi dan yang lain harus menangani email, nama, dll. pembaruan.

Dengan pengontrol yang dibuat untuk sumber daya (katakanlah Task), Anda akan memiliki tindakan CRUD seperti biasa :

class TasksController
{
    // usually displays a paginated list of tasks
    index();

    // displays a certain task, based on an identifier
    show(id);

    // displays page with form and
    // handles form submission for creating
    // new tasks
    create();

    // same as create(), but for changing records
    update(id);     

    // displays confirmation message
    // and handles submissions in case of confirmation
    delete()
}

Tentu saja, Anda memiliki kemungkinan untuk menambahkan sumber daya terkait ke pengontrol yang sama. Katakan misalnya Anda memiliki entitas Business, dan masing-masing memiliki beberapa BusinessServiceentitas. Pengontrol untuk itu mungkin terlihat seperti ini:

class BusinessController
{
    index();

    show(id);

    create();

    update(id);

    delete();

    // display the business services for a certain business
    listBusinessServices(businessId);

    // displays a certain business service
    showBusinessService(id);

    // create a new business service for a certain business
    createBusinessService(businessId);

    // updates a certain business service
    updateBusinessService(id);

    // deletes a certain business service
    deleteBusinessService(id);
}

Pendekatan ini masuk akal ketika entitas anak terkait tidak dapat ada tanpa entitas induk.

Ini adalah rekomendasi saya:

  • membuat pengontrol berdasarkan pada sekelompok operasi terkait (menangani tanggung jawab tertentu seperti keamanan, atau operasi CRUD pada sumber daya dll.);
  • untuk pengontrol berbasis sumber daya, jangan tambahkan tindakan yang tidak perlu (jika Anda tidak seharusnya memperbarui sumber daya, jangan tambahkan tindakan pembaruan);
  • Anda dapat menambahkan tindakan "kustom" untuk menyederhanakan hal-hal (misalnya Anda memiliki Subscriptionentitas yang memiliki ketersediaan berdasarkan jumlah entri yang terbatas, Anda dapat menambahkan tindakan baru ke pengontrol yang bernama use()yang memiliki tujuan tunggal untuk mengurangi satu entri dari Subscription)
  • sederhanakan hal-hal - jangan mengacaukan controller Anda dengan sejumlah besar tindakan dan logika kompleks, cobalah untuk menyederhanakan hal-hal dengan mengurangi jumlah aksi atau membuat dua controller;
  • jika Anda menggunakan kerangka kerja terfokus MVC, ikuti panduan praktik terbaiknya (jika ada).

Beberapa sumber untuk dibaca lebih lanjut di sini .


0

Saya melihat dua "kekuatan" desain antagonis (yang tidak eksklusif untuk pengendali):

  • kekompakan - pengendali harus mengelompokkan tindakan terkait
  • kesederhanaan - pengendali harus sekecil mungkin untuk mengelola kompleksitasnya

Dari sudut pandang keterpaduan, ketiga tindakan (login, logout, registrasi) terkait, tetapi login & logout lebih dari sekadar registrasi. Mereka terkait secara semantik (satu adalah inversi yang lain) dan sangat mungkin juga akan menggunakan objek layanan yang sama (implementasi mereka juga kohesif).

Instict pertama saya adalah mengelompokkan login dan logout menjadi satu controller. Tetapi jika implementasi pengontrol login & logout tidak begitu sederhana (mis. Login memiliki captcha, lebih banyak metode otentikasi, dll.), Saya tidak akan memiliki masalah untuk membaginya menjadi LoginController dan LogoutController untuk menjaga kesederhanaan. Di mana ambang kompleksitas ini (ketika Anda harus mulai memisahkan controller) terletak agak pribadi.

Juga ingat, bahwa apa pun yang Anda rancang kode Anda pada awalnya, Anda dapat (dan harus) memperbaikinya saat ia berubah. Dalam hal ini cukup umum untuk memulai dengan desain sederhana (miliki satu AuthenticationController) dan seiring waktu Anda akan menerima lebih banyak persyaratan yang akan menyulitkan kode. Setelah melewati ambang kompleksitas, Anda harus mengatur ulangnya menjadi dua pengontrol.

BTW, kode Anda menunjukkan bahwa Anda keluar pengguna dengan permintaan GET. Itu ide yang buruk karena HTTP GET harus nullipotent (seharusnya tidak mengubah keadaan aplikasi).


0

Berikut adalah beberapa aturan praktis:

  • Atur berdasarkan subjek atau topik, dengan nama pengontrol menjadi nama topik.

  • Ingat bahwa nama pengontrol akan muncul di URL, terlihat oleh pengguna Anda, jadi sebaiknya masuk akal bagi mereka.

Dalam situasi yang Anda sebutkan (otentikasi) tim MVC telah menulis controller untuk Anda. Buka Visual Studio 2013 lalu klik

File / New / Project... 
Search installed templates for "ASP.NET MVC4 Web Application"
Choose "Internet Application" / OK.

AccountController.cs berisi semua metode untuk mengelola akun pengguna:

Login()
Logoff()
Register()
Disassociate()
Manage()
ExternalLogin()

Jadi mereka telah diatur oleh topik "Akun pengguna dan otentikasi", dengan nama topik yang terlihat "Akun".


0

Terminologi

Saya percaya ini adalah kesalahpahaman besar untuk memanggil kelas yang berisi beberapa metode yang berhubungan dengan HTTP sebagai "pengontrol".

Pengontrol adalah metode yang menangani permintaan, tetapi bukan kelas yang berisi metode tersebut . Jadi, index(), submit(), logout()pengendali.

Kelas yang mengandung metode semacam itu dinamai "controller" hanya karena itu merupakan sekelompok pengendali, dan memainkan peran namespace "tingkat bawah". Dalam bahasa FP (seperti Haskell) itu hanya akan menjadi modul. Merupakan praktik yang baik untuk menjaga agar kelas "controller" tersebut tidak memiliki kewarganegaraan mungkin dalam bahasa OOP, kecuali referensi ke layanan dan hal-hal lain di seluruh program.

Jawabannya

Dengan terminologi yang diurutkan, pertanyaannya adalah "bagaimana kita harus memisahkan pengontrol menjadi ruang nama / modul?" Saya pikir jawabannya adalah: pengontrol di dalam namespace / modul tunggal harus berurusan dengan jenis data yang sama . Misalnya, UserControllerberurusan terutama dengan instance Userkelas, tetapi sesekali menyentuh hal-hal terkait lainnya, jika diperlukan.

Karena login, logoutdan tindakan semacam itu sebagian besar berhubungan dengan sesi, mungkin yang terbaik adalah menempatkannya di dalam SessionController, dan indexpengontrol, yang hanya mencetak formulir, harus ditempatkan ke dalam LoginPageController, karena jelas berhubungan dengan halaman login. Masuk akal sedikit untuk menempatkan rendering HTML dan manajemen sesi ke dalam satu kelas, dan itu akan melanggar SRP dan mungkin banyak praktik baik lainnya.

Prinsip umum

Saat Anda kesulitan menentukan di mana harus meletakkan sepotong kode, mulailah dengan data (dan jenis) yang Anda tangani.


2
Maaf, itu adalah Aksi, bukan Pengendali :)
JK01

@ JK01 Itulah yang Anda sebut mereka. Itu terminologi, Anda tahu. Dan ada kerangka kerja yang menyebut fungsi-fungsi itu "pengendali" (atau "penangan"), karena ada banyak kerangka kerja yang tidak mengaturnya ke dalam kelas, karena ruang nama / modul sudah cukup. Anda dapat menggunakan istilah apa pun yang Anda suka, itu hanya kata-kata, tetapi saya pikir memiliki istilah yang lebih sedikit lebih baik.
scriptin
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.