หลักการของความรู้น้อยที่สุด


32

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

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

แต่ดูเหมือนว่าบางครั้งจำเป็นต้องใช้ความสามารถนี้มาก

ตัวอย่างเช่น: ฉันมีหลายคลาส: คลาสการจับภาพวิดีโอ, คลาสตัวเข้ารหัส, คลาสสตรีมและพวกเขาทั้งหมดใช้คลาสพื้นฐานอื่น ๆ , VideoFrame และเนื่องจากพวกเขามีปฏิสัมพันธ์กันพวกเขาสามารถทำสิ่งนี้ได้:

streamer รหัสชั้นเรียน

...
frame = encoder->WaitEncoderFrame()
frame->DoOrGetSomething();
....

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


เป็นไปได้ที่ซ้ำกันของวิธีการผูกมัด vs การห่อหุ้ม
gnat


4
นั่นอาจเป็นสิ่งที่ใกล้เคียงกัน
Robert Harvey

1
สิ่งที่เกี่ยวข้อง: รหัสเช่นนี้เป็น "ซากรถไฟ" (ละเมิดกฎของ Demeter) หรือไม่ (การเปิดเผยอย่างเต็มรูปแบบ: นั่นเป็นคำถามของฉัน)
CVn

คำตอบ:


21

หลักการที่คุณกำลังพูดถึง (รู้จักกันดีในชื่อLaw of Demeter ) สำหรับฟังก์ชั่นสามารถนำไปใช้โดยการเพิ่มวิธีการช่วยเหลืออื่นให้กับคลาสสตรีมของคุณเช่น

  {
    frame = encoder->WaitEncoderFrame()
    DoOrGetSomethingForFrame(frame); 
    ...
  }

  void DoOrGetSomethingForFrame(Frame *frame)
  {
     frame->DoOrGetSomething();
  }  

ตอนนี้แต่ละฟังก์ชั่นเท่านั้น "พูดคุยกับเพื่อน" ไม่ใช่ "เพื่อนของเพื่อน"

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


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

+1 แม้ว่าจะมีเหตุผลที่ดีที่จะไม่ทำการเปลี่ยนรหัสนั่นคือส่วนติดต่อของชั้นเรียนอาจกลายเป็นวิธีการที่งานของคุณเพียงโทรหนึ่งหรือสองสามครั้งไปยังวิธีอื่น ๆ (และไม่มีตรรกะเพิ่มเติม) ในสภาพแวดล้อมการเขียนโปรแกรมบางประเภทพูด C ++ COM (โดยเฉพาะอย่างยิ่ง WIC และ DirectX) มีค่าใช้จ่ายสูงที่เกี่ยวข้องกับการเพิ่มของแต่ละวิธีเดียวในส่วนต่อประสาน COM เมื่อเทียบกับภาษาอื่น

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

10
+1: กฎหมายของ Demeterควรได้รับการเสนอ
Binary Worrier

Law of demeter เป็นคำแนะนำสำหรับการเลือกสถาปัตยกรรมในขณะที่คุณแนะนำการเปลี่ยนแปลงเครื่องสำอางดังนั้นดูเหมือนว่าคุณจะปฏิบัติตาม 'กฎหมาย' นั่นจะเป็นความเข้าใจผิดเพราะการเปลี่ยนแปลงทางไวยากรณ์ไม่ได้หมายความว่าทุกอย่างจะดีขึ้น เท่าที่ฉันรู้ว่ากฎของเดมิเทอร์หมายถึงโดยทั่วไปแล้ว: ถ้าคุณต้องการทำ OOP ให้หยุดเขียนโค้ด prodecural ด้วยฟังก์ชั่น getter ได้ทุกที่
user2180613

39

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

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

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

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

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

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

หากคุณไม่สามารถแก้ปัญหาได้ในขณะที่ทำตามหลักการนี้อาจมีความสามารถอื่นที่คุณขาดไป

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

ที่จริงแล้วเป็นไปตามหลักการอื่น: แยกการใช้งานจากการก่อสร้าง

frame = encoder->WaitEncoderFrame()

ด้วยการใช้รหัสนี้คุณต้องรับผิดชอบต่อการได้Frameมา คุณยังไม่ได้รับผิดชอบใด ๆ Frameสำหรับการพูดคุยกับ

frame->DoOrGetSomething(); 

ตอนนี้คุณต้องรู้วิธีการพูดคุยกับ a Frameแต่แทนที่ด้วย:

new FrameHandler(frame)->DoOrGetSomething();

และตอนนี้คุณเพียงแค่ต้องรู้วิธีพูดคุยกับเพื่อนของคุณ FrameHandler

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

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

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

SOLIDหลักการเป็นคนใหญ่ฉันพยายามที่จะปฏิบัติตาม การได้รับการเคารพสองประการในที่นี้คือความรับผิดชอบเดี่ยวและหลักการผกผันของการพึ่งพา เป็นเรื่องยากที่จะเคารพสองคนนี้และยังคงละเมิดกฎหมายของ Demeter

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


2
"มันบอกคุณว่าเป็นการดีกว่าที่จะพูดคุยกับ" เพื่อน "ของคุณเท่านั้นและไม่ใช่กับ" เพื่อนของเพื่อน "" ++
RubberDuck

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

2
@diemaus ถ้าฉันขโมยที่กั้นย่อหน้าจากที่ใดที่หนึ่งมันก็รั่วไหลออกมาจากหัวของฉัน ในขณะที่สมองของฉันเพิ่งคิดว่ามันฉลาด สิ่งที่ฉันจำได้คือฉันเพิ่มมันหลังจาก upvotes มากดังนั้นจึงเป็นเรื่องดีที่จะตรวจสอบความถูกต้อง ดีใจที่มันช่วย
candied_orange

19

การออกแบบฟังก์ชั่นดีกว่าการออกแบบเชิงวัตถุหรือไม่? มันขึ้นอยู่กับ.

MVVM ดีกว่า MVC หรือไม่ มันขึ้นอยู่กับ.

Amos & Andy หรือ Martin และ Lewis? มันขึ้นอยู่กับ.

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

[หนังสือบางเล่ม] บอกว่า [บางสิ่ง] ผิด

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

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

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


2

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

มีวิธีอื่นในการแยกลำดับชั้นของวัตถุ:

เปิดเผยinterfaceประเภทมากกว่าclassประเภทผ่านวิธีการและคุณสมบัติของคุณ

ในกรณีของโปสเตอร์ดั้งเดิม (OP's) encoder->WaitEncoderFrame()จะคืนค่าIEncoderFrameแทนที่จะเป็น a Frameและจะกำหนดการทำงานที่อนุญาต


แนวทางแก้ไข 1

ในกรณีที่ง่ายที่สุดFrameและEncoderคลาสมีทั้งภายใต้การควบคุมของคุณIEncoderFrameเป็นชุดย่อยของวิธีเฟรมที่เปิดเผยต่อสาธารณะและEncoderคลาสไม่สนใจสิ่งที่คุณทำกับวัตถุนั้น จากนั้นการใช้งานมีความสำคัญ ( รหัสใน c # ):

interface IEncoderFrame {
    void DoOrGetSomething();
}

class Frame : IEncoderFrame {
    // A method that already exists in Frame.
    public void DoOrGetSomething() { ... }
}

class Encoder {
    private Frame _frame;
    public IEncoderFrame TheFrame { get { return _frame; } }
    ...
}

แนวทางแก้ไข 2

ในกรณีที่เป็นสื่อกลางที่Frameนิยามไม่อยู่ภายใต้การควบคุมของคุณหรือมันจะไม่เหมาะสมที่จะเพิ่มIEncoderFrame's วิธีการที่จะFrameแล้วทางออกที่ดีเป็นอะแดปเตอร์ นั่นคือสิ่งที่คำตอบของ CandiedOrangeพูดถึงnew FrameHandler( frame )คือ สำคัญ: หากคุณทำเช่นนี้มันมีความยืดหยุ่นมากขึ้นถ้าคุณแสดงว่ามันเป็นอินเตอร์เฟซที่ไม่เป็นชั้น Encoderมีความรู้เกี่ยวกับแต่ลูกค้าจะต้องรู้ว่าclass FrameHandler interface IFrameHandlerหรือตามที่ฉันตั้งชื่อinterface IEncoderFrame- เพื่อระบุว่าเป็นFrameเฉพาะที่เห็นจาก POV of Encoder :

interface IEncoderFrame {
    void DoOrGetSomething();
}

// Adapter pattern. Appropriate if no access needed to Encoder.
class EncoderFrameWrapper : IEncoderFrame {
    Frame _frame;
    public EncoderFrameWrapper( Frame frame ) {
        _frame = frame;
    }
    public void DoOrGetSomething() {
        _frame....;
    }
}

class Encoder {
    private Frame _frame;

    // Adapter pattern. Appropriate if no access needed to Encoder.
    public IEncoderFrame TheFrame { get { return new EncoderFrameWrapper( _frame ); } }

    ...
}

ต้นทุน: การจัดสรรและ GC ของวัตถุใหม่ EncoderFrameWrapper ทุกครั้งที่encoder.TheFrameถูกเรียก (คุณสามารถแคชกระดาษห่อหุ้มนั้น แต่เพิ่มรหัสเพิ่มเติมและเป็นรหัสง่าย ๆ เท่านั้นหากฟิลด์เฟรมตัวเข้ารหัสไม่สามารถแทนที่ด้วยเฟรมใหม่ได้)


แนวทางแก้ไข 3

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

interface IEncoderFrame {
    void DoOrGetSomething();
}

// *** You will end up regretting this. See next code snippet instead ***
class EncoderFrameWrapper : IEncoderFrame {
    Encoder _owner;
    Frame _frame;
    public EncoderFrameWrapper( Encoder owner, Frame frame ) {
        _owner = owner;   _frame = frame;
    }
    public void DoOrGetSomething() {
        _frame.DoOrGetSomething();
        // Hmm, maybe this wrapper class should be nested inside Encoder...
        _owner... some work inside owner; maybe should be owner-internal details ...
    }
}

class Encoder {
    private Frame _frame;

    ...
}

มันช่างน่าเกลียด มีการใช้งานที่ซับซ้อนน้อยลงเมื่อ wrapper ต้องการสัมผัสรายละเอียดของผู้สร้าง / เจ้าของ (Encoder):

interface IEncoderFrame {
    void DoOrGetSomething();
}

class Encoder : IEncoderFrame {
    private Frame _frame;

    // HA! Client gets to think of this as "the frame object",
    // but its really me, intercepting it.
    public IEncoderFrame TheFrame { get { return this; } }

    // This is the method that the LoD approach suggests writing,
    // except that we are exposing it only when the instance is accessed as an IEncoderFrame,
    // to avoid extending Encoder's already large API surface.
    public void IEncoderFrame.DoOrGetSomething() {
        _frame.DoOrGetSomething();
       ... make some change within current Encoder instance ...
    }
    ...
}

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


ความคิดเห็นสุดท้าย

พิจารณาสิ่งนี้: หากผู้ดำเนินการEncoderรู้สึกว่าการเปิดเผยFrame frameเหมาะสมกับสถาปัตยกรรมโดยรวมหรือ "ง่ายกว่าการใช้ LoD" ก็จะปลอดภัยกว่ามากหากพวกเขาทำตัวอย่างข้อมูลแรกที่ฉันแสดง - เปิดเผยชุดย่อยที่ จำกัด ของ Frame เป็นส่วนต่อประสาน จากประสบการณ์ของฉันซึ่งมักจะเป็นวิธีแก้ปัญหาได้ เพียงเพิ่มวิธีการในอินเทอร์เฟซตามต้องการ (ฉันกำลังพูดถึงสถานการณ์ที่เรา "รู้" กรอบมีวิธีการที่จำเป็นอยู่แล้วหรือพวกเขาจะง่ายและไม่ขัดแย้งที่จะเพิ่มงาน "ใช้งาน" สำหรับแต่ละวิธีคือการเพิ่มหนึ่งบรรทัดในคำจำกัดความอินเทอร์เฟซ) และ รู้ว่าแม้ในสถานการณ์ที่เลวร้ายที่สุดในอนาคตก็เป็นไปได้ที่จะทำให้ API นั้นทำงานได้ - ที่นี่IEncoderFrameFrameEncoder.

นอกจากนี้ยังทราบว่าถ้าคุณไม่ได้รับอนุญาตให้เพิ่มIEncoderFrameการFrameหรือวิธีการที่จำเป็นไม่พอดีดีเพื่อทั่วไปFrameชั้นเรียนและวิธีการแก้ปัญหา # 2 ไม่เหมาะกับคุณบางทีอาจเป็นเพราะของวัตถุเสริมสร้างและการทำลายล้าง วิธีการแก้ปัญหา # 3 สามารถมองเห็นเป็นเพียงวิธีการจัดระเบียบวิธีการEncoderเพื่อให้บรรลุ LoD อย่าเพิ่งผ่านหลายสิบวิธี ล้อมรอบพวกเขาในอินเทอร์เฟซและใช้ "การใช้อินเทอร์เฟซที่ชัดเจน" (ถ้าคุณอยู่ใน c #) เพื่อให้พวกเขาสามารถเข้าถึงได้เฉพาะเมื่อวัตถุถูกดูผ่านอินเทอร์เฟซนั้น

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


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

@PerataBreatta - ฉันไม่สามารถและไม่เห็นด้วยอย่างแน่นอน แต่ฉันต้องการชี้ให้เห็นสิ่งหนึ่ง: อินเทอร์เฟซตามคำจำกัดความหมายถึงสิ่งที่จำเป็นต้องรู้ในขอบเขตระหว่างสองคลาส หาก "จำเป็นต้องเปลี่ยน" นั่นเป็นพื้นฐาน - ไม่มีวิธีการอื่นใดที่สามารถหลีกเลี่ยงการเข้ารหัสที่จำเป็นได้อย่างน่าอัศจรรย์ ตรงกันข้ามกับการทำหนึ่งในสามสถานการณ์ที่ฉันอธิบาย แต่ไม่ได้ใช้อินเทอร์เฟซแทนส่งคืนคลาสที่เป็นรูปธรรม ใน 1, Frame, ใน 2, EncoderFrameWrapper, ใน 3, Encoder คุณล็อคตัวเองเป็นที่วิธีการ อินเทอร์เฟซสามารถปรับให้เข้ากับพวกเขาทั้งหมด
ToolmakerSteve

@PeriataBreatta ... ซึ่งแสดงให้เห็นถึงประโยชน์ของการนิยามอย่างชัดเจนว่าเป็นส่วนต่อประสาน ในที่สุดฉันก็หวังว่าจะปรับปรุง IDE เพื่อให้สะดวกดังนั้นอินเทอร์เฟซนั้นจะถูกใช้อย่างหนักมากขึ้น การเข้าถึงหลายระดับส่วนใหญ่จะผ่านทางอินเทอร์เฟซบางอย่างดังนั้นจึงง่ายต่อการจัดการการเปลี่ยนแปลง (หากนั่นคือ "overkill" ในบางกรณีการวิเคราะห์รหัสรวมกับคำอธิบายประกอบที่เรายินดีที่จะเสี่ยงต่อการไม่มีส่วนต่อประสานเพื่อแลกเปลี่ยนกับการเพิ่มประสิทธิภาพขนาดเล็กสามารถ "รวบรวมออก" - แทนที่ด้วย หนึ่งในสามคลาสคอนกรีตจาก 3 "โซลูชัน")
ToolmakerSteve

@PerataBreatta - และเมื่อฉันพูดว่า "อินเทอร์เฟซสามารถปรับให้เข้ากับพวกเขาทั้งหมด" ฉันไม่ได้พูดว่า "ahhhh มันยอดเยี่ยมที่คุณลักษณะภาษานี้หนึ่งสามารถครอบคลุมกรณีที่แตกต่างกันเหล่านี้" ฉันกำลังบอกว่าการกำหนดอินเตอร์เฟสลดการเปลี่ยนแปลงที่คุณอาจต้องทำน้อยที่สุด ในกรณีที่ดีที่สุดการออกแบบสามารถเปลี่ยนทุกอย่างจากกรณีที่ง่ายที่สุด (โซลูชันที่ 1) เป็นกรณีที่ยากที่สุด (โซลูชันที่ 3) โดยไม่ต้องเปลี่ยนอินเทอร์เฟซเลย - เฉพาะความต้องการภายในของผู้ผลิตเท่านั้นที่ซับซ้อนมากขึ้น และแม้กระทั่งเมื่อจำเป็นต้องมีการเปลี่ยนแปลง
ToolmakerSteve
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.