การบังคับใช้หลักการความรับผิดชอบเดี่ยว


40

ฉันเพิ่งมาโดยปัญหาสถาปัตยกรรมที่ดูเหมือนเล็กน้อย ฉันมีพื้นที่เก็บข้อมูลอย่างง่ายในรหัสของฉันที่ถูกเรียกเช่นนี้ (รหัสเป็น C #):

var user = /* create user somehow */;
_userRepository.Add(user);
/* do some other stuff*/
_userRepository.SaveChanges();

SaveChanges เป็น wrapper ง่ายๆที่ยอมรับการเปลี่ยนแปลงในฐานข้อมูล:

void SaveChanges()
{
    _dataContext.SaveChanges();
    _logger.Log("User DB updated: " + someImportantInfo);
}

หลังจากนั้นครู่หนึ่งฉันจำเป็นต้องใช้ตรรกะใหม่ที่จะส่งการแจ้งเตือนทางอีเมลทุกครั้งที่ผู้ใช้ถูกสร้างขึ้นในระบบ เนื่องจากมีการโทรเข้า_userRepository.Add()และออกSaveChangesจากระบบจำนวนมากฉันจึงตัดสินใจอัปเดตSaveChangesดังนี้:

void SaveChanges()
{
    _dataContext.SaveChanges();
    _logger.Log("User DB updated: " + someImportantInfo);
    foreach (var newUser in dataContext.GetAddedUsers())
    {
       _eventService.RaiseEvent(new UserCreatedEvent(newUser ))
    }
}

วิธีนี้โค้ดภายนอกสามารถสมัครสมาชิก UserCreatedEvent และจัดการตรรกะทางธุรกิจที่จำเป็นที่จะส่งการแจ้งเตือน

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

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


22
การตอบโต้ของคุณคือ: "ตกลงดังนั้นคุณจะเขียนมันอย่างไรเพื่อไม่ให้ละเมิด SRP แต่ยังอนุญาตให้มีการแก้ไขเพียงจุดเดียว"
Robert Harvey

43
การสังเกตของฉันคือการเพิ่มกิจกรรมไม่ได้เพิ่มความรับผิดชอบเพิ่มเติม อันที่จริงแล้วตรงกันข้าม: มันมอบหมายความรับผิดชอบที่อื่น
Robert Harvey

ฉันคิดว่าผู้ร่วมงานของคุณถูกต้อง แต่คำถามของคุณถูกต้องและมีประโยชน์
Andres F.

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

คำตอบ:


14

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

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

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

// In EventFiringUserRepo:
public void SaveChanges()
{
  _basicRepo.SaveChanges();
   FireEventsForNewlyAddedUsers();
}

private void FireEventsForNewlyAddedUsers()
{
  foreach (var newUser in _basicRepo.DataContext.GetAddedUsers())
  {
     _eventService.RaiseEvent(new UserCreatedEvent(newUser))
  }
}

คุณสามารถเรียกคลาสพร็อกซี่ a NotifyingRepositoryหรือตามที่ObservableRepositoryคุณต้องการได้ตามคำตอบที่ได้รับการโหวตอย่างสูงของ @ Peter (ซึ่งจริง ๆ แล้วไม่ได้บอกวิธีแก้ไขการละเมิด SRP เพียงบอกว่าการละเมิดนั้นใช้ได้)

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

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

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

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


3
นอกจากนี้คำตอบนี้ มีทางเลือกในการมอบฉันทะเป็นเช่นAOP
Laiv

1
ฉันคิดว่าคุณพลาดประเด็นไม่ใช่ว่าการเพิ่มกิจกรรมแบ่ง SRP ของตนว่าการเพิ่มกิจกรรมเฉพาะสำหรับผู้ใช้ "ใหม่" ต้องการให้ repo รับผิดชอบต่อการรู้ว่าผู้ใช้ "ใหม่" แทนที่จะเป็น "เพิ่งเพิ่มฉัน" "ผู้ใช้
Ewan

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

2
นี่คือคำตอบที่ดีที่สุด / IMO ที่ถูกต้อง มันไม่เพียงรักษา SRP แต่ยังรักษาหลักการ Open / Closed และคิดว่าการทดสอบอัตโนมัติทั้งหมดที่การเปลี่ยนแปลงภายในชั้นเรียนอาจทำลายได้ การแก้ไขการทดสอบที่มีอยู่เมื่อเพิ่มฟังก์ชันการทำงานใหม่เป็นกลิ่นที่ยิ่งใหญ่
Keith Payne

1
โซลูชันนี้ทำงานอย่างไรหากมีธุรกรรมอยู่ในระหว่างดำเนินการ? เมื่อสิ่งนั้นเกิดขึ้นSaveChanges()ไม่ได้สร้างเร็กคอร์ดฐานข้อมูลจริง ๆ และมันอาจจบลงด้วยการย้อนกลับ ดูเหมือนว่าคุณจะต้องแทนที่AcceptAllChangesหรือสมัครเป็นสมาชิก TransactionCompleted เหตุการณ์
John Wu

29

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

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

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


แต่ที่เก็บ "แจ้งเตือน" เป็นสิ่งที่คุณต้องการจริงหรือ คุณพูดถึง C #: หลายคนเห็นด้วยว่าการใช้System.Collections.ObjectModel.ObservableCollection<>แทนSystem.Collections.Generic.List<>เมื่อสิ่งที่คุณต้องการคือความเลวและผิดทุกชนิด แต่มีเพียงไม่กี่คนที่ชี้ไปที่ SRP ในทันที

สิ่งที่คุณกำลังทำอยู่ตอนนี้ก็คือการแลกเปลี่ยนของคุณกับUserList _userRepository ObservableUserCollection _userRepositoryหากเป็นแนวทางที่ดีที่สุดหรือไม่ขึ้นอยู่กับแอปพลิเคชัน แต่ในขณะที่มันทำให้_userRepositoryน้ำหนักเบาลงอย่างไม่ต้องสงสัยในความเห็นของฉันก็ไม่ได้ละเมิด SRP


ปัญหาเกี่ยวกับการใช้ObservableCollectionสำหรับกรณีนี้คือมันทำให้เกิดเหตุการณ์ที่เทียบเท่ากันไม่ได้อยู่ที่การเรียกSaveChangesแต่ที่การเรียกไปAddซึ่งจะนำไปสู่พฤติกรรมที่แตกต่างจากที่แสดงในตัวอย่าง ดูคำตอบของฉันวิธีทำให้ repo ดั้งเดิมมีน้ำหนักเบาและยังคงติดอยู่กับ SRP โดยทำให้ซีแมนทิกส์ยังคงเหมือนเดิม
Doc Brown

@DocBrown ฉันเรียกคลาสที่รู้จักObservableCollection<>และList<>เพื่อเปรียบเทียบและบริบท ฉันไม่ได้ตั้งใจจะแนะนำให้ใช้คลาสจริงสำหรับการใช้งานภายในหรืออินเทอร์เฟซภายนอก
ปีเตอร์

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

16

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

การออกแบบที่ดีกว่าคือการมีกระบวนการแยกเรียกข้อมูล 'ผู้ใช้ใหม่' จากที่เก็บและส่งอีเมล การติดตามว่าผู้ใช้รายใดได้รับอีเมลความล้มเหลวส่งซ้ำ ฯลฯ ฯลฯ

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

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

มันอาจคุ้มค่าที่จะขยายความคิดเห็นด้านล่าง

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

ไม่ถูกต้อง คุณกำลังสร้าง "เพิ่มลงในที่เก็บ" และ "ใหม่"

"เพิ่มในที่เก็บ" หมายถึงสิ่งที่กล่าว ฉันสามารถเพิ่มและลบและเพิ่มผู้ใช้ไปยังที่เก็บต่างๆ

"ใหม่" เป็นสถานะของผู้ใช้ที่กำหนดโดยกฎเกณฑ์ทางธุรกิจ

ขณะนี้กฎทางธุรกิจอาจเป็น "ใหม่ == เพิ่งถูกเพิ่มไปยังที่เก็บ" แต่นั่นไม่ได้หมายความว่าไม่ใช่หน้าที่แยกต่างหากที่ต้องรู้และใช้กฎนั้น

คุณต้องระมัดระวังเพื่อหลีกเลี่ยงการคิดแบบฐานข้อมูลแบบนี้ คุณจะมีกระบวนการกรณีขอบซึ่งเพิ่มผู้ใช้ที่ไม่ใช่ผู้ใช้ใหม่ไปยังพื้นที่เก็บข้อมูลและเมื่อคุณส่งอีเมลถึงพวกเขาทุกธุรกิจจะพูดว่า "แน่นอนว่าไม่ใช่ผู้ใช้ใหม่ '! กฎที่แท้จริงคือ X"

คำตอบนี้คือ IMHO ค่อนข้างขาดจุด: repo เป็นศูนย์กลางเดียวในรหัสซึ่งรู้เมื่อผู้ใช้ใหม่ถูกเพิ่ม

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

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


11
พื้นที่เก็บข้อมูลไม่ทราบว่าผู้ใช้ที่คุณเพิ่มเป็นผู้ใช้ใหม่ - Addใช่มันไม่ก็มีวิธีการที่เรียกว่า ความหมายของมันหมายถึงว่าผู้ใช้ที่เพิ่มเข้ามาทั้งหมดเป็นผู้ใช้ใหม่ รวมอาร์กิวเมนต์ทั้งหมดที่ส่งไปยังAddก่อนการโทรSave- และคุณจะได้รับผู้ใช้ใหม่ทั้งหมด
Andre Borges

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

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

7
@Andre ใหม่สำหรับ repo แต่ไม่จำเป็นต้อง "ใหม่" ในแง่ธุรกิจ มันเป็นการรวมกันของความคิดทั้งสองที่ซ่อนความรับผิดชอบเป็นพิเศษจากการมองแวบแรก ฉันอาจนำเข้าผู้ใช้เก่าหลายล้านคนไปยังที่เก็บใหม่ของฉันหรือลบและเพิ่มผู้ใช้ใหม่และอื่น ๆ จะมีกฎเกณฑ์ทางธุรกิจเกี่ยวกับสิ่งที่ "ผู้ใช้ใหม่" เกินกว่า "ถูกเพิ่มใน dB"
Ewan

1
ผู้ดำเนินรายการหมายเหตุ:คำตอบของคุณไม่ใช่การสัมภาษณ์นักหนังสือพิมพ์ หากคุณมีการแก้ไขให้รวมไว้ในคำตอบของคุณอย่างเป็นธรรมชาติโดยไม่สร้างเอฟเฟกต์ "ข่าวด่วน" ทั้งหมด เราไม่ได้เป็นกระดานสนทนา
Robert Harvey

7

นี่เป็นจุดที่ถูกต้องหรือไม่?

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

สำหรับฉันแล้วดูเหมือนว่าการเพิ่มกิจกรรมที่นี่เป็นสิ่งเดียวกับการบันทึก: เพียงแค่เพิ่มฟังก์ชันการทำงานด้านข้างให้กับฟังก์ชัน

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

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

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

ตอนนี้กลับไปที่ตัวอย่างเฉพาะของคุณ หากคุณต้องส่งการแจ้งเตือนทุกครั้งที่มีการสร้างผู้ใช้และอาจดำเนินการพิเศษอื่น ๆ อีกด้วยคุณสามารถสร้างบริการแยกต่างหากที่ห่อหุ้มข้อกำหนดนี้สิ่งที่ต้องการUserCreationServiceซึ่งจะเปิดเผยวิธีการหนึ่งAdd(user)ซึ่งจัดการกับที่เก็บข้อมูลทั้งสอง ไปยังพื้นที่เก็บข้อมูลของคุณ) และการแจ้งเตือนเป็นการดำเนินการทางธุรกิจเดียว หรือทำในตัวอย่างต้นฉบับของคุณหลังจากนั้น_userRepository.SaveChanges();


2
การบันทึกไม่ได้เป็นส่วนหนึ่งของกระบวนการทางธุรกิจ - เรื่องนี้เกี่ยวข้องกับบริบทของ SRP อย่างไร หากวัตถุประสงค์ของกิจกรรมของฉันคือการส่งข้อมูลผู้ใช้ใหม่ไปยัง Google Analytics - จากนั้นการปิดใช้งานจะมีผลกระทบทางธุรกิจเช่นเดียวกับการปิดใช้งานการบันทึก: ไม่สำคัญ แต่ค่อนข้างน่ารำคาญ อะไรคือกฎของหัวแม่มือสำหรับการเพิ่ม / ไม่เพิ่มตรรกะใหม่ให้กับฟังก์ชั่น? "การปิดใช้งานจะทำให้เกิดผลข้างเคียงทางธุรกิจที่สำคัญหรือไม่"
Andre Borges

2
If the purpose of my event would be to send new user data to Google Analytics - then disabling it would have the same business effect as disabling logging: not critical, but pretty upsetting . เกิดอะไรขึ้นถ้าคุณกำลังยิงเหตุการณ์ก่อนกำหนดที่ก่อให้เกิด "ข่าว" ปลอม จะเกิดอะไรขึ้นหากการวิเคราะห์คำนึงถึง "ผู้ใช้" ที่ไม่ได้ถูกสร้างขึ้นในที่สุดเนื่องจากข้อผิดพลาดกับธุรกรรม DB เกิดอะไรขึ้นถ้า บริษัท กำลังตัดสินใจเกี่ยวกับสถานที่ที่ผิดพลาดซึ่งได้รับการสนับสนุนโดยข้อมูลที่ไม่ถูกต้อง คุณเน้นด้านเทคนิคของปัญหามากเกินไป "บางครั้งคุณไม่สามารถมองเห็นป่าไม้สำหรับต้นไม้"
Laiv

@Liv คุณกำลังสร้างจุดที่ถูกต้อง แต่นี่ไม่ใช่จุดของคำถามของฉันหรือคำตอบนี้ คำถามคือว่านี่เป็นวิธีแก้ปัญหาที่ถูกต้องในบริบทของ SRP หรือไม่ดังนั้นสมมติว่าไม่มีข้อผิดพลาดในการทำธุรกรรมฐานข้อมูล
Andre Borges

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

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

4

SRP เป็นในทางทฤษฎีเกี่ยวกับคนเป็นลุงบ๊อบอธิบายในบทความของเขาความรับผิดชอบหลักการเดี่ยว ขอบคุณ Robert Harvey ที่ให้ไว้ในความคิดเห็นของคุณ

คำถามที่ถูกต้องคือ:

"ผู้มีส่วนได้เสีย" ใดที่เพิ่มข้อกำหนด "ส่งอีเมล"

หากผู้มีส่วนได้เสียนั้นอยู่ในความดูแลของการคงอยู่ของข้อมูล (ไม่น่าเป็นไปได้ แต่เป็นไปได้) สิ่งนี้จะไม่ละเมิด SRP มิฉะนั้นจะทำ


2
น่าสนใจ - ฉันไม่เคยได้ยินเรื่องการตีความ SRP นี้ คุณมีพอยน์เตอร์สำหรับข้อมูลเพิ่มเติม / วรรณกรรมเกี่ยวกับการตีความนี้หรือไม่?
sleske

2
@sleske: จากลุงบ๊อบตัวเอง : "และนี่คือจุดเริ่มต้นของหลักการความรับผิดชอบเดียวหลักการนี้เป็นเรื่องเกี่ยวกับผู้คน เมื่อคุณเขียนโมดูลซอฟต์แวร์คุณต้องการให้แน่ใจว่าเมื่อมีการร้องขอการเปลี่ยนแปลง จากคนคนเดียวหรือมากกว่านั้นเป็นกลุ่มคนที่มีความสัมพันธ์แน่นหนาซึ่งเป็นตัวแทนของหน้าที่ทางธุรกิจที่นิยามไว้เพียงอย่างเดียว "
Robert Harvey

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

1
อ๋อ นั่นคือสิ่งที่เกิดขึ้นกับคำว่า REST แม้แต่ Roy Fielding กล่าวว่าผู้คนใช้ผิด
Robert Harvey

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

2

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

สร้างผู้ใช้ตัดสินใจเลือกสิ่งที่เป็นผู้ใช้ใหม่และการคงอยู่ของมันมี 3 สิ่งที่แตกต่าง

สถานที่ตั้งของฉัน

พิจารณาหลักฐานก่อนหน้านี้ก่อนที่จะตัดสินใจว่าที่เก็บเป็นสถานที่ที่เหมาะสมในการแจ้งเตือนเหตุการณ์ทางธุรกิจ (ไม่ว่าจะเป็น SRP) หรือไม่ หมายเหตุที่ผมกล่าวว่าเหตุการณ์ทางธุรกิจเพราะฉันUserCreatedมีความหมายที่แตกต่างกันกว่าUserStoredหรือ1UserAdded ฉันจะพิจารณาแต่ละเหตุการณ์เหล่านั้นเพื่อส่งไปยังผู้ชมที่แตกต่างกัน

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

ในทางกลับกันมันไม่จำเป็นต้องเป็นเรื่องจริงที่_dataContext.SaveChanges();ยืนยันผู้ใช้สำเร็จ มันจะขึ้นอยู่กับช่วงธุรกรรมของฐานข้อมูล ตัวอย่างเช่นอาจเป็นจริงสำหรับฐานข้อมูลเช่น MongoDB ซึ่งธุรกรรมเป็น atomic แต่ก็ไม่สามารถทำได้สำหรับ RDBMS ดั้งเดิมที่ใช้ธุรกรรม ACID ซึ่งอาจมีธุรกรรมที่เกี่ยวข้องมากกว่า

นี่เป็นจุดที่ถูกต้องหรือไม่?

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

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

สำหรับฉันแล้วดูเหมือนว่าการเพิ่มกิจกรรมที่นี่เป็นสิ่งเดียวกับการบันทึก

ไม่ได้อย่างแน่นอน. อย่างไรก็ตามการบันทึกข้อมูลนั้นไม่มีผลข้างเคียงตามที่คุณแนะนำเหตุการณ์UserCreatedน่าจะทำให้การดำเนินธุรกิจอื่น ๆ เกิดขึ้น ชอบแจ้งเตือน 3

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

ไม่จำเป็นต้องเป็นเรื่องจริง SRP ไม่ได้เกี่ยวข้องกับคลาสเท่านั้น มันทำงานในระดับต่าง ๆ ของ abstractions เช่นเลเยอร์ไลบรารีและระบบ! มันเป็นเรื่องของการทำงานร่วมกันเกี่ยวกับการรักษาร่วมกันสิ่งที่เปลี่ยนแปลงด้วยเหตุผลเดียวกันด้วยมือของผู้มีส่วนได้ส่วนเสียเดียวกัน หากการสร้างผู้ใช้ ( กรณีการใช้งาน ) มีการเปลี่ยนแปลงอาจเป็นไปได้ในช่วงเวลาหนึ่งและสาเหตุที่ทำให้เหตุการณ์เกิดขึ้น


1: การตั้งชื่อสิ่งต่าง ๆ ก็มีความสำคัญเช่นกัน

2: บอกว่าเราส่งUserCreatedหลังจาก_dataContext.SaveChanges();นั้น แต่ธุรกรรมฐานข้อมูลทั้งหมดล้มเหลวในภายหลังเนื่องจากปัญหาการเชื่อมต่อหรือการละเมิดข้อ จำกัด ระวังการแพร่ภาพเหตุการณ์ก่อนกำหนดเนื่องจากผลข้างเคียงอาจยากที่จะเลิกทำ (หากเป็นไปได้)

3: กระบวนการแจ้งเตือนที่ไม่ได้รับการปฏิบัติอย่างเพียงพออาจทำให้คุณต้องทำการแจ้งเตือนที่ไม่สามารถเลิกทำได้ / sup>


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

2
เผง เหตุการณ์แสดงถึงความแน่นอน มีบางอย่างเกิดขึ้น แต่จบแล้ว
Laiv

1
@Laiv: ยกเว้นเมื่อพวกเขาทำไม่ได้ Microsoft มีเหตุการณ์ทุกประเภทที่นำหน้าด้วยBeforeหรือPreviewไม่มีการรับประกันเกี่ยวกับความแน่นอน
Robert Harvey

1
@ jpmc26: หากไม่มีทางเลือกคำแนะนำของคุณจะไม่เป็นประโยชน์
Robert Harvey

1
@ jpmc26: คำตอบของคุณคือ "เปลี่ยนเป็นระบบนิเวศการพัฒนาที่แตกต่างอย่างสิ้นเชิงด้วยชุดเครื่องมือและคุณสมบัติด้านประสิทธิภาพที่แตกต่างกันโดยสิ้นเชิง" โทรหาฉันตรงกันข้าม แต่ฉันคิดว่าสิ่งนี้เป็นไปไม่ได้สำหรับความพยายามในการพัฒนาส่วนใหญ่
Robert Harvey

1

ไม่มีสิ่งนี้ไม่ได้ละเมิด SRP

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

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

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


1

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

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

ใช่มันน่าประหลาดใจมาก ระบบภายนอกทั้งสองนี้แยกจากกันอย่างสมบูรณ์และชื่อ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)


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


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

4
โดยพื้นฐานแล้วคุณกำลังโต้แย้งว่า "อย่าใช้เหตุการณ์"
Robert Harvey

3
[ยัก]กิจกรรมเป็นศูนย์กลางของเฟรมเวิร์ก UI ส่วนใหญ่ กำจัดเหตุการณ์และกรอบงานเหล่านั้นไม่ทำงานเลย
Robert Harvey

2
@ jpmc26: เรียกว่า ASP.NET Webforms มันแย่มาก
Robert Harvey

2
My repository is not sending emails. It just raises an eventทำให้เกิดผลกระทบ ที่เก็บกำลังเรียกกระบวนการแจ้งเตือน
Laiv

0

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

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

ในการรับโซลูชัน SRP ที่บริสุทธิ์อันดับแรกให้เรียกเหตุการณ์จากนั้นเรียกใช้ hooks ทั้งหมดสำหรับเหตุการณ์นั้นซึ่งตอนนี้มีอยู่สามวิธี ได้แก่ การบันทึกการบันทึกและการส่งอีเมลในที่สุด

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


-1

รหัสละเมิด SRP อยู่แล้ว - คลาสเดียวกันรับผิดชอบการสื่อสารกับบริบทข้อมูลและการบันทึก

คุณเพียงแค่อัพเกรดมันให้มีความรับผิดชอบ 3 อย่าง

วิธีหนึ่งในการแยกสิ่งต่าง ๆ กลับมาเป็นความรับผิดชอบ 1 อย่าง_userRepositoryคือ ทำให้เป็นผู้ประกาศคำสั่ง

มันมีชุดคำสั่งรวมทั้งชุดฟัง มันได้รับคำสั่งและถ่ายทอดไปยังผู้ฟัง อาจเป็นไปได้ว่าผู้ฟังเหล่านั้นได้รับคำสั่งและบางทีพวกเขาก็สามารถพูดได้ว่าคำสั่งนั้นล้มเหลว

ตอนนี้คำสั่งส่วนใหญ่อาจมีเพียง 1 ฟัง (บริบทข้อมูล) SaveChanges ก่อนการเปลี่ยนแปลงของคุณมี 2 - บริบทข้อมูลและตัวบันทึก

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

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

ทั้งหมดนี้ได้รับการตัดสินใจเมื่อ_userRepositoryมีการสร้างและต่อสาย (หรืออาจจะเป็นคุณสมบัติพิเศษเหล่านั้นที่ได้รับการเพิ่ม / ลบออกได้ทันทีสามารถเพิ่ม / ปรับปรุงการบันทึกในขณะที่แอปพลิเคชันทำงานอาจใช้งานได้)

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