วิธีการตรวจสอบสิ่งที่ควรได้รับตัวควบคุมที่เกี่ยวข้องของตัวเอง?


10

ฉันใช้รูปแบบ MVC ในเว็บแอปพลิเคชันของฉันซึ่งสร้างด้วย PHP

ฉันมักจะดิ้นรนเพื่อตรวจสอบว่าฉันต้องการตัวควบคุมเฉพาะใหม่สำหรับชุดของการกระทำหรือถ้าฉันควรวางไว้ในตัวควบคุมที่มีอยู่แล้ว

มีกฎง่ายๆที่ควรปฏิบัติเมื่อสร้างคอนโทรลเลอร์หรือไม่?

ตัวอย่างเช่นฉันสามารถมี:

AuthenticationController ด้วยการกระทำ:

  • index() เพื่อแสดงแบบฟอร์มเข้าสู่ระบบ
  • submit() เพื่อจัดการกับการส่งแบบฟอร์ม
  • logout()อธิบายตนเอง

หรือ

LoginController ด้วยการกระทำ:

  • index() เพื่อแสดงแบบฟอร์มเข้าสู่ระบบ
  • submit() เพื่อจัดการกับการส่งแบบฟอร์ม

LogoutController ด้วยการกระทำ:

  • index() เพื่อจัดการการออกจากระบบ

หรือ

AccountController ด้วยการกระทำ:

  • loginGet() เพื่อแสดงแบบฟอร์มเข้าสู่ระบบ
  • loginPost() เพื่อจัดการกับการส่งแบบฟอร์มเข้าสู่ระบบ
  • logoutGet() เพื่อจัดการการออกจากระบบ
  • registerGet() เพื่อแสดงแบบฟอร์มลงทะเบียน
  • registerPost() เพื่อจัดการกับการส่งแบบฟอร์ม

    และการกระทำอื่น ๆ ที่เกี่ยวข้องกับบัญชี


อาจดูการออกแบบ RESTful มันไม่ได้แก้ปัญหาแบบนี้ทุกอย่าง แต่ให้ทิศทางที่ดีแก่คุณเกี่ยวกับวิธีคิด
thorsten müller

คำตอบ:


3

เพื่อที่จะหาการจัดกลุ่มที่เหมาะสมสำหรับควบคุมคิดของการทดสอบ

(แม้ว่าคุณจะไม่ทำการทดสอบใด ๆ จริง ๆ ลองนึกดูว่าคุณจะทดสอบการควบคุมของคุณอย่างไรจะให้ข้อมูลเชิงลึกที่ดีเกี่ยวกับวิธีการจัดโครงสร้าง)

An AuthenticationControllerไม่สามารถทดสอบได้ด้วยตัวเองเพราะมันมีเพียงฟังก์ชันการทำงานสำหรับการเข้าสู่ระบบและออกจากระบบ แต่รหัสการทดสอบของคุณจะต้องสร้างบัญชีปลอมเพื่อการทดสอบก่อนจึงจะสามารถทดสอบการเข้าสู่ระบบได้สำเร็จ คุณสามารถข้ามระบบย่อยที่อยู่ภายใต้การทดสอบและไปที่แบบจำลองของคุณเพื่อสร้างบัญชีการทดสอบ แต่คุณจะมีการทดสอบที่เปราะบางในมือของคุณ: หากแบบจำลองมีการเปลี่ยนแปลงคุณจะต้องแก้ไขรหัสที่ไม่เพียงทดสอบ รูปแบบ แต่ยังรหัสที่ทดสอบตัวควบคุมแม้ว่าอินเทอร์เฟซและลักษณะการทำงานของตัวควบคุมยังคงไม่เปลี่ยนแปลง มันไม่มีเหตุผล

A LoginControllerไม่เหมาะสมด้วยเหตุผลเดียวกัน: คุณไม่สามารถทดสอบได้โดยไม่ต้องสร้างบัญชีก่อนและมีสิ่งอื่น ๆ อีกมากมายที่คุณไม่สามารถทดสอบได้ตัวอย่างเช่นการป้องกันการลงชื่อเข้าใช้ซ้ำ แต่จากนั้นอนุญาตให้ผู้ใช้เข้าสู่ระบบหลังจากออกจากระบบ (เนื่องจากคอนโทรลเลอร์นี้ไม่มีฟังก์ชันการออกจากระบบ)

AccountControllerจะให้ทุกสิ่งที่คุณต้องการในการดำเนินการทดสอบของคุณ: คุณสามารถสร้างบัญชีทดสอบแล้วพยายามที่จะเข้าสู่ระบบคุณสามารถลบบัญชีแล้วให้แน่ใจว่าคุณจะไม่สามารถเข้าสู่ระบบอีกต่อไปคุณสามารถเปลี่ยนรหัสผ่านและทำให้แน่ใจว่า ต้องใช้รหัสผ่านที่ถูกต้องเพื่อเข้าสู่ระบบ ฯลฯ

เพื่อสรุป: เพื่อที่จะเขียนแม้แต่ชุดทดสอบที่เล็กที่สุดคุณจะต้องทำให้ฟังก์ชั่นทั้งหมดของAccountControllerมันพร้อมใช้งาน การแบ่งย่อยลงเป็นคอนโทรลเลอร์ขนาดเล็กดูเหมือนจะยอมให้ผู้ควบคุมคนพิการที่มีฟังก์ชั่นไม่เพียงพอสำหรับการทดสอบที่เหมาะสม นี่เป็นข้อบ่งชี้ที่ดีมากว่าฟังก์ชั่นการทำงานของAccountControllerแผนกย่อยที่เล็กที่สุดนั้นสมเหตุสมผล

และโดยทั่วไปแล้ววิธีการ "คิดถึงการทดสอบ" จะไม่เพียง แต่ในสถานการณ์เฉพาะนี้เท่านั้น แต่ในสถานการณ์ที่คล้ายกันที่คุณเจอในอนาคต


1

คำตอบนั้นไม่ชัดเจน

โปรดให้ฉันอธิบายบางสิ่งก่อนที่ฉันจะทำคำตอบใด ๆ ก่อนอื่น:

คอนโทรลเลอร์คืออะไร?

ตัวควบคุมเป็นส่วนหนึ่งของระบบที่ควบคุมการร้องขอ - หลังจากการจัดส่ง ดังนั้นเราสามารถกำหนดเป็นชุดการกระทำบางอย่างที่เกี่ยวข้องกับ ... อะไร

ขอบเขตของตัวควบคุมคืออะไร?

และนั่นก็เป็นส่วนที่มากหรือน้อยเมื่อเราจะได้คำตอบใด ๆ คุณคิดอย่างไร? มันเป็นสิ่งควบคุม (เช่นบัญชี) หรือตัวควบคุมการกระทำ แน่นอนมันเป็นตัวควบคุมของบางรุ่นหรือบางสิ่งที่เป็นนามธรรมที่ให้การกระทำกับมัน

คำตอบคือ...

AuthenticationController พร้อมแอ็คชัน:

  • ดัชนี () เพื่อแสดงแบบฟอร์มเข้าสู่ระบบ
  • submit () เพื่อจัดการกับการส่งแบบฟอร์ม
  • ออกจากระบบ () อธิบายตนเอง

ไม่รับรองความถูกต้องเป็นกระบวนการ อย่าไปทางนั้น

LoginController พร้อมการกระทำ:

  • ดัชนี () เพื่อแสดงแบบฟอร์มเข้าสู่ระบบ
  • submit () เพื่อจัดการกับการส่งแบบฟอร์ม

กันที่นี่ เข้าสู่ระบบ - การกระทำ ดีกว่าอย่าสร้างตัวควบคุมการกระทำ (คุณไม่มีรูปแบบที่สัมพันธ์กัน)

AccountController พร้อมการกระทำ:

  • loginGet () เพื่อแสดงแบบฟอร์มเข้าสู่ระบบ
  • loginPost () เพื่อจัดการกับการส่งแบบฟอร์มเข้าสู่ระบบ
  • logoutGet () เพื่อจัดการการออกจากระบบ
  • registerGet () เพื่อแสดงแบบฟอร์มลงทะเบียน
  • registerPost () เพื่อจัดการกับการส่งแบบฟอร์ม

ค่อนข้างดี แต่ฉันไม่เชื่อว่าการสร้างตัวควบคุมระดับต่ำ อย่างไรก็ตามการสร้างวิธีที่มี * Get หรือ * Post ไม่ชัดเจน

ข้อเสนอแนะใด ๆ

ใช่พิจารณา:

AccountController:

  • เข้าสู่ระบบ (AccountModel)
  • ออกจากระบบ (AccountModel)
  • ลงทะเบียน (AccountModel)
  • ดัชนี()

และรูปแบบที่เกี่ยวข้องกับมันของ class บัญชี มันจะให้โอกาสคุณในการย้ายคู่คอนโทรลเลอร์โมเดลของคุณไปที่อื่น (ถ้าจำเป็น) และสร้างโค้ดที่ชัดเจน (ชัดเจนว่ามันlogin()หมายถึงวิธีการ) การทำโมเดลให้มีชื่อเสียงจริงๆโดยเฉพาะอย่างยิ่งกับแอปพลิเคชั่น CRUD และอาจเป็นวิธีสำหรับคุณ


1

โดยปกติแล้วตัวควบคุมจะถูกสร้างขึ้นสำหรับทรัพยากรบางอย่าง (คลาสเอนทิตี, ตารางในฐานข้อมูล) แต่ยังสามารถสร้างขึ้นเพื่อจัดกลุ่มการดำเนินการร่วมกันที่รับผิดชอบกับส่วนหนึ่งของแอปพลิเคชัน ในตัวอย่างของคุณนั่นจะเป็นตัวควบคุม aa ที่จัดการความปลอดภัยสำหรับแอปพลิเคชัน:

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

หมายเหตุ : อย่าใส่การดำเนินการที่เกี่ยวข้องกับความปลอดภัยและการกระทำของโพรไฟล์ผู้ใช้ในตัวควบคุมเดียวกัน มันอาจสมเหตุสมผลเนื่องจากเกี่ยวข้องกับผู้ใช้ แต่ควรจัดการการรับรองความถูกต้องและอื่น ๆ ควรจัดการอีเมลชื่อ ฯลฯ การปรับปรุง

ด้วยตัวควบคุมที่สร้างขึ้นสำหรับทรัพยากร (สมมติว่าTask) คุณจะมีการกระทำCRUDตามปกติ:

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

แน่นอนคุณมีความเป็นไปได้ที่จะเพิ่มทรัพยากรที่เกี่ยวข้องลงในคอนโทรลเลอร์เดียวกัน พูดเช่นคุณมีเอนทิตีBusinessและแต่ละอันมีBusinessServiceเอนทิตีหลายแห่ง ตัวควบคุมอาจมีลักษณะเช่นนี้:

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

วิธีการนี้เหมาะสมเมื่อเอนทิตีลูกที่เกี่ยวข้องไม่สามารถอยู่ได้หากไม่มีเอนทิตีหลัก

นี่คือคำแนะนำของฉัน:

  • สร้างตัวควบคุมขึ้นอยู่กับกลุ่มของการดำเนินงานที่เกี่ยวข้อง (การจัดการความรับผิดชอบบางอย่างเช่นการรักษาความปลอดภัยหรือการดำเนินงาน CRUD ในทรัพยากร ฯลฯ );
  • สำหรับตัวควบคุมที่ใช้ทรัพยากรอย่าเพิ่มการกระทำที่ไม่จำเป็น (หากคุณไม่ควรอัปเดตทรัพยากรไม่ต้องเพิ่มการดำเนินการอัปเดต)
  • คุณสามารถเพิ่มการกระทำ "กำหนดเอง" เพื่อลดความซับซ้อนของสิ่งที่ (เช่นคุณมีSubscriptionหน่วยงานที่มีความพร้อมใช้งานขึ้นอยู่กับจำนวนที่ จำกัด ของรายการที่คุณสามารถเพิ่มการกระทำใหม่ไปยังตัวควบคุมที่มีชื่อuse()ที่มีวัตถุประสงค์เดียวในการลบหนึ่งรายการจากSubscription)
  • ทำให้สิ่งต่าง ๆ เป็นเรื่องง่าย - อย่าถ่วงตัวควบคุมของคุณด้วยการกระทำจำนวนมากและตรรกะที่ซับซ้อนพยายามทำให้สิ่งต่าง ๆ ง่ายขึ้นโดยลดจำนวนการกระทำหรือทำให้ตัวควบคุมสองตัว
  • หากคุณกำลังใช้เฟรมเวิร์กที่เน้น MVC ให้ทำตามแนวทางปฏิบัติที่ดีที่สุด (ถ้ามี)

แหล่งข้อมูลสำหรับการอ่านเพิ่มเติมที่นี่


0

ฉันเห็น "พลัง" สองอย่างที่เป็นปรปักษ์กัน (ซึ่งไม่ได้ จำกัด เฉพาะกับคอนโทรลเลอร์):

  • การทำงานร่วมกัน - ผู้ควบคุมควรจัดกลุ่มการกระทำที่เกี่ยวข้อง
  • ความเรียบง่าย - ตัวควบคุมควรมีขนาดเล็กที่สุดเท่าที่จะทำได้เพื่อจัดการความซับซ้อนของมัน

จากมุมมองที่อยู่ร่วมกันทั้งสามการกระทำ (เข้าสู่ระบบออกจากระบบการลงทะเบียน) เกี่ยวข้อง แต่การเข้าสู่ระบบและออกจากระบบมากกว่าการลงทะเบียน พวกเขามีความสัมพันธ์เชิงความหมาย (หนึ่งคือการผกผันของอื่น ๆ ) และค่อนข้างจะอาจจะใช้วัตถุบริการเดียวกัน (การใช้งานของพวกเขายังเหนียว)

instict แรกของฉันคือการเข้าสู่ระบบกลุ่มและออกจากระบบเป็นหนึ่งในตัวควบคุม แต่ถ้าการใช้งานตัวควบคุมการเข้าสู่ระบบและออกจากระบบไม่ง่าย (เช่นการเข้าสู่ระบบมี captcha วิธีการรับรองความถูกต้องเพิ่มเติม ฯลฯ ) ฉันจะไม่มีปัญหาในการแบ่งพวกเขาออกเป็น LoginController และ LogoutController เพื่อรักษาความเรียบง่าย ในกรณีที่เกณฑ์ความซับซ้อนนี้ (เมื่อคุณควรเริ่มแยกตัวควบคุม) การโกหกเป็นเรื่องเล็กน้อย

โปรดจำไว้ว่าสิ่งที่คุณออกแบบรหัสของคุณเริ่มแรกคุณสามารถ (และควร) ปรับโครงสร้างอีกครั้งเมื่อมีการเปลี่ยนแปลง ในกรณีนี้มันค่อนข้างปกติที่จะเริ่มต้นด้วยการออกแบบที่เรียบง่าย (มี AuthenticationController หนึ่งอัน) และเมื่อเวลาผ่านไปคุณจะได้รับข้อกำหนดเพิ่มเติมซึ่งจะทำให้รหัสซับซ้อน เมื่อข้ามขีดจำกัดความซับซ้อนคุณควรปรับโครงสร้างให้เป็นสองตัวควบคุม

BTW รหัสของคุณแนะนำว่าคุณกำลังออกจากระบบผู้ใช้ด้วยคำขอ GET เป็นความคิดที่ไม่ดีเนื่องจาก HTTP GET ควรเป็น nullipotent (ไม่ควรแก้ไขสถานะของแอปพลิเคชัน)


0

นี่เป็นกฎง่ายๆ

  • จัดระเบียบตามหัวเรื่องหรือหัวข้อโดยชื่อคอนโทรลเลอร์เป็นชื่อของหัวข้อ

  • โปรดจำไว้ว่าชื่อของตัวควบคุมจะปรากฏใน URL ซึ่งปรากฏแก่ผู้ใช้ของคุณดังนั้นจึงควรมีเหตุผล

ในสถานการณ์ที่คุณพูดถึง (การรับรองความถูกต้อง) ทีม MVC ได้เขียนคอนโทรลเลอร์สำหรับคุณแล้ว เปิด Visual Studio 2013 แล้วคลิกตกลง

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

AccountController.cs มีวิธีการทั้งหมดสำหรับการจัดการบัญชีผู้ใช้:

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

ดังนั้นพวกเขาจึงจัดระเบียบตามหัวข้อ "บัญชีผู้ใช้และการรับรองความถูกต้อง" โดยมีชื่อหัวข้อว่า "บัญชี"


0

คำศัพท์

ฉันเชื่อว่าเป็นความเข้าใจผิดที่ยิ่งใหญ่ที่จะเรียกคลาสที่มีวิธีการที่เกี่ยวข้องกับ HTTP บางอย่างเป็น "คอนโทรลเลอร์"

Controller เป็นวิธีการที่จับขอ แต่ไม่ได้ชั้นที่มีวิธีการดังกล่าว ดังนั้นindex(), submit(), logout()ตัวควบคุม

คลาสที่ประกอบด้วยเมธอดนั้นมีชื่อว่า "controller" เพียงเพราะมันประกอบไปด้วยกลุ่มของคอนโทรลเลอร์และกำลังเล่นบทบาทของเนมสเปซ "ระดับล่าง" ในภาษา FP (เช่น Haskell) มันจะเป็นแค่โมดูล มันเป็นแนวปฏิบัติที่ดีในการรักษาคลาส "คอนโทรลเลอร์" เหล่านั้นให้เป็นไร้สัญชาติมากที่สุดในภาษา OOP ยกเว้นการอ้างอิงไปยังบริการและเนื้อหาอื่น ๆ ของโปรแกรม

คำตอบ

ด้วยคำศัพท์ที่แยกออกคำถามคือ"เราจะแยกคอนโทรลเลอร์ออกเป็นเนมสเปซ / โมดูลได้อย่างไร" ผมคิดว่าคำตอบคือ: ควบคุมภายในเดียว namespace / โมดูลควรจะจัดการกับชนิดเดียวกันของข้อมูล ตัวอย่างเช่นUserControllerข้อตกลงเป็นหลักกับอินสแตนซ์ของUserคลาส แต่บางครั้งก็สัมผัสสิ่งที่เกี่ยวข้องอื่น ๆ ถ้าจำเป็น

ตั้งแต่login, logoutและการดำเนินการอื่น ๆ เช่นส่วนใหญ่จะจัดการกับเซสชั่นก็น่าจะดีที่สุดที่จะนำพวกเขาภายในSessionControllerและindexควบคุมซึ่งเพียงแค่พิมพ์แบบฟอร์มที่ควรจะวางลงLoginPageControllerเพราะมันเห็นได้ชัดว่ามีข้อตกลงกับหน้าเข้าสู่ระบบ การใช้การเรนเดอร์ HTML และการจัดการเซสชั่นลงในคลาสเดียวนั้นมีความหมายเพียงเล็กน้อยซึ่งอาจเป็นการละเมิดSRPและอาจเป็นวิธีปฏิบัติที่ดีอื่น ๆ

หลักการทั่วไป

เมื่อคุณมีปัญหาในการตัดสินใจว่าจะใส่รหัสชิ้นใดให้เริ่มด้วยข้อมูล (และประเภท) ที่คุณจัดการ


2
ขออภัยนั่นคือการกระทำไม่ใช่ตัวควบคุม :)
JK01

@ JK01 นี่คือสิ่งที่คุณเรียกพวกเขา มันเป็นศัพท์ที่คุณรู้ และมีเฟรมเวิร์กที่เรียกฟังก์ชันเหล่านี้ว่า "controllers" (หรือ "handler") เนื่องจากมีเฟรมเวิร์กมากมายที่ไม่ได้จัดระเบียบเหล่านั้นไว้ในคลาสเนื่องจากเนมสเปซ / โมดูลเพียงพอแล้ว คุณสามารถใช้คำใด ๆ ที่คุณชอบมันเป็นเพียงคำพูด แต่ฉันคิดว่าการมีเงื่อนไขน้อยกว่าจะดีกว่า
scriptin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.