วิธีการผูกมัด - ทำไมมันเป็นการปฏิบัติที่ดีหรือไม่?


151

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

participant.addSchedule(events[1]).addSchedule(events[2]).setStatus('attending').save()

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

participant.getSchedule('monday').saveTo('monnday.file')

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

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

participant.attend(event).setNotifications('silent').getSocialStream('twitter').postStatus('Joining '+event.name).follow(event.getSocialId('twitter'))

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

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

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


ดูเหมือนว่าวิธีการผูกมัดอย่างน้อยหนึ่งวิธีในการเขียนรหัสสมาร์ทใน java แม้ว่าจะไม่ได้ทุกคนเห็นด้วย ..
เมอร์เซอร์ Traieste

พวกเขายังเรียกวิธีการ "คล่องแคล่ว" เหล่านี้หรืออินเตอร์เฟซ "คล่องแคล่ว" คุณอาจต้องการอัปเดตชื่อของคุณเพื่อใช้คำนี้
S.Lott

4
ในการสนทนา SO อีกครั้งหนึ่งได้มีการกล่าวว่าส่วนต่อประสานที่คล่องแคล่วเป็นแนวคิดที่ใหญ่กว่าที่เกี่ยวกับการอ่านรหัสและวิธีการผูกมัดเป็นเพียงวิธีเดียวที่จะมุ่งสู่เป้าหมายนี้ แม้ว่าจะมีความเกี่ยวข้องกันอย่างใกล้ชิดดังนั้นฉันจึงเพิ่มแท็กและอินเทอร์เฟซที่อ้างอิงในข้อความ - ฉันคิดว่าสิ่งเหล่านี้เพียงพอ
Ilari Kajaste

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

มันเป็นสำนวนที่ดีในการเขียนโค้ด แต่ก็เหมือนกับเครื่องมือที่ยอดเยี่ยมทั้งหมดที่มันถูกทารุณกรรม
Martin Spamer

คำตอบ:


74

ฉันยอมรับว่านี่เป็นความคิดเห็น ส่วนใหญ่ฉันหลีกเลี่ยงวิธีการผูกมัด แต่เมื่อเร็ว ๆ นี้ฉันยังพบกรณีที่เป็นเพียงสิ่งที่ถูกต้อง - ฉันมีวิธีที่ยอมรับบางสิ่งเช่นพารามิเตอร์ 10 รายการและต้องการมากกว่า แต่ส่วนใหญ่คุณต้องระบุ น้อย ด้วยการแทนที่สิ่งนี้กลายเป็นเรื่องยุ่งยากอย่างรวดเร็วมาก แต่ฉันเลือกใช้วิธีการผูกมัด:

MyObject.Start()
    .SpecifySomeParameter(asdasd)
    .SpecifySomeOtherParameter(asdasd)
    .Execute();

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

ประเด็นคือ - ในกรณี 99% ที่คุณอาจทำได้ดีหรือดีกว่าโดยไม่ต้องผูกมัดวิธีการ แต่มี 1% ซึ่งเป็นวิธีที่ดีที่สุด


4
IMO P = MyObject.GetParamsObj().SomeParameter(asdasd).SomeOtherParameter(asdasd); Obj = MyObject.Start(); MyObject.Execute(P);วิธีที่ดีที่สุดที่จะใช้วิธีการผูกมัดในสถานการณ์นี้คือการสร้างวัตถุพารามิเตอร์ที่จะส่งผ่านไปยังฟังก์ชั่นเช่น คุณมีข้อได้เปรียบของการสามารถ reutilize วัตถุพารามิเตอร์นี้ในการโทรอื่น ๆ ซึ่งเป็นข้อดี!
pedromanoel

20
ส่วนใหญ่ของฉันมีส่วนร่วมเกี่ยวกับรูปแบบวิธีการของโรงงานมักจะมีจุดสร้างเพียงจุดเดียวและผลิตภัณฑ์เป็นตัวเลือกแบบคงที่ตามพารามิเตอร์ของวิธีการจากโรงงาน การสร้างลูกโซ่ดูรูปแบบตัวสร้างมากขึ้นซึ่งคุณเรียกวิธีการที่แตกต่างกันเพื่อให้ได้ผลลัพธ์วิธีการที่สามารถเลือกได้เช่นเดียวกับในการผูกมัดวิธีเราสามารถมีสิ่งที่ต้องการPizzaBuilder.AddSauce().AddDough().AddTopping()อ้างอิงเพิ่มเติมที่นี่
Marco Medrano

3
วิธีการผูกมัด (ตาม chown ในคำถามเดิม) ถือว่าไม่ดีเมื่อมีการละเมิดกฎหมายของ Demeter ดู: iface butts.net/2006/03/07/…คำตอบที่ให้ในความเป็นจริงตามกฎหมายดังกล่าวเพราะมันเป็น "รูปแบบการสร้าง"
Angel O'Sphere

2
@Marco Medrano ตัวอย่าง PizzaBuilder bugged ฉันเสมอตั้งแต่ฉันอ่านมันใน JavaWorld ทุกเพศทุกวัยที่ผ่านมา ฉันรู้สึกว่าฉันควรเพิ่มซอสให้กับพิซซ่าไม่ใช่เพื่อพ่อครัว
Breandán Dalton

1
ฉันรู้ว่าคุณหมายถึงอะไร Vilx แต่เมื่อฉันอ่านlist.add(someItem)ฉันอ่านว่า "รหัสนี้เพิ่มsomeItemไปยังlistวัตถุ" ดังนั้นเมื่อฉันอ่านPizzaBuilder.AddSauce()ฉันอ่านข้อความนี้อย่างเป็นธรรมชาติว่า "โค้ดนี้เพิ่มซอสให้กับPizzaBuilderวัตถุ" ในคำอื่น ๆ ฉันเห็น orchestrator (คนที่ทำการเพิ่ม) เป็นวิธีการที่รหัสlist.add(someItem)หรือPizzaBuilder.addSauce()ฝังตัว แต่ฉันแค่คิดว่าตัวอย่าง PizzaBuilder เป็นสิ่งประดิษฐ์เล็กน้อย ตัวอย่างMyObject.SpecifySomeParameter(asdasd)งานของคุณใช้ได้ดีสำหรับฉัน
Breandán Dalton

78

แค่ 2 เซ็นต์ของฉัน;

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

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

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


4
ไม่ได้ใช้งานการขึ้นบรรทัดใหม่และการผูกมัดด้วยวิธีอิสระ คุณสามารถเชื่อมโยงกับการโทรแต่ละครั้งในการขึ้นบรรทัดใหม่ตาม @ Vilx- answer และโดยทั่วไปคุณสามารถใส่คำสั่งแยกหลายรายการในบรรทัดเดียวกัน (เช่นการใช้เครื่องหมายอัฒภาคใน Java)
brabster

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

1
@Brabster พวกเขาอาจแยกจากกันสำหรับผู้ debuggers บางคนอย่างไรก็ตามการโทรแยกกันด้วยตัวแปรกลางยังช่วยให้คุณมีข้อมูลมากขึ้นในขณะที่คุณตรวจสอบข้อบกพร่อง
RAY

4
+1 คุณไม่ทราบว่าสิ่งใดที่แต่ละวิธีกลับมาเมื่อการดีบักขั้นตอนห่วงโซ่วิธี รูปแบบห่วงโซ่วิธีการคือแฮ็ค มันเป็นฝันร้ายที่เลวร้ายที่สุดของโปรแกรมซ่อมบำรุง
Lee Kowalkowski

1
ฉันไม่ใช่แฟนตัวยงของการผูกมัด แต่ทำไมไม่ใส่จุดพักไว้ในคำจำกัดความของวิธีการ?
Pankaj Sharma

39

โดยส่วนตัวแล้วฉันชอบวิธีการผูกมัดที่ทำหน้าที่กับวัตถุต้นฉบับเท่านั้นเช่นการตั้งค่าคุณสมบัติหลายอย่างหรือการเรียกใช้เมธอดชนิดยูทิลิตี้

foo.setHeight(100).setWidth(50).setColor('#ffffff');
foo.moveTo(100,100).highlight();

ฉันไม่ใช้มันเมื่อหนึ่งในวิธีการที่ถูกล่ามโซ่จะส่งคืนวัตถุใด ๆ นอกเหนือจาก foo ในตัวอย่างของฉัน ในขณะที่วากยสัมพันธ์คุณสามารถโยงอะไรก็ได้ตราบใดที่คุณใช้ API ที่ถูกต้องสำหรับวัตถุนั้นในห่วงโซ่การเปลี่ยนวัตถุ IMHO ทำให้สิ่งที่อ่านไม่ได้น้อยลงและอาจสับสนได้จริงหาก APIs สำหรับวัตถุต่าง ๆ มีความคล้ายคลึงกัน ถ้าคุณใช้วิธีการที่ใช้กันทั่วไปบางอย่างให้เรียกที่ท้าย.toString() , .print(), อะไรก็ตาม) ซึ่งวัตถุที่คุณกำลังทำหน้าที่ในท้ายที่สุดเมื่อ? บางคนอ่านโค้ดโดยไม่ตั้งใจอาจไม่เข้าใจว่ามันจะเป็นวัตถุที่ถูกส่งคืนโดยปริยายในสายโซ่แทนที่จะเป็นข้อมูลอ้างอิงดั้งเดิม

การผูกมัดวัตถุที่แตกต่างกันยังสามารถนำไปสู่ข้อผิดพลาดที่ไม่คาดคิดเป็นโมฆะ ในตัวอย่างของฉันสมมติว่าfooนั้นถูกต้องการเรียกใช้เมธอดทั้งหมดนั้น "ปลอดภัย" (เช่นใช้ได้สำหรับ foo) ในตัวอย่างของ OP:

participant.getSchedule('monday').saveTo('monnday.file')

... ไม่มีการรับประกัน (ในฐานะนักพัฒนาซอฟต์แวร์ภายนอกที่ดูรหัส) ที่ getSchedule จะส่งคืนวัตถุกำหนดการที่ถูกต้องและไม่เป็นโมฆะ นอกจากนี้การดีบักสไตล์ของรหัสนี้มักจะยากกว่ามากเนื่องจาก IDEs จำนวนมากจะไม่ประเมินการเรียกใช้เมธอด ณ เวลาดีบั๊กเป็นวัตถุที่คุณสามารถตรวจสอบได้ IMO ทุกครั้งที่คุณอาจต้องการวัตถุเพื่อตรวจสอบเพื่อจุดประสงค์ในการดีบั๊กฉันต้องการให้มันเป็นตัวแปรที่ชัดเจน


หากมีโอกาสที่Participantไม่ได้ถูกต้องScheduleแล้วgetScheduleวิธีการที่ถูกออกแบบมาเพื่อกลับMaybe(of Schedule)ชนิดและsaveToวิธีการที่ถูกออกแบบมาเพื่อยอมรับMaybeประเภท
Lightman

24

Martin Fowler มีการสนทนาที่ดีที่นี่:

วิธีการผูกมัด

ควรใช้เมื่อใด

วิธีการผูกมัดสามารถเพิ่มมากในการอ่านของ DSL ภายในและเป็นผลเกือบ synonum สำหรับ DSLs ภายในในใจบางอย่าง วิธีการผูกมัดที่ดีที่สุด แต่เมื่อมันถูกใช้ร่วมกับการรวมฟังก์ชั่นอื่น ๆ

วิธีการผูกมัดมีประสิทธิภาพโดยเฉพาะอย่างยิ่งกับไวยากรณ์เช่น parent :: = (this | that) * การใช้วิธีการต่างๆให้วิธีที่สามารถอ่านได้ว่าการโต้เถียงใดกำลังจะเกิดขึ้นต่อไป อาร์กิวเมนต์ตัวเลือกที่คล้ายกันสามารถข้ามไปได้อย่างง่ายดายด้วยการผูกมัดด้วยวิธีการ รายการของข้อบังคับที่จำเป็นเช่น parent :: = วินาทีแรกนั้นใช้งานไม่ได้กับรูปแบบพื้นฐานแม้ว่ามันจะสามารถรองรับได้ดีโดยการใช้ส่วนต่อประสานแบบก้าวหน้า เวลาส่วนใหญ่ฉันต้องการฟังก์ชั่นที่ซ้อนกันสำหรับกรณีที่

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


2
การเชื่อมโยงจะตาย :(
Qw3ry

คุณหมายถึงอะไรกับ DSL ภาษาเฉพาะโดเมน
Sören

@ Sören: Fowler หมายถึงภาษาเฉพาะโดเมน
Dirk Vollmar

21

ในความคิดของฉันวิธีการผูกมัดเป็นเรื่องแปลก แน่นอนว่ามันดูเท่ห์ แต่ฉันไม่เห็นข้อดีที่แท้จริงในนั้น

อย่างไร:

someList.addObject("str1").addObject("str2").addObject("str3")

ดีกว่า:

someList.addObject("str1")
someList.addObject("str2")
someList.addObject("str3")

ข้อยกเว้นอาจเกิดขึ้นเมื่อ addObject () ส่งคืนออบเจ็กต์ใหม่ซึ่งในกรณีนี้รหัสที่ไม่มีการแก้ไขอาจจะยุ่งยากกว่านี้เล็กน้อยเช่น:

someList = someList.addObject("str1")
someList = someList.addObject("str2")
someList = someList.addObject("str3")

9
กระชับยิ่งขึ้นเนื่องจากคุณหลีกเลี่ยงส่วน 'someList' สองส่วนแม้ในตัวอย่างแรกของคุณและจบลงด้วยหนึ่งบรรทัดแทนที่จะเป็นสามบรรทัด ตอนนี้ถ้ามันดีหรือไม่ดีขึ้นอยู่กับสิ่งต่าง ๆ และบางทีอาจเป็นเรื่องของรสนิยม
Fabian Steeg

26
ข้อได้เปรียบที่แท้จริงของการมี 'someList' เพียงครั้งเดียวก็คือการให้ชื่อที่มีความหมายและยาวกว่านั้นง่ายกว่ามาก ทุกครั้งที่ชื่อต้องปรากฏหลายครั้งติดต่อกันอย่างรวดเร็วมีแนวโน้มที่จะทำให้สั้น (เพื่อลดการซ้ำซ้อนและปรับปรุงการอ่าน) ซึ่งทำให้คำอธิบายน้อยลงทำร้ายการอ่านง่าย
Chris Dodd

จุดที่ดีเกี่ยวกับความสามารถในการอ่านและหลักการ DRY กับข้อแม้ที่การผูกมัดวิธีการป้องกันการส่งคืนค่าของวิธีการที่กำหนดและ / หรือแสดงถึงข้อสันนิษฐานของรายการ / ชุดที่ว่างเปล่าและค่าที่ไม่เป็นโมฆะจากทุกวิธีในห่วงโซ่ NPE จำนวนมากในระบบที่ฉันต้องดีบัก / แก้ไขบ่อยครั้ง)
Darrell Teague

@ChrisDodd ไม่เคยได้ยินเกี่ยวกับความสมบูรณ์อัตโนมัติเลยเหรอ?
inf3rno

1
"สมองมนุษย์ดีมากในการจดจำการทำซ้ำข้อความหากมีการเยื้องเดียวกัน" - นั่นเป็นปัญหาที่เกิดขึ้นซ้ำ ๆ กันอย่างแม่นยำ: มันทำให้สมองให้ความสำคัญกับรูปแบบการทำซ้ำ ดังนั้นในการอ่านและทำความเข้าใจกับรหัสคุณต้องบังคับให้สมองของคุณมองไปที่ด้านหลัง / ด้านหลังของรูปแบบการทำซ้ำเพื่อดูสิ่งที่เกิดขึ้นจริงซึ่งอยู่ในความแตกต่างระหว่างรูปแบบการทำซ้ำที่สมองของคุณมีแนวโน้ม นี่คือเหตุผลที่การทำซ้ำไม่ดีสำหรับการอ่านรหัส
Chris Dodd

7

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

ฉันจะยกตัวอย่าง:

foodStore เป็นวัตถุที่ประกอบด้วยร้านขายอาหารมากมายที่คุณเป็นเจ้าของ foodstore.getLocalStore () ส่งคืนออบเจ็กต์ที่เก็บข้อมูลเกี่ยวกับร้านค้าที่ใกล้เคียงที่สุดกับพารามิเตอร์ getPriceforProduct (อะไร) เป็นวิธีการของวัตถุนั้น

ดังนั้นเมื่อคุณโทรหา foodStore.getLocalStore (พารามิเตอร์) .getPriceforProduct (อะไรก็ได้)

คุณไม่เพียง แต่พึ่งพา FoodStore เท่านั้น แต่ขึ้นอยู่กับ LocalStore ด้วย

ควร getPriceforProduct (อะไรก็ได้) ที่เปลี่ยนแปลงตลอดเวลาคุณต้องเปลี่ยนไม่เพียง แต่ FoodStore เท่านั้น แต่ยังเป็นคลาสที่เรียกว่าวิธีการโยง

คุณควรตั้งเป้าหมายว่าจะแต่งงานกันในชั้นเรียน

ที่ถูกกล่าวว่าฉันชอบโซ่พวกเขาเมื่อเขียนโปรแกรม Ruby


7

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

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


6

ดูเหมือนว่าเป็นอัตนัย

วิธีการผูกมัดไม่ได้เป็นสิ่งที่เลวร้ายหรือ imo ดีโดยธรรมชาติ

การอ่านเป็นสิ่งที่สำคัญที่สุด

(พิจารณาด้วยว่าการใช้วิธีการล่ามโซ่จำนวนมากจะทำให้สิ่งที่บอบบางมากหากมีการเปลี่ยนแปลง)


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

มันจะไม่เลวโดยเนื้อแท้ถ้ามันส่งผลในการมีเพศสัมพันธ์สูง? การแบ่งสายโซ่ลงในแต่ละประโยคไม่ได้ลดความสามารถในการอ่าน
aberrant80

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

6

ประโยชน์ของการผูกมัด
คือที่ที่ฉันชอบใช้

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

ฉันรู้ว่านี่เป็นตัวอย่างที่วางแผนไว้ แต่บอกว่าคุณมีคลาสต่อไปนี้

Public Class Location
   Private _x As Integer = 15
   Private _y As Integer = 421513

   Public Function X() As Integer
      Return _x
   End Function
   Public Function X(ByVal value As Integer) As Location
      _x = value
      Return Me
   End Function

   Public Function Y() As Integer
      Return _y
   End Function
   Public Function Y(ByVal value As Integer) As Location
      _y = value
      Return Me
   End Function

   Public Overrides Function toString() As String
      Return String.Format("{0},{1}", _x, _y)
   End Function
End Class

Public Class HomeLocation
   Inherits Location

   Public Overrides Function toString() As String
      Return String.Format("Home Is at: {0},{1}", X(), Y())
   End Function
End Class

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

  Dim loc As New HomeLocation()
  loc.X(1337)
  PrintLocation(loc)

แต่นี่ไม่ใช่เรื่องง่ายที่จะอ่าน:

  PrintLocation(New HomeLocation().X(1337))

หรือสมาชิกชั้นเรียนจะเป็นอย่างไร?

Public Class Dummy
   Private _locA As New Location()
   Public Sub New()
      _locA.X(1337)
   End Sub
End Class

VS

Public Class Dummy
   Private _locC As Location = New Location().X(1337)
End Class

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

New Dealer.CarPicker().Subaru.WRX.SixSpeed.TurboCharged.BlueExterior.GrayInterior.Leather.HeatedSeats

Vs บางอย่างเช่น

New Dealer.CarPicker(Dealer.CarPicker.Makes.Subaru
                   , Dealer.CarPicker.Models.WRX
                   , Dealer.CarPicker.Transmissions.SixSpeed
                   , Dealer.CarPicker.Engine.Options.TurboCharged
                   , Dealer.CarPicker.Exterior.Color.Blue
                   , Dealer.CarPicker.Interior.Color.Gray
                   , Dealer.CarPicker.Interior.Options.Leather
                   , Dealer.CarPicker.Interior.Seats.Heated)

ความเสียหายของการผูกมัด
เช่นที่ฉันไม่ชอบใช้งาน

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

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

ข้อสรุป

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

ฉันพยายามทำตามกฎเหล่านี้

  1. พยายามไม่โยงระหว่างคลาส
  2. ทำกิจวัตรโดยเฉพาะสำหรับการผูกมัด
  3. ทำสิ่งเดียวเท่านั้นในชีวิตประจำวันที่ผูกมัด
  4. ใช้เมื่อปรับปรุงการอ่าน
  5. ใช้มันเมื่อมันทำให้รหัสง่ายขึ้น

6

วิธีการผูกมัดสามารถอนุญาตสำหรับการออกแบบขั้นสูง DSLใน Java โดยตรง โดยพื้นฐานแล้วคุณสามารถสร้างกฎ DSL ประเภทเหล่านี้อย่างน้อย:

1. SINGLE-WORD
2. PARAMETERISED-WORD parameter
3. WORD1 [ OPTIONAL-WORD]
4. WORD2 { WORD-CHOICE-A | WORD-CHOICE-B }
5. WORD3 [ , WORD3 ... ]

กฎเหล่านี้สามารถใช้งานได้โดยใช้อินเตอร์เฟซเหล่านี้

// Initial interface, entry point of the DSL
interface Start {
  End singleWord();
  End parameterisedWord(String parameter);
  Intermediate1 word1();
  Intermediate2 word2();
  Intermediate3 word3();
}

// Terminating interface, might also contain methods like execute();
interface End {}

// Intermediate DSL "step" extending the interface that is returned
// by optionalWord(), to make that method "optional"
interface Intermediate1 extends End {
  End optionalWord();
}

// Intermediate DSL "step" providing several choices (similar to Start)
interface Intermediate2 {
  End wordChoiceA();
  End wordChoiceB();
}

// Intermediate interface returning itself on word3(), in order to allow for
// repetitions. Repetitions can be ended any time because this interface
// extends End
interface Intermediate3 extends End {
  Intermediate3 word3();
}

ด้วยกฎง่ายๆเหล่านี้คุณสามารถใช้ DSL ที่ซับซ้อนเช่น SQL โดยตรงใน Java เช่นเดียวกับjOOQซึ่งเป็นไลบรารีที่ฉันสร้างขึ้น ดูตัวอย่าง SQL ที่ค่อนข้างซับซ้อนซึ่งนำมาจากบล็อกของฉันที่นี่:

create().select(
    r1.ROUTINE_NAME,
    r1.SPECIFIC_NAME,
    decode()
        .when(exists(create()
            .selectOne()
            .from(PARAMETERS)
            .where(PARAMETERS.SPECIFIC_SCHEMA.equal(r1.SPECIFIC_SCHEMA))
            .and(PARAMETERS.SPECIFIC_NAME.equal(r1.SPECIFIC_NAME))
            .and(upper(PARAMETERS.PARAMETER_MODE).notEqual("IN"))),
                val("void"))
        .otherwise(r1.DATA_TYPE).as("data_type"),
    r1.NUMERIC_PRECISION,
    r1.NUMERIC_SCALE,
    r1.TYPE_UDT_NAME,
    decode().when(
    exists(
        create().selectOne()
            .from(r2)
            .where(r2.ROUTINE_SCHEMA.equal(getSchemaName()))
            .and(r2.ROUTINE_NAME.equal(r1.ROUTINE_NAME))
            .and(r2.SPECIFIC_NAME.notEqual(r1.SPECIFIC_NAME))),
        create().select(count())
            .from(r2)
            .where(r2.ROUTINE_SCHEMA.equal(getSchemaName()))
            .and(r2.ROUTINE_NAME.equal(r1.ROUTINE_NAME))
            .and(r2.SPECIFIC_NAME.lessOrEqual(r1.SPECIFIC_NAME)).asField())
    .as("overload"))
.from(r1)
.where(r1.ROUTINE_SCHEMA.equal(getSchemaName()))
.orderBy(r1.ROUTINE_NAME.asc())
.fetch()

อีกหนึ่งตัวอย่างที่ดีคือjRTFซึ่งเป็น DSL ขนาดเล็กที่ออกแบบมาเพื่อทำเอกสาร RTF ใน Java โดยตรง ตัวอย่าง:

rtf()
  .header(
    color( 0xff, 0, 0 ).at( 0 ),
    color( 0, 0xff, 0 ).at( 1 ),
    color( 0, 0, 0xff ).at( 2 ),
    font( "Calibri" ).at( 0 ) )
  .section(
        p( font( 1, "Second paragraph" ) ),
        p( color( 1, "green" ) )
  )
).out( out );

@ user877329: ใช่มันสามารถใช้ในภาษาโปรแกรมเชิงวัตถุใด ๆ ที่รู้อะไรบางอย่างเช่นส่วนต่อประสานและประเภทย่อยที่หลากหลาย
Lukas Eder

4

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

$this->db->select('something')->from('table')->where('id', $id);

ที่ดูสะอาดกว่ามาก (และสมเหตุสมผลกว่าในความคิดของฉัน) กว่า:

$this->db->select('something');
$this->db->from('table');
$this->db->where('id', $id);

มันเป็นเรื่องส่วนตัว ทุกคนมีความคิดเห็นของตนเอง


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

3

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

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

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

นอกจากนี้:

. convertTextToVoice.LoadText ( "source.txt") ConvertToVoice ( "destination.wav");

เป็นวิธีที่ฉันมักจะใช้มัน การใช้มันเพื่อโยงพารามิเตอร์จำนวน x ไม่ใช่วิธีที่ฉันใช้ หากฉันต้องการใส่จำนวนพารามิเตอร์ x ในการเรียกเมธอดฉันจะใช้ไวยากรณ์params :

โมฆะสาธารณะ foo (วัตถุ params [] รายการ)

และโยนวัตถุตามประเภทหรือเพียงแค่ใช้อาร์เรย์ประเภทข้อมูลหรือคอลเลกชันขึ้นอยู่กับกรณีการใช้งานของคุณ


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

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

2

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

ก่อน:

collection.orderBy("column").limit(10);

หลังจาก:

collection = collection.orderBy("column").limit(10);

ในการดำเนินการ "ก่อน" ฟังก์ชั่นปรับเปลี่ยนวัตถุและสิ้นสุดใน return thisการดำเนินงานฟังก์ชั่นปรับเปลี่ยนวัตถุและสิ้นสุดลงใน ผมเปลี่ยนการดำเนินการเพื่อกลับวัตถุใหม่ประเภทเดียวกัน

เหตุผลของฉันสำหรับการเปลี่ยนแปลงนี้ :

  1. ค่าส่งคืนไม่เกี่ยวข้องกับฟังก์ชั่นมันเป็นเพียงแค่การสนับสนุนส่วนที่ผูกมัดมันควรจะเป็นฟังก์ชั่นโมฆะตาม OOP

  2. วิธีการผูกมัดในห้องสมุดระบบยังใช้มันอย่างนั้น (เช่น linq หรือสตริง):

    myText = myText.trim().toUpperCase();
    
  3. วัตถุดั้งเดิมยังคงไม่เปลี่ยนแปลงทำให้ผู้ใช้ API สามารถตัดสินใจได้ว่าจะทำอย่างไรกับมัน มันช่วยให้:

    page1 = collection.limit(10);
    page2 = collection.offset(10).limit(10);
    
  4. การคัดลอกสามารถใช้สำหรับการสร้างวัตถุ:

    painting = canvas.withBackground('white').withPenSize(10);
    

    ที่setBackground(color)ฟังก์ชั่นการเปลี่ยนแปลงตัวอย่างและผลตอบแทนอะไร(เช่นควรที่จะ)

  5. พฤติกรรมของฟังก์ชั่นสามารถคาดเดาได้มากขึ้น (ดูจุดที่ 1 & 2)

  6. การใช้ชื่อตัวแปรแบบสั้นยังสามารถลดความยุ่งเหยิงของรหัสได้โดยไม่ต้องบังคับใช้ API บนแบบจำลอง

    var p = participant; // create a reference
    p.addSchedule(events[1]);p.addSchedule(events[2]);p.setStatus('attending');p.save()
    

สรุป:
ในความคิดของฉันส่วนต่อประสานที่ใช้return thisงานได้คล่องนั้นผิด


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

@Apeiron Performance-wise แน่นอนว่ามันจะเร็วกว่าในreturn thisการจัดการหรืออย่างอื่น มันเป็นความคิดเห็นของฉันที่ได้รับ API ที่คล่องแคล่วในลักษณะ "ไม่เป็นธรรมชาติ" (เพิ่มเหตุผลที่ 6: เพื่อแสดงทางเลือกที่ไม่คล่องแคล่วซึ่งไม่มีค่าใช้จ่าย / เพิ่มฟังก์ชั่น)
Bob Fanger

ฉันเห็นด้วยกับคุณ. เป็นการดีที่สุดที่จะปล่อยให้สถานะพื้นฐานของ DSL ไม่มีการแตะต้องและส่งคืนวัตถุใหม่ทุกครั้งที่มีการเรียกใช้เมธอดแทน ... ฉันอยากรู้: ห้องสมุดอะไรที่คุณพูดถึง?
Lukas Eder

1

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

A.method1().method2().method3(); // one A

A.method1();
A.method2();
A.method3(); // repeating A 3 times

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

ในทางปฏิบัติประโยชน์มีขนาดเล็กในตัวอย่างนี้ แต่ถึงกระนั้นการพิมพ์ที่น้อยลงเล็กน้อยก็มีความสว่างมากขึ้น (DRY)


14
การทำซ้ำชื่อตัวแปรในซอร์สโค้ดไม่เกี่ยวข้องกับหลักการ DRY แห้งระบุว่า "ความรู้ทุกชิ้นจะต้องมีความเป็นหนึ่งเดียวกำกวมแทนอำนาจในระบบ" หรือในแง่อื่น ๆ หลีกเลี่ยงการซ้ำซ้อนของความรู้ (ไม่ใช่การซ้ำซ้อนของข้อความ )
pedromanoel

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

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

"คอมไพเลอร์" อาจกลับข้อผิดพลาดหรือบางทีคุณกำลังใช้ PHP หรือ JS และมีล่ามอาจส่งข้อผิดพลาดที่รันไทม์เมื่อคุณกดเงื่อนไขนี้
Anfurny

1

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

1. )

participant
    .addSchedule(events[1])
    .addSchedule(events[2])
    .setStatus('attending')
    .save();

VS

participant.addSchedule(events[1]);
participant.addSchedule(events[2]);
participant.setStatus('attending');
participant.save()

2. )

participant
    .getSchedule('monday')
        .saveTo('monnday.file');

VS

mondaySchedule = participant.getSchedule('monday');
mondaySchedule.saveTo('monday.file');

3. )

participant
    .attend(event)
    .setNotifications('silent')
    .getSocialStream('twitter')
        .postStatus('Joining '+event.name)
        .follow(event.getSocialId('twitter'));

VS

participant.attend(event);
participant.setNotifications('silent')
twitter = participant.getSocialStream('twitter')
twitter.postStatus('Joining '+event.name)
twitter.follow(event.getSocialId('twitter'));

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

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

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


0

ดี:

  1. มันสั้น แต่ช่วยให้คุณใส่มากขึ้นในบรรทัดเดียวอย่างสง่างาม
  2. บางครั้งคุณสามารถหลีกเลี่ยงการใช้ตัวแปรซึ่งบางครั้งอาจมีประโยชน์
  3. มันอาจทำงานได้ดีขึ้น

ไม่ดี:

  1. คุณกำลังใช้การส่งคืนโดยการเพิ่มฟังก์ชันการทำงานให้กับวิธีการบนวัตถุที่ไม่ได้เป็นส่วนหนึ่งของวิธีการที่จะทำ มันกำลังส่งคืนสิ่งที่คุณมีอยู่แล้วเพื่อประหยัดสองสามไบต์
  2. มันซ่อนสวิทช์บริบทเมื่อห่วงโซ่หนึ่งนำไปสู่อีก คุณสามารถรับสิ่งนี้ด้วย getters ยกเว้นจะค่อนข้างชัดเจนเมื่อบริบทเปลี่ยน
  3. การผูกสายมากกว่าหลายบรรทัดดูน่าเกลียดไม่เล่นได้ดีกับการเยื้องและอาจทำให้ผู้ปฏิบัติงานบางคนจัดการกับความสับสน (โดยเฉพาะในภาษาที่มี ASI)
  4. หากคุณต้องการเริ่มส่งคืนสิ่งอื่นซึ่งมีประโยชน์สำหรับวิธีการผูกมัดคุณอาจต้องแก้ไขหรือแก้ไขปัญหาให้หนักขึ้น
  5. คุณกำลังถ่ายการควบคุมไปยังเอนทิตีที่ปกติคุณจะไม่ได้ถ่ายไปเพื่อความสะดวกเพียงอย่างเดียวแม้ในภาษาที่พิมพ์ผิดอย่างเคร่งครัดซึ่งไม่สามารถตรวจพบได้
  6. มันอาจทำงานได้แย่ลง

ทั่วไป:

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

การผูกมัดอาจทำร้ายการอ่านได้ค่อนข้างรุนแรงในบางกรณีโดยเฉพาะเมื่อชั่งน้ำหนักในจุดที่ 1 และ 2

ในการกล่าวโทษมันอาจถูกนำไปใช้ในทางที่ผิดเช่นแทนที่จะใช้วิธีอื่น (ผ่านอาร์เรย์เป็นต้น) หรือวิธีการผสมในรูปแบบที่แปลกประหลาด (parent.setSomething (). getChild (). setSomething (). getParent (). setSomething ()


0

คำตอบที่ดื้อดึง

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

บางคำถาม:

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

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

เวลาการคอมไพล์อาจช้ากว่านี้ขึ้นอยู่กับภาษาและคอมไพเลอร์เนื่องจากนิพจน์สามารถแก้ไขได้ยากกว่ามาก

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


0

ในภาษาที่พิมพ์ (ขาดautoหรือเทียบเท่า) สิ่งนี้จะช่วยผู้ดำเนินการไม่ต้องประกาศประเภทของผลกลาง

import Participant
import Schedule

Participant participant = new Participant()
... snip...
Schedule s = participant.getSchedule(blah)
s.saveTo(filename)

สำหรับเครือข่ายที่ยาวขึ้นคุณอาจต้องเผชิญกับสื่อกลางหลายประเภทที่แตกต่างกันคุณจะต้องประกาศแต่ละประเภท

ฉันเชื่อว่าวิธีการนี้พัฒนาขึ้นจริงใน Java โดยที่ a) การเรียกใช้ฟังก์ชั่นทั้งหมดเป็นการเรียกใช้ฟังก์ชั่นสมาชิกและ b) ต้องการประเภทที่ชัดเจน แน่นอนว่ามีการแลกเปลี่ยนที่นี่ในแง่การสูญเสียบางอย่างชัดเจน แต่ในบางสถานการณ์บางคนคิดว่ามันคุ้มค่าในขณะที่

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