บันทึกวัตถุด้วยวิธีการของตัวเองหรือผ่านชั้นเรียนอื่นได้หรือไม่


38

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

ข้อเสนอแนะใดบ้างตามกระบวนทัศน์ของ OOD

ตัวอย่างเช่น

Class Student
{
    public string Name {set; get;}
    ....
    public bool Save()
    {
        SqlConnection con = ...
        // Save the class in the db
    }
    public bool Retrieve()
    {
         // search the db for the student and fill the attributes
    }
    public List<Student> RetrieveAllStudents()
    {
         // this is such a method I have most problem with it 
         // that an object returns an array of objects of its own class!
    }
}

กับ (ฉันรู้ว่าแนะนำต่อไปนี้ แต่ดูเหมือนว่าฉันจะขัดกับความต่อเนื่องของStudentชั้น)

Class Student { /* */ }
Class DB {
  public bool AddStudent(Student s)
  {

  }
  public Student RetrieveStudent(Criteria)
  {
  } 
  public List<Student> RetrieveAllStudents()
  {
  }
}

วิธีการผสมพวกเขา

   Class Student
    {
        public string Name {set; get;}
        ....
        public bool Save()
        {
            /// do some business logic!
            db.AddStudent(this);
        }
        public bool Retrieve()
        {
             // build the criteria 
             db.RetrieveStudent(criteria);
             // fill the attributes
        }
    }

3
คุณควรทำวิจัยเกี่ยวกับรูปแบบ Active Record เช่นคำถามนี้
Eric King

@gnat ฉันคิดว่าการถามแบบไหนดีกว่าคือเพิ่มความคิดเห็นแล้วเพิ่ม "ข้อดีข้อเสีย" และไม่มีปัญหาที่จะลบออก แต่จริงๆแล้วฉันหมายถึงกระบวนทัศน์ของ OOD ที่แนะนำ
Ahmad

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

คำตอบ:


34

Single รับผิดชอบหลักการ , แยกความกังวลและการทำงานร่วมกันทำงาน หากคุณอ่านแนวคิดเหล่านี้คำตอบที่คุณได้รับคือ: แยกพวกเขาออก

เหตุผลง่ายๆในการแยกชั้นStudent"DB" (หรือStudentRepositoryเพื่อทำตามอนุสัญญาที่เป็นที่นิยมมากขึ้น) คือการอนุญาตให้คุณเปลี่ยน "กฎเกณฑ์ทางธุรกิจ" ของคุณนำเสนอในStudentชั้นเรียนโดยไม่ส่งผลกระทบต่อโค้ดที่รับผิดชอบต่อการติดตา ในทางกลับกัน

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

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

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

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

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


11
อา แต่Studentชั้นควรถูกห่อหุ้มอย่างถูกต้องใช่ไหม จากนั้นคลาสภายนอกจะจัดการกับการคงอยู่ของสถานะส่วนตัวได้อย่างไร? การห่อหุ้มจะต้องมีความสมดุลกับ SoC และ SRP แต่การเลือกอย่างใดอย่างหนึ่งโดยไม่มีการชั่งน้ำหนักอย่างระมัดระวังการแลกเปลี่ยนอาจจะผิด ทางออกที่เป็นไปได้สำหรับปริศนานี้คือการใช้ access-package accessors สำหรับรหัสการคงอยู่ที่จะใช้
amon

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

4
การอ้างสิทธิ์ว่า SRP ทำให้โปรแกรมง่ายต่อการดูแลรักษานั้นน่าสงสัยที่สุด ปัญหาที่ SRP สร้างขึ้นคือแทนที่จะมีคอลเลกชันของคลาสที่มีชื่อที่ดีซึ่งชื่อบอกทุกสิ่งที่คลาสสามารถทำได้และไม่สามารถทำได้ (กล่าวอีกนัยหนึ่งคือเข้าใจง่ายซึ่งทำให้ง่ายต่อการบำรุงรักษา) หลายพันชั้นเรียนซึ่งผู้คนจำเป็นต้องใช้อรรถาภิธานเพื่อเลือกชื่อที่ไม่ซ้ำใครหลายชื่อมีความคล้ายคลึงกัน แต่ไม่มากนักและเรียงลำดับตามทั้งหมดที่แกลบเป็นงานที่น่าเบื่อ IOW, ไม่สามารถบำรุงรักษาได้เลย IMO
Dunk

3
@Dunk คุณพูดราวกับว่าคลาสเป็นหน่วยการทำให้เป็นโมดูลเดียวที่มีอยู่ ฉันเคยเห็นและเขียนหลายโครงการที่ติดตาม SRP และฉันยอมรับว่าตัวอย่างของคุณเกิดขึ้น แต่ถ้าคุณไม่ใช้แพคเกจและไลบรารีที่ถูกต้อง หากคุณแยกความรับผิดชอบออกเป็นแพ็คเกจเนื่องจากบริบทมีความหมายโดยนัยคุณสามารถตั้งชื่อคลาสได้อย่างอิสระอีกครั้ง จากนั้นเพื่อรักษาระบบเช่นนี้สิ่งที่ต้องทำก็คือเจาะลึกไปยังแพ็คเกจที่ถูกต้องก่อนค้นหาคลาสที่คุณต้องการเปลี่ยน :)
MichelHenrich

5
@Dunk OOD หลักการสามารถระบุเป็น: "ในหลักการการทำเช่นนี้จะดีกว่าเพราะ X, Y และ Z" ไม่ได้หมายความว่าควรนำไปใช้เสมอ ผู้คนจะต้องปฏิบัติตนและถ่วงน้ำหนักการแลกเปลี่ยน ในโครงการขนาดใหญ่ประโยชน์ที่ได้นั้นมักจะเกินดุลช่วงการเรียนรู้ที่คุณพูดถึง - แต่แน่นอนว่าไม่ใช่ความจริงสำหรับคนส่วนใหญ่และดังนั้นจึงไม่ควรนำมาใช้อย่างหนัก อย่างไรก็ตามแม้ในแอพพลิเคชั่นที่เบากว่า SRP ฉันคิดว่าเราสามารถตกลงกันได้ว่าการแยกการคงอยู่จากกฎเกณฑ์ทางธุรกิจให้ผลประโยชน์เกินกว่าราคาเสมอ (ยกเว้นรหัสโยนทิ้ง)
MichelHenrich

10

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

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


ขอบคุณฉันแก้ไขคำตอบแล้ววิธีที่สามล่ะ อย่างไรก็ตามฉันคิดว่าที่นี่ประเด็นเกี่ยวกับเลเยอร์ที่แตกต่างกัน
Ahmad

มีบางสิ่งที่แนะนำให้ฉันใช้ StudentRepository แทน DB (ไม่ทราบว่าทำไม! อาจเป็นความรับผิดชอบเดี่ยวอีกครั้ง) แต่ฉันก็ยังไม่สามารถหาที่เก็บข้อมูลที่แตกต่างกันสำหรับแต่ละชั้นทำไม? ถ้ามันเป็นเพียง data access layer มันมีฟังก์ชั่นยูทิลิตี้สแตติกมากมายและสามารถอยู่ด้วยกันได้ใช่ไหม? แค่บางทีมันก็จะกลายเป็นชั้นเรียนที่สกปรกและแออัด (ขอโทษสำหรับภาษาอังกฤษของฉัน)
Ahmad

1
@Ahmad: มีเหตุผลสองสามข้อและข้อดีข้อเสียที่ต้องพิจารณา นี่สามารถเติมหนังสือทั้งเล่มได้ เหตุผลที่อยู่เบื้องหลังสิ่งนี้ทำให้ความสามารถในการทดสอบการบำรุงรักษาและการพัฒนาในระยะยาวของโปรแกรมของคุณโดยเฉพาะเมื่อพวกเขามีวงจรชีวิตที่ยืนยาว
Doc Brown

มีใครทำงานโครงการที่มีการเปลี่ยนแปลงที่สำคัญในเทคโนโลยี DB หรือไม่? (ไม่มีการเขียนซ้ำขนาดใหญ่?) ฉันยังไม่ได้ ถ้าเป็นเช่นนั้นทำตาม SRP ตามที่แนะนำที่นี่เพื่อหลีกเลี่ยงการผูกติดอยู่กับฐานข้อมูลนั้นหรือไม่ช่วยอย่างมีนัยสำคัญ?
user949300

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

3

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

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

ตามหมายเหตุด้านข้าง: ในตัวอย่างของคุณ RetrieveStudent, AddStudent ควรเป็นวิธีการแบบคงที่ (เนื่องจากไม่ได้ขึ้นอยู่กับอินสแตนซ์)

อีกวิธีหนึ่งของการมีวิธีการบันทึก / โหลดในระดับคือ:

class Animal{
    public string Name {get; set;}
    public void Save(){...}
    public static Animal Load(string name){....}
    public static string[] GetAllNames(){...} // if you just want to list them
    public static Animal[] GetAllAnimals(){...} // if you actually need to retrieve them all
}

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

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


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

คุณพูดถูกฉันมีข้อสันนิษฐานมากมายจากคำถามเดิม
Gerino

ขอบคุณฉันคิดว่าวิธีที่ดีที่สุดคือการใช้เลเยอร์ตามที่กล่าวไว้ในรูปแบบ Repository
Ahmad

1

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

แต่ฉันคิดว่ามันคงยอดเยี่ยมมาก

แต่จะดีกว่าถ้าให้คลาสจัดการข้อมูลและฟังก์ชันการทำงาน (เช่นพลเมือง OO ที่ดี) และทำให้กลไกการคงอยู่ไปยังคลาสอื่นหรือชุดของคลาสภายนอก

ตัวเลือกอื่นคือการใช้เฟรมเวิร์กการคงอยู่ที่กำหนดการคงอยู่ที่ประกาศไว้ (เช่นเดียวกับคำอธิบายประกอบ) แต่นั่นก็ยังคงอยู่ภายนอกการคงอยู่


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

เกี่ยวกับRetrieveAllStudents()วิธีการความรู้สึกของคุณถูกต้องมันอาจถูกวางผิดเพราะคุณอาจมีรายชื่อนักเรียนที่แตกต่างกัน ทำไมไม่เก็บรายการไว้นอกStudentห้องเรียน?


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