ไม่ต้องกังวลกับหลักการความรับผิดชอบเดียว มันจะไม่ช่วยให้คุณตัดสินใจได้ดีที่นี่เพราะคุณสามารถเลือกแนวคิดที่เจาะจงว่าเป็น "ความรับผิดชอบ" คุณสามารถพูดได้ว่าความรับผิดชอบของชั้นเรียนคือการจัดการข้อมูลที่มีอยู่ในฐานข้อมูลหรือคุณอาจบอกว่าความรับผิดชอบของชั้นเรียนคือการทำงานทั้งหมดที่เกี่ยวข้องกับการสร้างผู้ใช้ สิ่งเหล่านี้เป็นเพียงระดับที่แตกต่างกันของพฤติกรรมของแอปพลิเคชันและพวกเขาทั้งคู่แสดงออกถึงแนวคิดที่ถูกต้องของ "ความรับผิดชอบเดียว" ดังนั้นหลักการนี้ไม่ช่วยเหลือในการแก้ปัญหาของคุณ
หลักการที่มีประโยชน์ที่สุดที่จะใช้ในกรณีนี้คือหลักการของความประหลาดใจน้อยที่สุด ลองถามคำถาม: น่าแปลกใจไหมว่าที่เก็บที่มีบทบาทหลักในการเก็บข้อมูลไปยังฐานข้อมูลก็ส่งอีเมล์ด้วย
ใช่มันน่าประหลาดใจมาก ระบบภายนอกทั้งสองนี้แยกจากกันอย่างสมบูรณ์และชื่อSaveChanges
ไม่ได้บอกเป็นนัยถึงการส่งการแจ้งเตือน ความจริงที่คุณมอบหมายให้เหตุการณ์นี้ทำให้เกิดพฤติกรรมที่น่าแปลกใจมากขึ้นเนื่องจากมีคนที่อ่านรหัสไม่สามารถเห็นได้ง่ายว่าพฤติกรรมใดที่ถูกเรียกใช้เพิ่มเติม การบอกทางเป็นอันตรายต่อความสามารถในการอ่าน บางครั้งประโยชน์ก็คุ้มค่ากับการอ่าน แต่ไม่ใช่เมื่อคุณเรียกใช้ระบบภายนอกเพิ่มเติมโดยอัตโนมัติซึ่งมีผลที่สังเกตได้สำหรับผู้ใช้ (การบันทึกสามารถแยกได้ที่นี่เนื่องจากผลของมันคือการเก็บบันทึกเพื่อการดีบั๊กผู้ใช้จะไม่ใช้บันทึกดังนั้นจึงไม่มีอันตรายในการบันทึกเสมอ) ยิ่งแย่ลงซึ่งจะช่วยลดความยืดหยุ่นในการกำหนดเวลา ของการส่งอีเมลทำให้ไม่สามารถสอดแทรกการดำเนินการอื่นระหว่างการบันทึกและการแจ้งเตือน
หากรหัสของคุณต้องการส่งการแจ้งเตือนเมื่อสร้างผู้ใช้สำเร็จคุณสามารถสร้างวิธีที่ทำได้ดังนี้
public void AddUserAndNotify(IUserRepository repo, IEmailNotification notifier, MyUser user)
{
repo.Add(user);
repo.SaveChanges();
notifier.SendUserCreatedNotification(user);
}
แต่สิ่งนี้จะเพิ่มมูลค่าหรือไม่นั้นขึ้นอยู่กับข้อมูลเฉพาะของแอปพลิเคชันของคุณ
ฉันไม่สนับสนุนการมีอยู่ของSaveChanges
วิธีการทั้งหมด วิธีการนี้สันนิษฐานว่าจะกระทำธุรกรรมฐานข้อมูล แต่เก็บอื่น ๆ อาจมีการปรับเปลี่ยนฐานข้อมูลในรายการเดียวกัน ความจริงที่ว่ามันกระทำทั้งหมดนั้นน่าแปลกใจอีกครั้งเนื่องจากSaveChanges
มีการเชื่อมโยงกับอินสแตนซ์ของที่เก็บผู้ใช้นี้โดยเฉพาะ
รูปแบบที่ตรงไปตรงมาที่สุดสำหรับการจัดการธุรกรรมฐานข้อมูลคือusing
บล็อกภายนอก:
using (DataContext context = new DataContext())
{
_userRepository.Add(context, user);
context.SaveChanges();
notifier.SendUserCreatedNotification(user);
}
นี้จะช่วยให้การควบคุมอย่างชัดเจนโปรแกรมเมอร์กว่าเมื่อมีการเปลี่ยนแปลงสำหรับทั้งหมดที่เก็บจะถูกบันทึกไว้กองกำลังรหัสอย่างชัดเจนเอกสารลำดับของเหตุการณ์ที่จะต้องเกิดขึ้นก่อนที่จะกระทำให้มั่นใจว่าการย้อนกลับจะออกในข้อผิดพลาด (สมมติว่าDataContext.Dispose
ปัญหาย้อนกลับ) และหลีกเลี่ยงซ่อน การเชื่อมต่อระหว่างคลาส stateful
ฉันไม่ต้องการส่งอีเมลโดยตรงในคำขอ มันจะมีประสิทธิภาพมากขึ้นในการบันทึกความต้องการการแจ้งเตือนในคิว สิ่งนี้จะช่วยให้การจัดการความล้มเหลวดีขึ้น โดยเฉพาะอย่างยิ่งหากเกิดข้อผิดพลาดในการส่งอีเมลสามารถลองได้อีกครั้งในภายหลังโดยไม่รบกวนการบันทึกผู้ใช้และจะหลีกเลี่ยงกรณีที่ผู้ใช้ถูกสร้างขึ้น แต่เว็บไซต์จะส่งคืนข้อผิดพลาด
using (DataContext context = new DataContext())
{
_userRepository.Add(context, user);
_emailNotificationQueue.AddUserCreateNotification(user);
_emailNotificationQueue.Commit();
context.SaveChanges();
}
เป็นการดีกว่าที่จะยอมรับคิวการแจ้งเตือนก่อนเนื่องจากผู้บริโภคของคิวสามารถตรวจสอบว่ามีผู้ใช้อยู่ก่อนส่งอีเมลในกรณีที่การcontext.SaveChanges()
โทรล้มเหลว (ไม่เช่นนั้นคุณจะต้องใช้กลยุทธ์การส่งสองขั้นตอนแบบเต็มรูปแบบเพื่อหลีกเลี่ยง heisenbugs)
บรรทัดล่างคือการปฏิบัติ ที่จริงคิดผ่านผลที่ตามมา (ทั้งในแง่ของความเสี่ยงและผลประโยชน์) ของการเขียนรหัสวิธีการเฉพาะ ฉันพบว่า "หลักการความรับผิดชอบเดี่ยว" ไม่บ่อยช่วยฉันทำอย่างนั้นในขณะที่ "หลักการของความประหลาดใจน้อยที่สุด" มักจะช่วยให้ฉันเข้าไปในหัวของนักพัฒนาคนอื่น (เพื่อพูด) และคิดเกี่ยวกับสิ่งที่อาจเกิดขึ้น