วิธีที่ดีที่สุดในการจัดระเบียบไฟล์คลาสและส่วนต่อประสาน


17

ตกลง .. หลังจากการสนทนาทั้งหมดฉันเปลี่ยนคำถามเล็กน้อยเพื่อให้สะท้อนตัวอย่างที่เป็นรูปธรรมที่ฉันจัดการดีขึ้น


ฉันมีสองคลาสModelOneและคลาสModelTwoเหล่านี้มีฟังก์ชันการทำงานที่คล้ายกัน แต่ไม่เกี่ยวข้องกัน แต่ฉันมีชั้นที่สามCommonFuncที่มีบางฟังก์ชันการทำงานของประชาชนที่จะดำเนินการทั้งในModelOneและและได้รับปัจจัยจากการเป็นต่อModelTwo DRYทั้งสองรุ่นมีอินสแตนซ์ภายในModelMainคลาส (ซึ่งเป็นอินสแตนซ์ในระดับที่สูงขึ้น ฯลฯ - แต่ฉันหยุดที่ระดับนี้)

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

ดังนั้นรหัสฉลาดฉันท้ายด้วย2 :

public interface ICommonFunc 
{ 
}

public interface IModelOne 
{ 
   ICommonFunc Common { get; } 
   .. 
}

public interface IModelTwo
{ 
   ICommonFunc Common { get; } 
   .. 
}

public interface IModelMain 
{ 
  IModelOne One { get; } 
  IModelTwo Two { get; } 
  ..
}

public class CommonFunc : ICommonFunc { .. }

public class ModelOne : IModelOne { .. }

public class ModelTwo : IModelTwo { .. }

public class ModelMain : IModelMain { .. }

คำถามคือเกี่ยวกับวิธีจัดระเบียบโซลูชันของฉัน ฉันควรรักษาคลาสและอินเทอร์เฟซไว้ด้วยกันหรือไม่ หรือฉันควรรักษาคลาสและอินเทอร์เฟซไว้ด้วยกัน? เช่น:

ตัวเลือก 1 - จัดโดยชื่อคลาส

MySolution
  |
  |-MyProject
  |   |
      |-Models
      |   |
          |-Common
          |   |
          |   |-CommonFunc.cs
          |   |-ICommonFunc.cs
          |
          |-Main
          |   |
          |   |-IModelMain.cs
          |   |-ModelMain.cs
          |
          |-One
          |   |
          |   |-IModelOne.cs
          |   |-ModelOne.cs
          |
          |-Two
              |
              |-IModelTwo.cs
              |-ModelTwo.cs
              |

ตัวเลือก 2 - จัดระเบียบตามหน้าที่การใช้งาน (ส่วนใหญ่)

MySolution
  |
  |-MyProject
  |   |
      |-Models
      |   |
          |-Common
          |   |
          |   |-CommonFunc.cs
          |   |-ICommonFunc.cs
          |
          |-IModelMain.cs
          |-IModelOne.cs
          |-IModelTwo.cs
          |-ModelMain.cs
          |-ModelOne.cs
          |-ModelTwo.cs
          |

ตัวเลือก 3 - การแยกส่วนต่อประสานและการนำไปใช้

MySolution
  |
  |-MyProject
      |
      |-Interfaces
      |   |
      |   |-Models
      |   |   |
      |       |-Common
      |       |   |-ICommonFunc.cs
      |       |
      |       |-IModelMain.cs
      |       |-IModelOne.cs
      |       |-IModelTwo.cs
      |
      |-Classes
          | 
          |-Models
          |   |
              |-Common
              |   |-CommonFunc.cs
              |
              |-ModelMain.cs
              |-ModelOne.cs
              |-ModelTwo.cs
              |

ตัวเลือก 4 - ยกตัวอย่างการใช้งานเพิ่มเติม

MySolution
  |
  |-MyProject
  |   |
      |-Models
      |   |
          |-Components
          |   |
          |   |-Common
          |   |   |
          |   |   |-CommonFunc.cs
          |   |   |-ICommonFunc.cs
          |   |   
          |   |-IModelOne.cs
          |   |-IModelTwo.cs
          |   |-ModelOne.cs
          |   |-ModelTwo.cs
          |
          |-IModelMain.cs
          |-ModelMain.cs
          |

ฉันไม่ชอบตัวเลือกที่ 1 เนื่องจากชื่อคลาสในพา ธ แต่ที่ผมกำลังพุ่งไปอัตราส่วน 1: 1 เพราะ IoC / ทางเลือกการใช้งานของฉัน (และที่อาจจะเป็นที่ถกเถียงกัน) นี้มีข้อได้เปรียบในการมองเห็นความสัมพันธ์ระหว่างไฟล์

ตัวเลือกที่ 2 น่าสนใจสำหรับฉัน แต่ตอนนี้ฉันมีโคลนระหว่างน้ำกับModelMainรุ่นย่อย

ตัวเลือกที่ 3 ทำงานเพื่อแยกคำจำกัดความอินเทอร์เฟซจากการใช้งาน แต่ตอนนี้ฉันมีตัวแบ่งเทียมเหล่านี้ในชื่อพา ธ

ตัวเลือก 4 ฉันเอาตัวเลือก 2 และ tweaked มันเพื่อแยกส่วนประกอบจากรูปแบบหลัก

มีเหตุผลที่ดีในการเลือกหนึ่งอันใดอันหนึ่งหรือไม่? หรือรูปแบบที่เป็นไปได้อื่น ๆ ที่ฉันพลาดไป?


1. แฟรงค์แสดงความคิดเห็นว่าการมีอัตราส่วน 1: 1 ทำให้กลับมาที่ C ++ วันของไฟล์. h และ. cpp ฉันรู้ว่าเขามาจากไหน ความเข้าใจในความสามัคคีของฉันดูเหมือนจะทำให้ฉันเข้ามุมนี้ แต่ฉันก็ไม่แน่ใจด้วยซ้ำว่าจะทำยังไงถ้าคุณทำตามสุภาษิตของProgram to an interface แต่นั่นเป็นการอภิปรายอีกวันหนึ่ง

2. ฉันได้ละรายละเอียดของตัวสร้างออบเจ็กต์แต่ละรายการ นี่คือที่ที่คอนเทนเนอร์ IoC ฉีดวัตถุตามต้องการ


1
ฮึ. มีกลิ่นว่าคุณมีอัตราส่วน 1 ต่อ 1 อินเตอร์เฟซ / คลาส คุณใช้ IoC คอนเทนเนอร์ตัวไหน Ninject จัดเตรียมกลไกที่ไม่ต้องการอัตราส่วน 1: 1
RubberDuck

1
@RubberDuck FWIW ฉันใช้ Unity ฉันไม่ได้อ้างว่าเป็นผู้เชี่ยวชาญ แต่ถ้าชั้นเรียนของฉันได้รับการออกแบบให้มีความรับผิดชอบเดียวฉันจะไม่จบด้วยอัตราส่วน 1: 1 ได้อย่างไร
Peter M

คุณต้องการ IBase หรืออาจเป็นนามธรรม? ทำไม IDerivedOne เมื่อใช้งาน IBase ไปแล้ว คุณควรขึ้นอยู่กับ IBase ไม่ใช่มา ฉันไม่รู้เกี่ยวกับ Unity แต่คอนเทนเนอร์ IoC อื่น ๆ อนุญาตให้คุณทำการฉีด "context sensitive" โดยทั่วไปเมื่อClient1ต้องการจะให้IBase Derived1เมื่อClient2ความต้องการIBaseที่ IoC Derived2ให้
RubberDuck

1
ดีถ้าฐานเป็นนามธรรมแล้วไม่มีเหตุผลที่จะมีinterfaceมัน An interfaceเป็นเพียงคลาสนามธรรมกับสมาชิกเสมือนทั้งหมด
RubberDuck

1
RubberDuck ถูกต้อง ส่วนต่อประสานที่ไม่จำเป็นนั้นน่ารำคาญ
Frank Hileman

คำตอบ:


4

เนื่องจากอินเตอร์เฟสนั้นมีความคล้ายคลึงกับคลาสพื้นฐานให้ใช้ตรรกะเดียวกับที่คุณใช้สำหรับคลาสพื้นฐาน คลาสที่ใช้อินเทอร์เฟซนั้นสัมพันธ์กับอินเตอร์เฟสอย่างใกล้ชิด

ฉันสงสัยว่าคุณต้องการไดเรกทอรีที่เรียกว่า "Base Classes"; นักพัฒนาส่วนใหญ่ไม่ต้องการหรือไดเรกทอรีที่เรียกว่า "อินเทอร์เฟซ" ใน c # ไดเรกทอรีเป็น namespaces โดยค่าเริ่มต้นทำให้สับสนเป็นสองเท่า

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


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

@PeterM ฉันไม่แน่ใจว่าคุณใช้ IDE ใด แต่ Visual Studio ใช้ชื่อไดเรกทอรีเพื่อสร้างการประกาศ namespace โดยอัตโนมัติเมื่อเพิ่มคลาสตัวอย่างเช่น ซึ่งหมายความว่าแม้ว่าคุณจะมีความแตกต่างระหว่างชื่อไดเรกทอรีและชื่อเนมสเปซ แต่มันก็ยิ่งเจ็บปวดในการทำงาน ดังนั้นคนส่วนใหญ่ไม่ทำเช่นนั้น
Frank Hileman

ฉันกำลังใช้ VS และใช่ฉันรู้ถึงความเจ็บปวด มันนำความทรงจำกลับมาของเลย์เอาต์ไฟล์ Visual Source Safe
Peter M

1
@PeterM คำนึงถึงอัตราส่วน 1: 1 กับอัตราส่วนของคลาส เครื่องมือบางอย่างต้องการสิ่งนี้ ฉันมองว่านี่เป็นข้อบกพร่องของเครื่องมือ -. net มีความสามารถในการสะท้อนมากพอที่จะไม่ต้องการข้อ จำกัด ดังกล่าวในเครื่องมือดังกล่าว
Frank Hileman

1

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

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


ฉันได้แก้ไขตัวอย่างรหัสของฉันและเพิ่มตัวเลือกเพิ่มเติมบางอย่าง
Peter M

1

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

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

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

นั่งลงและสร้างบางกรณีการใช้งาน จากนั้นองค์กรที่ดีที่สุดอาจปรากฏขึ้นต่อหน้าคุณ


-2

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

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