วิธีการจัดโครงสร้างโครงการใน winform อย่างถูกต้อง?


26

เมื่อไม่นานมานี้ฉันเริ่มสร้างแอปพลิเคชั่น winform และในเวลานั้นมันมีขนาดเล็กและฉันก็ไม่ได้คิดว่าจะจัดโครงสร้างโครงการอย่างไร

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

วิธีการปรับโครงสร้างโฟลเดอร์โครงการอย่างเหมาะสม?

ในขณะนี้ฉันกำลังนึกถึงบางสิ่งเช่นนี้:

  • สร้างโฟลเดอร์สำหรับแบบฟอร์ม
  • สร้างโฟลเดอร์สำหรับคลาสยูทิลิตี้
  • สร้างโฟลเดอร์สำหรับคลาสที่มีข้อมูลเท่านั้น

หลักการตั้งชื่อคืออะไรเมื่อเพิ่มคลาส?

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

สิ่งที่ต้องทำเพื่อไม่ให้รหัสทั้งหมดสำหรับฟอร์มหลักสิ้นสุดลงใน Form1.cs

ปัญหาอีกประการหนึ่งที่ฉันพบคือเมื่อฟอร์มหลักมีขนาดใหญ่ขึ้นตามคุณลักษณะที่ฉันเพิ่มแต่ละไฟล์รหัส (Form1.cs) เริ่มมีขนาดใหญ่มาก ฉันมีตัวอย่างเช่น TabControl และแต่ละแท็บมีพวงของการควบคุมและรหัสทั้งหมดลงเอยใน Form1.cs จะหลีกเลี่ยงสิ่งนี้ได้อย่างไร

นอกจากนี้คุณรู้บทความหรือหนังสือที่จัดการกับปัญหาเหล่านี้หรือไม่

คำตอบ:


24

ดูเหมือนว่าคุณจะได้พบกับข้อผิดพลาดทั่วไป แต่ไม่ต้องกังวลพวกเขาสามารถแก้ไขได้ :)

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

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

เคล็ดลับที่แท้จริงที่นี่คือวิธีที่คุณจัดการการสื่อสารระหว่างตัวควบคุม คุณไม่ต้องการให้ผู้ใช้ควบคุม 30 คนในแบบฟอร์มทั้งหมดถือการอ้างอิงซึ่งกันและกันแบบสุ่มและวิธีการโทรกับพวกเขา

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

ดังนั้นการใช้แอปตัวอย่างของเราอินเทอร์เฟซแรกสำหรับการควบคุมการโฮสต์กล่องรายการของผู้ใช้จะรวมถึงเหตุการณ์ที่เรียกว่า UserChanged ซึ่งส่งผ่านวัตถุผู้ใช้ออก

นี่เป็นสิ่งที่ดีเพราะตอนนี้ถ้าคุณเบื่อกล่องรายการและต้องการการควบคุมตาวิเศษสามมิติ zoomy คุณเพียงแค่กำหนดมันให้เป็นอินเทอร์เฟซเดียวกันและเสียบมันเข้าไป :)

ตกลงดังนั้นตอนที่สองแยกตรรกะ UI จากตรรกะโดเมน นี่เป็นเส้นทางที่ล้าสมัยและฉันขอแนะนำให้คุณดูรูปแบบ MVP ที่นี่ มันง่ายมาก

ขณะนี้การควบคุมแต่ละรายการเรียกว่า View (V ใน MVP) และเราได้กล่าวถึงสิ่งที่จำเป็นข้างต้นแล้ว ในกรณีนี้การควบคุมและอินเตอร์เฟซสำหรับมัน

สิ่งที่เราเพิ่มคือโมเดลและผู้นำเสนอ

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

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

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

ดังนั้นผู้นำเสนอจะเปิดเผยวิธีการเช่น ListOfAllUsers ซึ่งมุมมองจะใช้เพื่อรับรายการผู้ใช้หรือคุณสามารถใส่วิธี AddUser เป็นมุมมองและเรียกสิ่งนั้นจากผู้นำเสนอ ฉันชอบอันหลัง ด้วยวิธีนี้ผู้นำเสนอสามารถเพิ่มผู้ใช้ไปยังกล่องรายการเมื่อต้องการ

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

ดังนั้นโดยสรุปให้ใช้ MVP Microsoft ให้สิ่งที่เรียกว่า SCSF (โรงงานซอฟต์แวร์ไคลเอนต์สมาร์ท) ซึ่งใช้ MVP ในแบบที่ฉันอธิบาย มันทำสิ่งอื่น ๆ มากมายเช่นกัน มันค่อนข้างซับซ้อนและฉันไม่ชอบวิธีที่พวกเขาทำทุกอย่าง แต่อาจช่วยได้


8

โดยส่วนตัวฉันชอบแยกความกังวลที่แตกต่างกันระหว่างชุดประกอบหลายชุดแทนที่จะรวมทุกอย่างเข้าด้วยกันเป็นชุดปฏิบัติการเดียว

โดยทั่วไปฉันต้องการเก็บรหัสปริมาณน้อยที่สุดไว้ที่จุดเข้าใช้งานของแอปพลิเคชัน - ไม่มีตรรกะทางธุรกิจไม่มีรหัส GUI และไม่มีการเข้าถึงข้อมูล (การเข้าถึงฐานข้อมูล / ไฟล์ / การเชื่อมต่อเครือข่าย / ฯลฯ ); ฉันมักจะ จำกัด รหัสจุดเริ่มต้น (เช่นปฏิบัติการ) เพื่อบางสิ่งบางอย่างตามสายของ

  • การสร้างและการเริ่มต้นส่วนประกอบแอปพลิเคชันที่หลากหลายจากแอสเซมบลีที่ขึ้นต่อกันทั้งหมด
  • การกำหนดค่าองค์ประกอบบุคคลที่สามซึ่งแอปพลิเคชันทั้งหมดขึ้นอยู่กับ (เช่น Log4Net สำหรับเอาต์พุตการวินิจฉัย)
  • นอกจากนี้ฉันอาจจะรวมถึง "จับข้อยกเว้นทั้งหมดและบันทึกการสืบค้นกลับสแต็ก" ของรหัสประเภทในฟังก์ชั่นหลักซึ่งจะช่วยให้เข้าสู่ระบบสถานการณ์ของความล้มเหลวที่สำคัญ / ร้ายแรงที่ไม่เหมาะสม

สำหรับองค์ประกอบของแอปพลิเคชันตัวเองฉันมักจะตั้งเป้าไว้อย่างน้อยสามแอปพลิเคชันขนาดเล็ก

  • Data Access Layer (การเชื่อมต่อฐานข้อมูลการเข้าถึงไฟล์และอื่น ๆ ) - ขึ้นอยู่กับความซับซ้อนของข้อมูลที่เก็บถาวร / ที่ใช้โดยแอปพลิเคชันอาจมีชุดประกอบเหล่านี้หลายชุด - ฉันอาจสร้างชุดประกอบแยกต่างหากสำหรับการจัดการฐานข้อมูล แอสเซมบลีถ้าโต้ตอบกับฐานข้อมูลเกี่ยวข้องอะไรที่ซับซ้อน - เช่นถ้าคุณ stucx กับฐานข้อมูลที่ออกแบบมาไม่ดีคุณอาจจำเป็นต้องจัดการความสัมพันธ์ของฐานข้อมูลในรหัสดังนั้นจึงอาจเหมาะสมที่จะเขียนหลายโมดูลสำหรับการแทรกและดึงข้อมูล)

  • Logic Layer - "เนื้อ" หลักที่มีการตัดสินใจและอัลกอริทึมทั้งหมดที่ทำให้แอปพลิเคชันของคุณทำงานได้ การตัดสินใจเหล่านี้ไม่ควรรู้อะไรเลยเกี่ยวกับ GUI (ใครบอกว่ามี GUI) และไม่ควรรู้อะไรเลยเกี่ยวกับฐานข้อมูล (ใช่มั้ยมีฐานข้อมูลอยู่ทำไมไม่เป็นไฟล์) ลอจิกเลเยอร์ที่ออกแบบมาอย่างดีหวังว่าจะสามารถ "ดึงออก" และตกอยู่ในแอปพลิเคชันอื่นโดยไม่จำเป็นต้องรวบรวมใหม่ ในแอพพลิเคชั่นที่ซับซ้อนอาจมีชุดประกอบตรรกะเหล่านี้จำนวนมาก (เพราะคุณอาจต้องการที่จะตัด 'ส่วน' โดยไม่ต้องลากไปตามส่วนที่เหลือของแอปพลิเคชัน)

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

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

ที่จะนำมันไปอีกทาง; ถ้าฉันต้องการเขียนโปรแกรมอื่นที่ทำงานในลักษณะเดียวกับแอปพลิเคชันของคุณ แต่ใช้อินเตอร์เฟสบรรทัดคำสั่งหรือเว็บอินเตอร์เฟส; และสลับการจัดเก็บฐานข้อมูลสำหรับการจัดเก็บไฟล์ (หรืออาจเป็นฐานข้อมูลชนิดอื่น) จากนั้นฉันสามารถทำได้โดยไม่จำเป็นต้องสัมผัสตรรกะแอปพลิเคชันหลักเลย - ทั้งหมดที่ฉันต้องทำคือเขียนเครื่องมือบรรทัดคำสั่งเล็กน้อย และรูปแบบข้อมูลอื่นแล้ว "เสียบเข้าด้วยกัน" และฉันพร้อมที่จะไป

คุณอาจจะคิดว่า "ฉันไม่เคยต้องการทำสิ่งใดดังนั้นจึงไม่สำคัญว่าฉันจะสลับสิ่งเหล่านี้ไม่ได้" - จุดที่แท้จริงคือหนึ่งในจุดเด่นของโปรแกรมประยุกต์แบบแยกส่วนคือ ความสามารถในการแยก 'chunks' (โดยไม่จำเป็นต้องรวบรวมอะไรใหม่) และนำชิ้นส่วนเหล่านั้นกลับมาใช้ที่อื่น ในการเขียนโค้ดเช่นนี้โดยทั่วไปบังคับให้คุณคิดยาวและหนักแน่นเกี่ยวกับหลักการออกแบบ - คุณต้องคิดเกี่ยวกับการเขียนอินเทอร์เฟซมากขึ้นและคิดอย่างรอบคอบเกี่ยวกับการแลกเปลี่ยนหลักการโซลิดต่างๆ (เช่นเดียวกัน วิธีที่คุณต้องการสำหรับ Behavior-Driven-Development หรือ TDD)

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


4

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

หลักการตั้งชื่อคืออะไรเมื่อเพิ่มคลาส?

แน่นอนคุณต้องการแยกแต่ละชั้นนอกแบบฟอร์ม ฉันจะแนะนำยังไฟล์ต่อชั้น (แม้ว่า MS ไม่ได้ทำในรหัสที่สร้างขึ้นสำหรับ EF เช่น)

หลายคนใช้คำนามสั้น ๆ ที่มีความหมายเป็นพหูพจน์ (เช่นลูกค้า) ptrfer บางชื่อจะใกล้เคียงกับชื่อเอกฐานสำหรับตารางฐานข้อมูลที่สอดคล้องกัน (ถ้าคุณใช้การจับคู่ 1-1 ระหว่างวัตถุและตาราง)

สำหรับคลาสการตั้งชื่อมีหลายแหล่งตัวอย่างเช่นดูที่: . net อนุสัญญาการตั้งชื่อและมาตรฐานการเขียนโปรแกรม - แนวทางปฏิบัติที่ดีที่สุดและ / หรือแนวทางการเข้ารหัส STOVF-C #

สิ่งที่ต้องทำเพื่อไม่ให้รหัสทั้งหมดสำหรับฟอร์มหลักสิ้นสุดลงใน Form1.cs

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

เนื่องจากลักษณะการกระทำของ MS Windows Forms ไม่มีสิ่งใดที่ฉันรู้ว่าสามารถช่วยให้คุณนำโค้ดออกจากฟอร์มหลักโดยไม่ต้องเพิ่มความคลุมเครือและความพยายาม อย่างไรก็ตาม MVVM อาจเป็นทางเลือก (ในโครงการในอนาคต) ดูตัวอย่าง: MVVM สำหรับ Windows แบบฟอร์ม


2

พิจารณา MVP เป็นตัวเลือกเพราะจะช่วยให้คุณสามารถจัดระเบียบตรรกะการนำเสนอซึ่งเป็นทุกอย่างในแอปพลิเคชันธุรกิจขนาดใหญ่ ในชีวิตจริงแอพลอจิกส่วนใหญ่อยู่ในฐานข้อมูลดังนั้น rarelly คุณจำเป็นต้องเขียนเลเยอร์ธุรกิจด้วยรหัสเนทีฟจริง ๆ ทิ้งไว้เมื่อจำเป็นต้องมีฟังก์ชั่นการนำเสนอที่มีโครงสร้างอย่างดี

โครงสร้างที่เหมือน MVP จะส่งผลให้มีชุดของผู้นำเสนอหรือตัวควบคุมหากคุณต้องการซึ่งจะประสานงานกับแต่ละอื่น ๆ และจะไม่ผสมกับ UI หรือรหัสที่อยู่เบื้องหลังสิ่งของ คุณสามารถใช้ UIs ที่แตกต่างกับคอนโทรลเลอร์เดียวกันได้

ในที่สุดการจัดการทรัพยากรอย่างง่ายการแยกส่วนประกอบและระบุการพึ่งพากับ IoC และ DI พร้อมกับวิธีการ MVP จะช่วยให้คุณมีกุญแจสำหรับการสร้างระบบที่หลีกเลี่ยงข้อผิดพลาดและความซับซ้อนที่เกิดขึ้นตามเวลาและเปิดรับการเปลี่ยนแปลง


1

โครงสร้างของโครงการทั้งหมดขึ้นอยู่กับโครงการและขนาดของโครงการอย่างไรก็ตามคุณสามารถเพิ่มโฟลเดอร์ได้เล็กน้อยเช่น

  • ทั่วไป (มีคลาสที่มีประโยชน์เช่นยูทิลิตี้)
  • DataAccess (คลาสที่เกี่ยวข้องกับการเข้าถึงข้อมูลโดยใช้ SQL หรือเซิร์ฟเวอร์ฐานข้อมูลอื่น ๆ ที่คุณใช้)
  • รูปแบบ (หากคุณมีไฟล์ CSS ใด ๆ ในโครงการของคุณ)
  • ทรัพยากร (เช่นรูปภาพไฟล์ทรัพยากร)
  • WorkFlow (คลาสที่เกี่ยวข้องกับเวิร์กโฟลว์หากคุณมี)

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

แบบแผนการตั้งชื่อนั้นคล้ายกับว่าคลาสของคุณพิมพ์ข้อความ "Hello World" ดังนั้นชื่อคลาสควรเป็นสิ่งที่เกี่ยวข้องกับงานและชื่อที่เหมาะสมของคลาสนั้นควรจะเป็น HelloWorld.cs

คุณสามารถสร้างภูมิภาคได้เช่นกัน

#region Hello แล้วออกไป endregionในตอนท้าย

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

หนังสือ ERM

ไม่มีหนังสือใดบอกโครงสร้างของโครงการเนื่องจากแต่ละโครงการแตกต่างกันคุณเรียนรู้สิ่งเหล่านี้จากประสบการณ์

หวังว่ามันจะช่วย!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.