คลาส `Employee` ควรได้รับการออกแบบอย่างไร?


11

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

การใช้งานขั้นพื้นฐานที่ฉันคิดว่าเป็นเรื่องง่าย ๆ นี้:

class Employee
{
    // Employee data (let's say, dozens of properties).

    Employee() {}
    Create() {}
    Update() {}
    Delete() {}
}

ด้วยการใช้งานนี้ฉันพบปัญหาหลายประการ

  1. IDของพนักงานจะได้รับจากฐานข้อมูลดังนั้นหากผมใช้วัตถุสำหรับการอธิบายเป็นพนักงานใหม่จะไม่มีIDการจัดเก็บเลยในขณะที่วัตถุที่เป็นตัวแทนของพนักงานที่มีอยู่จะIDมี ดังนั้นฉันจึงมีคุณสมบัติที่บางครั้งอธิบายวัตถุและบางครั้งก็ไม่ (ซึ่งอาจบ่งบอกว่าเราละเมิดSRPเนื่องจากเราใช้คลาสเดียวกันสำหรับตัวแทนพนักงานใหม่และพนักงานปัจจุบัน ... )
  2. Createวิธีการที่ควรจะสร้างการทำงานของพนักงานในฐานข้อมูลได้ในขณะที่UpdateและDeleteควรจะดำเนินการกับพนักงานที่มีอยู่ (อีกครั้งSRP ... )
  3. วิธีการ 'สร้าง' ควรมีพารามิเตอร์ใด มีพารามิเตอร์หลายสิบสำหรับข้อมูลพนักงานทั้งหมดหรืออาจเป็นEmployeeวัตถุ?
  4. ชั้นควรจะไม่เปลี่ยนรูป?
  5. จะUpdateทำงานอย่างไร จะใช้คุณสมบัติและอัปเดตฐานข้อมูลหรือไม่ หรืออาจจะใช้วัตถุสองชิ้น - อันเก่าและอันใหม่และอัพเดทฐานข้อมูลด้วยความแตกต่างระหว่างพวกมัน (ฉันคิดว่าคำตอบเกี่ยวข้องกับคำตอบเกี่ยวกับความไม่แน่นอนของชั้นเรียน)
  6. อะไรคือความรับผิดชอบของผู้สร้าง? สิ่งที่จะเป็นพารามิเตอร์ที่ใช้? มันจะดึงข้อมูลพนักงานจากฐานข้อมูลโดยใช้idพารามิเตอร์และเติมคุณสมบัติหรือไม่

อย่างที่คุณเห็นฉันมีความยุ่งเหยิงในหัวของฉันและฉันก็สับสนมาก คุณช่วยฉันเข้าใจว่าชั้นเรียนควรมีลักษณะอย่างไร

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


3
การละเมิดข้อ จำกัด ของ SRP คือคุณมีคลาสที่เป็นตัวแทนของทั้งเอนทิตีและรับผิดชอบต่อตรรกะ CRUD ถ้าคุณแยกมันการดำเนินงาน CRUD นั้นและโครงสร้างเอนทิตีจะเป็นคลาสที่แตกต่างกันดังนั้น1และ2จะไม่ทำลาย SRP 3.ควรนำEmployeeวัตถุเพื่อให้เป็นนามธรรมคำถาม4และ5โดยทั่วไปไม่สามารถตอบได้ขึ้นอยู่กับความต้องการของคุณและถ้าคุณแยกโครงสร้างและการดำเนินการ CRUD ออกเป็นสองคลาสแล้วมันค่อนข้างชัดเจนตัวสร้างของEmployeeข้อมูลที่ไม่สามารถดึงข้อมูลได้ จาก db อีกต่อไปดังนั้นคำตอบที่6
Andy

@DavidPacker - ขอบคุณ คุณตอบคำถามนั้นได้หรือไม่?
Sipo

5
อย่าทำซ้ำอย่าให้ ctor ของคุณยื่นมือไปยังฐานข้อมูล การทำรหัสให้แน่นกับคู่ของฐานข้อมูลและทำให้สิ่งที่พระเจ้ายากที่จะทดสอบ (แม้การทดสอบด้วยตนเองจะยากขึ้น) ดูที่รูปแบบที่เก็บ ลองคิดดูสักครู่คุณUpdateเป็นพนักงานหรือคุณอัปเดตประวัติพนักงานหรือไม่ คุณEmployee.Delete()หรือไม่Boss.Fire(employee)?
RubberDuck

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

คำตอบ:


10

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


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

กล่าวโดยย่อบันทึกที่ใช้งานอยู่คือวัตถุซึ่ง:

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

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

แต่การแก้ไขการออกแบบนั้นง่ายมากโดยการแยกการแสดงเอนทิตีและตรรกะ CRUD ออกเป็นสองคลาส (หรือมากกว่า)

นี่คือการออกแบบของคุณในตอนนี้:

  • Employee- มีข้อมูลเกี่ยวกับโครงสร้างพนักงาน (คุณลักษณะของมัน) และวิธีการแก้ไขเอนทิตี (ถ้าคุณตัดสินใจที่จะเปลี่ยนวิธี), มีตรรกะ CRUD สำหรับEmployeeเอนทิตีสามารถส่งกลับรายการของEmployeeวัตถุยอมรับEmployeeวัตถุเมื่อคุณต้องการ อัปเดตพนักงานสามารถส่งคืนเดียวEmployeeผ่านวิธีการเช่นgetSingleById(id : string) : Employee

ว้าวชั้นเรียนดูใหญ่มาก

นี่จะเป็นวิธีแก้ปัญหาที่เสนอ:

  • Employee - มีข้อมูลเกี่ยวกับโครงสร้างพนักงาน (คุณลักษณะของมัน) และวิธีการแก้ไขเอนทิตี (ถ้าคุณตัดสินใจที่จะใช้วิธีที่ไม่แน่นอน)
  • EmployeeRepository- มีตรรกะ CRUD สำหรับEmployeeเอนทิตีสามารถส่งกลับรายการของEmployeeวัตถุยอมรับEmployeeวัตถุเมื่อคุณต้องการอัพเดตพนักงานสามารถส่งคืนเดียวEmployeeผ่านวิธีเช่นgetSingleById(id : string) : Employee

คุณเคยได้ยินเรื่องการแยกความกังวลหรือไม่? ไม่ตอนนี้คุณจะ มันเป็นรุ่นที่เข้มงวดน้อยกว่าของหลักการความรับผิดชอบเดี่ยวซึ่งกล่าวว่าชั้นเรียนควรมีความรับผิดชอบเพียงอย่างเดียวเท่านั้นหรือตามที่ลุงบ๊อบกล่าวว่า:

โมดูลควรมีเหตุผลเดียวเท่านั้นที่จะเปลี่ยน

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

สิ่งที่ยอดเยี่ยมเกี่ยวกับรูปแบบพื้นที่เก็บข้อมูลมันไม่เพียงทำหน้าที่เป็นนามธรรมเพื่อให้ชั้นกลางระหว่างฐานข้อมูล (ซึ่งสามารถเป็นอะไรไฟล์ noSQL, SQL, วัตถุเชิงหนึ่ง) แต่ไม่จำเป็นต้องเป็นรูปธรรม ชั้น ในหลายภาษา OO คุณสามารถกำหนดอินเทอร์เฟซเป็นจริงinterface(หรือคลาสด้วยวิธีเสมือนจริงถ้าคุณอยู่ใน C ++) แล้วมีการนำไปใช้หลายอย่าง

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

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

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


คำถามที่ 4:ไม่สามารถตอบได้เลยไม่ใช่โดยทั่วไปเพราะคำตอบนั้นขึ้นอยู่กับว่าคุณต้องการอะไร


คำถามที่ 5:ตอนนี้คุณได้แยกชั้นป่องออกเป็นสองคุณสามารถมีวิธีการอัปเดตหลายโดยตรงบนEmployeeชั้นเช่นchangeUsername, markAsDeceasedซึ่งจะจัดการกับข้อมูลของEmployeeชั้นเท่านั้นในแรมและจากนั้นคุณสามารถแนะนำวิธีเช่นregisterDirtyจากรูปแบบหน่วยการทำงานเป็นคลาสที่เก็บข้อมูลซึ่งคุณจะให้ที่เก็บทราบว่าวัตถุนี้มีการเปลี่ยนแปลงคุณสมบัติและจะต้องมีการปรับปรุงหลังจากที่คุณเรียกใช้commitเมธอด

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


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


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


คำถามที่ 1:คุณจบด้วยEmployeeคลาสแบบง่าย ๆซึ่งตอนนี้ (ท่ามกลางคุณสมบัติอื่น ๆ ) จะมีรหัส ระบุว่า ID เต็มหรือว่างเปล่า (หรือnull) ขึ้นอยู่กับว่าวัตถุได้รับการบันทึกแล้ว อย่างไรก็ตามรหัสยังคงเป็นคุณลักษณะที่กิจการเป็นเจ้าของและความรับผิดชอบของEmployeeกิจการคือการดูแลคุณลักษณะของกิจการดังนั้นการดูแล ID

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


โน๊ตสำคัญ

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


อืมเหมือนกับคำตอบของฉัน แต่ไม่ใช่ 'edgey' วางบนเฉดสี
Ewan

2
@Ewan ฉันไม่ได้ลงคะแนนคำตอบของคุณ แต่ฉันสามารถดูว่าทำไมบางคนอาจมี มันไม่ได้ตอบคำถามของ OP โดยตรงโดยตรงและคำแนะนำบางอย่างของคุณดูเหมือนไม่มีมูล
Andy

1
คำตอบที่ดีและครอบคลุม ชมเล็บบนหัวด้วยความกังวลแยก และฉันชอบคำเตือนที่ pintpoints เป็นตัวเลือกที่สำคัญในการเลือกระหว่างการออกแบบที่ซับซ้อนและการประนีประนอมที่ดี
Christophe

จริงคำตอบของคุณดีกว่า
Ewan

เมื่อสร้างวัตถุพนักงานใหม่ครั้งแรกจะไม่มีค่าเป็น ID เขตข้อมูล id สามารถปล่อยด้วยค่า Null แต่จะทำให้วัตถุของพนักงานที่อยู่ในสถานะที่ไม่ถูกต้อง ????
Susantha7

2

ขั้นแรกให้สร้างโครงสร้างพนักงานที่มีคุณสมบัติของพนักงานที่มีแนวคิด

จากนั้นสร้างฐานข้อมูลที่มีโครงสร้างตารางตรงกันเช่น mssql

จากนั้นสร้างที่เก็บข้อมูลพนักงานสำหรับฐานข้อมูลนั้น EmployeeRepoMsSql ด้วยการดำเนินการ CRUD ต่างๆที่คุณต้องการ

จากนั้นสร้างอินเตอร์เฟส IEmployeeRepo เพื่อเปิดเผยการดำเนินการ CRUD

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

เมื่อมันถึงฉันแนะนำให้คุณใช้ GUID ซึ่งสามารถสร้างขึ้นได้ด้วยรหัสในตัวสร้าง

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

อีกวิธีหนึ่งคุณสามารถเลือกที่จะขมวดคิ้ว (แต่ในมุมมองของฉันที่เหนือกว่า) โมเดล Anemic Domain Object ที่คุณไม่ได้เพิ่มวิธีการ CRUD ให้กับวัตถุของคุณและเพียงแค่ส่งวัตถุไปยัง repo เพื่ออัปเดต / บันทึก / ลบ

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

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

ตัวอย่างรหัส *****

public class Employee
{
    public string Id { get; set; }

    public string Name { get; set; }

    private IEmployeeRepo repo;

    //with the OOP approach you want the save method to be on the Employee Object
    //so you inject the IEmployeeRepo in the Employee constructor
    public Employee(IEmployeeRepo repo)
    {
        this.repo = repo;
        this.Id = Guid.NewGuid().ToString();
    }

    public bool Save()
    {
        return repo.Save(this);
    }
}

public interface IEmployeeRepo
{
    bool Save(Employee employee);

    Employee Get(string employeeId);
}

public class EmployeeRepoSql : IEmployeeRepo
{
    public Employee Get(string employeeId)
    {
        var sql = "Select * from Employee where Id=@Id";
        //more db code goes here
        Employee employee = new Employee(this);
        //populate object from datareader
        employee.Id = datareader["Id"].ToString();

    }

    public bool Save(Employee employee)
    {
        var sql = "Insert into Employee (....";
        //db logic
    }
}

public class MyADMProgram
{
    public void Main(string id)
    {
        //with ADM don't inject the repo into employee, just use it in your program
        IEmployeeRepo repo = new EmployeeRepoSql();
        var emp = repo.Get(id);

        //do business logic
        emp.Name = TextBoxNewName.Text;

        //save to DB
        repo.Save(emp);

    }
}

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

ในกรณีนี้ repo คือบริการและฟังก์ชั่นเป็นการทำงานของ CRUD
Ewan

@DavidPacker คุณกำลังบอกว่ารุ่น Anemic Domain นั้นดีหรือไม่?
candied_orange

1
@CandiedOrange ฉันไม่ได้แสดงความคิดเห็นของฉันในความคิดเห็น แต่ไม่ถ้าคุณตัดสินใจที่จะไปไกลถึงแอปพลิเคชันของคุณไปยังเลเยอร์ที่เลเยอร์หนึ่งรับผิดชอบต่อตรรกะทางธุรกิจเท่านั้นฉันอยู่กับนายฟาวเลอร์ ในความเป็นจริงการต่อต้านรูปแบบ ทำไมฉันจึงต้องการUserUpdateบริการที่มีchangeUsername(User user, string newUsername)วิธีการเมื่อฉันสามารถเพิ่มchangeUsernameวิธีการในชั้นเรียนUserได้โดยตรง การสร้างบริการที่ไม่สมเหตุสมผล
Andy

1
ฉันคิดว่าในกรณีนี้การฉีด repo เพียงเพื่อใส่ตรรกะ CRUD บน Model ไม่เหมาะสมที่สุด
Ewan

1

ตรวจสอบการออกแบบของคุณ

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

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

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

คุณต้องจัดการสถานะของวัตถุด้วย ตัวอย่างเช่น:

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

ด้วยสิ่งนี้ในใจเราสามารถเลือก:

class Employee
{
    ...
    Employee () {}       // Initialize an empty Employee
    Load(IDType ID) {}   // Load employee with known ID from the database
    bool Create() {}     // Create an new employee an set its ID 
    bool Update() {}     // Update the employee (can ID be changed?)
    bool Delete() {}     // Delete the employee (and reset ID because there's no corresponding ID. 
    bool isClean () {}   // true if ID empty or if all properties match database
}

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

คำถามของคุณ

  1. ฉันคิดว่าคุณสมบัติ ID ไม่ได้ละเมิด SRP ความรับผิดชอบเดียวคือการอ้างถึงวัตถุฐานข้อมูล

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

    การออกแบบอื่นอาจทำให้เขตข้อมูลเปลี่ยนแปลงได้ในวัตถุอื่นที่จะโหลดเมื่อจำเป็นต้องเข้าถึงเขตข้อมูลเท่านั้น

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

  3. ฉันจะไม่เพิ่มพารามิเตอร์หลายสิบตัวลงไปCreate()เพราะวัตถุทางธุรกิจสามารถพัฒนาและทำให้ยากต่อการบำรุงรักษาทั้งหมดนี้ และรหัสจะอ่านไม่ได้ คุณมี 2 ตัวเลือกที่นี่: ผ่านชุดพารามิเตอร์แบบเรียบง่าย (ไม่เกิน 4) ที่จำเป็นอย่างยิ่งสำหรับการสร้างพนักงานในฐานข้อมูลและทำการเปลี่ยนแปลงที่เหลือผ่านการอัปเดตหรือคุณผ่านวัตถุ my_employee.Create()โดยวิธีการในการออกแบบของคุณผมเข้าใจว่าคุณได้เลือกแล้ว:

  4. ชั้นควรจะไม่เปลี่ยนรูป? ดูการอภิปรายข้างต้น: ในการออกแบบดั้งเดิมของคุณไม่มี ฉันจะเลือกใช้ ID ที่ไม่เปลี่ยนแปลง แต่ไม่ใช่พนักงานที่ไม่เปลี่ยนรูป พนักงานวิวัฒนาการในชีวิตจริง (ตำแหน่งงานใหม่ที่อยู่ใหม่สถานการณ์เกี่ยวกับการแต่งงานใหม่แม้แต่ชื่อใหม่ ... ) ฉันคิดว่ามันจะง่ายขึ้นและเป็นธรรมชาติมากขึ้นในการทำงานกับความเป็นจริงในใจอย่างน้อยในชั้นตรรกะทางธุรกิจ

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

  6. ฉันคิดว่าการดึงพนักงานจากฐานข้อมูลในตัวสร้างไม่ใช่ความคิดที่ดีเพราะการดึงข้อมูลอาจผิดไปได้และในหลาย ๆ ภาษายากที่จะรับมือกับการก่อสร้างที่ล้มเหลว ตัวสร้างควรเริ่มต้นวัตถุ (โดยเฉพาะ ID) และสถานะของมัน

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