หลักการและโครงสร้างของโซลิด


150

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

คำถามสัมภาษณ์คือ:

หากคุณกำลังดูโครงการ. Net ที่ฉันบอกคุณว่าปฏิบัติตามหลักการของ SOLID อย่างเคร่งครัดคุณคาดหวังอะไรในแง่ของโครงการและโครงสร้างโค้ด

ฉันดิ้นรนนิดหน่อยไม่ตอบคำถามจริงๆแล้วก็ระเบิดออกมา

ฉันจะจัดการคำถามนี้ได้ดีขึ้นได้อย่างไร


3
ฉันสงสัยว่าสิ่งใดที่ไม่ชัดเจนในหน้าวิกิสำหรับ SOLID
BЈовић

ส่วนเสริมแบบนามธรรม
ร. ว.

ด้วยการทำตามหลักการของการออกแบบเชิงวัตถุ SOLID คลาสของคุณจะมีขนาดเล็กเป็นธรรมชาติและทดสอบได้ง่าย ที่มา: docs.asp.net/en/latest/fundamentals/…
WhileTrueSleep

คำตอบ:


188

S = หลักการความรับผิดชอบเดี่ยว

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

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

O = หลักการเปิด / ปิด

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

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

L = หลักการทดแทน Liskov

สิ่งนี้เกี่ยวข้องกับความสามารถในการจัดการกับชนิดย่อยเป็นชนิดพาเรนต์ สิ่งนี้ออกมาจากกล่องใน C # หากคุณใช้ลำดับชั้นวัตถุที่สืบทอดมาอย่างเหมาะสม

ฉันคาดหวังว่าจะเห็นรหัสปฏิบัติกับวัตถุทั่วไปเป็นประเภทพื้นฐานและวิธีการโทรในคลาสฐาน / นามธรรมมากกว่าการสร้างอินสแตนซ์และทำงานกับประเภทย่อยเอง

I = หลักการแยกส่วนต่อประสาน

คล้ายกับ SRP โดยทั่วไปคุณกำหนดย่อยขนาดเล็กของการทำงานเป็นอินเตอร์เฟซและการทำงานร่วมกับผู้ให้ระบบของคุณหลุดพ้น (เช่นFileManagerอาจมีความรับผิดชอบเดียวในการจัดการกับไฟล์ I / O แต่ที่สามารถดำเนินการIFileReaderและIFileWriterที่มีความหมายวิธีการเฉพาะสำหรับการอ่าน และการเขียนไฟล์)

D = หลักการผกผันของการพึ่งพา

อีกครั้งนี้เกี่ยวข้องกับการรักษาระบบแยกชิ้นส่วน บางทีคุณอาจจะต้องระวังสำหรับการใช้ห้องสมุดฉีด .NET อ้างอิงที่ถูกนำมาใช้ในการแก้ปัญหาเช่นUnityหรือNinjectหรือระบบ ServiceLocator AutoFacServiceLocatorเช่น


36
ฉันเห็นการละเมิด LSP มากมายใน C # ทุกครั้งที่มีคนตัดสินใจว่าชนิดย่อยของพวกเขานั้นมีความเฉพาะและดังนั้นจึงไม่จำเป็นต้องใช้อินเทอร์เฟซและส่งข้อยกเว้นบนชิ้นส่วนนั้นแทน ... นี่เป็นวิธีการทั่วไป เพื่อแก้ปัญหาการใช้อินเตอร์เฟซและการออกแบบที่เข้าใจผิด
จิมมี่ฮอฟฟา

2
@JimmyHoffa นั่นเป็นหนึ่งในเหตุผลหลักที่ฉันยืนยันในการใช้รหัสสัญญา การผ่านขั้นตอนความคิดในการออกแบบสัญญาช่วยให้ผู้คนจำนวนมากหลุดพ้นจากนิสัยที่ไม่ดีนั้น
Andy

12
ฉันไม่ชอบ "LSP ออกมาจากกล่องใน C #" และเทียบเคียงการฝึกฉีดพึ่งพา
ร่าเริง

3
+1 แต่การพึ่งพาการพึ่งพา <> การพึ่งพาการฉีด พวกเขาเล่นได้ดีด้วยกัน แต่การผกผันของการพึ่งพาอาศัยกันเป็นอะไรที่มากกว่าแค่การฉีดยาเสพติด การอ้างอิง: จุ่มในป่า
Marjan Venema

3
@Andy: สิ่งที่ช่วยได้เช่นกันคือการทดสอบหน่วยที่กำหนดไว้ในส่วนต่อประสานที่ซึ่งผู้ใช้งานทั้งหมด (คลาสใด ๆ ที่สามารถ / เป็นอินสแตนซ์) ได้รับการทดสอบ
Marjan Venema

17

คลาสเล็กและอินเทอร์เฟซขนาดเล็กจำนวนมากที่มีการฉีดขึ้นอยู่กับที่ อาจเป็นโครงการขนาดใหญ่คุณอาจใช้กรอบ IoC เพื่อช่วยคุณสร้างและจัดการอายุการใช้งานของวัตถุขนาดเล็กเหล่านั้นทั้งหมด ดูhttps://stackoverflow.com/questions/21288/which-net-dependency-injection-frameworks-are-worth-looking-into

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

คุณเห็นจะเป็นของแข็งคุณต้องทำตาม:

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

Oหลักการปิดปากกาซึ่งใน. NET มักจะใช้กับการฉีดพึ่งพาซึ่งยังต้องใช้ I และ D ด้านล่าง ...

Lหลักการ iskov เปลี่ยนตัวอาจเป็น inmpossible ที่จะอธิบายใน c # กับหนึ่งซับ โชคดีที่มีคำถามอื่น ๆ ที่กล่าวถึงเช่นhttps://stackoverflow.com/questions/4428725/can-you-explain-liskov-substitution-principle-with-a-good-c-sharp-example

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

Dหลักการ ependency inversion คลาสระดับสูงไม่ควรขึ้นอยู่กับคลาสระดับต่ำทั้งสองควรขึ้นอยู่กับ abstractions


SRP ไม่ได้หมายความว่า "ทำสิ่งเดียวเท่านั้น"
Robert Harvey

13

สิ่งพื้นฐานบางอย่างที่ฉันคาดหวังที่จะเห็นใน codebase ของร้านค้าที่ดำเนินการเกี่ยวกับ SOLID ในการทำงานประจำวัน:

  • ไฟล์โค้ดขนาดเล็กจำนวนมาก - ด้วยหนึ่งคลาสต่อไฟล์เป็นแนวปฏิบัติที่ดีที่สุดใน. NET และหลักการความรับผิดชอบเดี่ยวที่สนับสนุนโครงสร้างคลาสมอดูลาร์ขนาดเล็กฉันคาดหวังว่าจะเห็นไฟล์จำนวนมากแต่ละไฟล์
  • รูปแบบตัวแปลงและคอมโพสิตจำนวนมาก - ฉันคาดหวังว่าจะใช้รูปแบบตัวแปลงจำนวนมาก (คลาสที่ใช้อินเตอร์เฟสหนึ่งโดย "ส่งผ่าน" ไปยังฟังก์ชันของอินเทอร์เฟซที่แตกต่างกัน) เพื่อปรับปรุงการเสียบปลั๊กในการพัฒนา สถานที่ต่าง ๆ ที่จำเป็นในการใช้งาน อัปเดตง่าย ๆ เพียงแค่แทนที่ตัวบันทึกคอนโซลด้วยตัวบันทึกไฟล์จะละเมิด LSP / ISP / DIP หากอินเทอร์เฟซได้รับการอัปเดตเพื่อแสดงวิธีการระบุชื่อไฟล์ที่จะใช้ คลาสตัวบันทึกไฟล์จะแสดงสมาชิกเพิ่มเติมแทน แต่อะแด็ปเตอร์จะทำให้ตัวบันทึกไฟล์ดูเหมือนกับตัวบันทึกคอนโซลโดยการซ่อนสิ่งใหม่ดังนั้นเฉพาะวัตถุที่หักล้างทั้งหมดนี้เข้าด้วยกันจึงต้องทราบความแตกต่าง

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

  • อินเทอร์เฟซและ ABCs จำนวนมาก - DIP จำเป็นต้องมีบทคัดย่อที่มีอยู่และ ISP สนับสนุนให้สิ่งเหล่านี้มีขอบเขตที่แคบ ดังนั้นอินเทอร์เฟซและคลาสพื้นฐานที่เป็นนามธรรมจึงเป็นกฎและคุณจะต้องใช้จำนวนมากเพื่อครอบคลุมฟังก์ชันการพึ่งพาที่ใช้ร่วมกันของ codebase ของคุณ ในขณะที่ SOLID ที่เข้มงวดจะทำให้การฉีดทุกอย่างชัดเจนว่าคุณต้องสร้างที่ไหนสักแห่งและดังนั้นหากมีการสร้างรูปแบบ GUI เพียงครั้งเดียวในฐานะลูกของฟอร์มหลักเดียวโดยดำเนินการบางอย่างกับผู้ปกครองดังกล่าว จากรหัสโดยตรงภายในผู้ปกครอง ฉันมักจะทำให้รหัสนั้นเป็นวิธีการของตัวเองดังนั้นหากการกระทำสองอย่างของแบบฟอร์มเดียวกันเปิดหน้าต่างฉันจะเรียกวิธีนั้น
  • หลายโครงการ - ประเด็นทั้งหมดนี้คือการ จำกัด ขอบเขตของการเปลี่ยนแปลง การเปลี่ยนแปลงนั้นจำเป็นต้องคอมไพล์อีกครั้ง (เป็นการออกกำลังกายที่ไม่สำคัญอีกต่อไป แต่ยังคงมีความสำคัญในการทำงานของโปรเซสเซอร์และแบนด์วิดธ์ที่สำคัญเช่นการปรับใช้การอัปเดตไปยังสภาพแวดล้อมมือถือ) หากไฟล์หนึ่งไฟล์ในโครงการต้องถูกสร้างใหม่ไฟล์ทั้งหมดจะทำ นั่นหมายความว่าถ้าคุณวางอินเทอร์เฟซในไลบรารีเดียวกันกับการใช้งานของพวกเขาคุณจะพลาดจุดนั้น คุณจะต้องคอมไพล์การใช้งานทั้งหมดอีกครั้งหากคุณเปลี่ยนการใช้งานอินเทอร์เฟซเพราะคุณจะทำการคอมไพล์คำจำกัดความของอินเทอร์เฟซอีกครั้งโดยกำหนดให้การชี้ไปยังตำแหน่งใหม่ในไบนารีที่เกิดขึ้น ดังนั้นการรักษาอินเตอร์เฟซที่แยกต่างหากจากประเพณีและ การใช้งานในขณะที่ยังแยกพวกเขาตามพื้นที่การใช้งานทั่วไปเป็นแนวปฏิบัติที่ดีที่สุดโดยทั่วไป
  • ความสนใจมากมายที่จ่ายให้กับคำศัพท์ "Gang of Four" - รูปแบบการออกแบบที่ระบุไว้ในรูปแบบการออกแบบหนังสือในปี 1994 เน้นการออกแบบขนาดรหัสกัดที่โซลิดพยายามสร้าง ตัวอย่างเช่นหลักการผกผันของการพึ่งพาและหลักการเปิด / ปิดเป็นหัวใจสำคัญของรูปแบบที่ระบุส่วนใหญ่ในหนังสือเล่มนั้น เช่นนี้ฉันคาดหวังว่าร้านค้าที่ยึดมั่นในหลักการของ SOLID เพื่อที่จะโอบกอดศัพท์ในหนังสือของ Gang of Four และชื่อชั้นเรียนตามหน้าที่ของพวกเขาตามสายงานเหล่านั้นเช่น "AbcFactory", "XyzRepository", "DefToXyzAdapter" "," A1Command "ฯลฯ
  • พื้นที่เก็บข้อมูลทั่วไป - เพื่อให้สอดคล้องกับ ISP กรมทรัพย์สินทางปัญญาและ SRP ตามที่เข้าใจกันโดยทั่วไปพื้นที่เก็บข้อมูลเกือบจะเป็นที่แพร่หลายในการออกแบบ SOLID เนื่องจากอนุญาตให้ใช้งานรหัสเพื่อขอคลาสข้อมูลในทางที่เป็นนามธรรมโดยไม่จำเป็นต้องมีความรู้เฉพาะเกี่ยวกับกลไก มันวางโค้ดที่ทำสิ่งนี้ในที่เดียวซึ่งตรงข้ามกับรูปแบบ DAO (ซึ่งถ้าคุณมีตัวอย่างเช่นคลาสข้อมูลใบแจ้งหนี้คุณจะต้องมี InvoiceDAO ที่สร้างวัตถุไฮเดรตของประเภทนั้นและอื่น ๆ สำหรับ วัตถุข้อมูล / ตารางทั้งหมดใน codebase / schema)
  • An IoC Container - ฉันลังเลที่จะเพิ่มอันนี้เนื่องจากฉันไม่ได้ใช้ IoC framework เพื่อทำการฉีดพึ่งพาส่วนใหญ่ของฉัน มันกลายเป็นรูปแบบการต่อต้านของพระเจ้าอย่างรวดเร็วในการขว้างทุกอย่างลงในภาชนะเขย่ามันและเทการพึ่งพาในแนวดิ่งที่คุณต้องการผ่านวิธีการฉีดจากโรงงาน ฟังดูดีจนกว่าคุณจะรู้ว่าโครงสร้างนั้นมีขนาดใหญ่มากและโครงการที่มีข้อมูลการลงทะเบียนถ้า "คล่องแคล่ว" ตอนนี้ต้องรู้ทุกอย่างเกี่ยวกับทุกสิ่งในโซลูชันของคุณ นั่นเป็นจำนวนมากของเหตุผลที่จะเปลี่ยนแปลง หากยังไม่คล่องแคล่ว (การลงทะเบียนล่าช้าที่ใช้ไฟล์กำหนดค่า) ชิ้นส่วนสำคัญของโปรแกรมของคุณจะต้องใช้ "สายอักขระเวทมนต์" ซึ่งเป็นเวิร์มที่แตกต่างกันโดยสิ้นเชิง

1
ทำไม downvotes
KeithS

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

10

เบี่ยงเบนความสนใจของพวกเขาด้วยการอภิปรายของJon Skeetว่า 'O' ใน SOLID เป็นอย่างไร "ไร้ประโยชน์และเข้าใจได้ยาก" และให้พวกเขาพูดถึง Alistair Cockburn ของ "การเปลี่ยนแปลงที่มีการป้องกัน" และ Josh Bloch ของ "การออกแบบเพื่อสืบทอด

บทสรุปสั้น ๆ ของบทความ Skeet (แม้ว่าฉันจะไม่แนะนำให้วางชื่อของเขาโดยไม่อ่านบทความในบล็อกเดิม):

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

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

คำตอบที่ดีอีกข้อก็คือ "ก็ขึ้นอยู่กับว่าพวกเขาเข้าใจดีแค่ไหนถ้าพวกเขารู้ทั้งหมดคือ buzzwords ที่เป็นของแข็งฉันคาดหวังว่าจะได้รับมรดกจากการใช้มรดก สะท้อนให้เห็นถึงคำศัพท์โดเมนที่ใช้ในการสื่อสารกับการจัดการผลิตภัณฑ์ .... "


6

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

ดังนั้นคุณคาดหวังที่จะเห็นสิ่งต่อไปนี้:

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

4

นี่เป็นคำถามที่ยอดเยี่ยมถึงแม้ว่าฉันคิดว่ามันเป็นคำถามสัมภาษณ์ที่ยากลำบาก

หลักการโซลิดควบคุมคลาสและอินเทอร์เฟซจริง ๆ และเกี่ยวข้องกันอย่างไร

คำถามนี้เป็นคำถามที่เกี่ยวข้องกับไฟล์มากกว่าและไม่จำเป็นต้องมีคลาส

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

โรเบิร์ตมาร์ตินกล่าวถึงหัวข้อนี้ในขอบเขตของ C ++ ในการออกแบบเชิงคัดค้าน c ++ โปรแกรมประยุกต์ใช้วิธี Booch (ดูส่วนที่เกี่ยวกับการทำงานร่วมกัน, การปิดและสามารถนำมาใช้) และในรหัสสะอาด


. NET coders IME มักจะปฏิบัติตามกฎ "1 คลาสต่อไฟล์" และยังสะท้อนโครงสร้างของโฟลเดอร์ / เนมสเปซ; Visual Studio IDE ส่งเสริมการปฏิบัติทั้งสองและปลั๊กอินต่าง ๆ เช่น ReSharper สามารถบังคับใช้ ดังนั้นฉันคาดว่าจะเห็นโครงสร้างโครงการ / ไฟล์สะท้อนโครงสร้างคลาส / ส่วนต่อประสาน
KeithS
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.