มันเป็นการปฏิบัติที่ไม่ถูกต้องหรือไม่ที่จะส่งอินสแตนซ์ผ่านหลายเลเยอร์


60

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

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

คำตอบ:


54

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

  • วัตถุฐานข้อมูลไม่ควรผ่านไปยังเลเยอร์ที่สูงขึ้น ฉันเคยเห็นโปรแกรมที่ใช้คลาสDataAdapterของ. NET , คลาส DB-access, และส่งผ่านไปยังเลเยอร์ UI, แทนที่จะใช้ DataAdapter ใน DAL, สร้าง DTO หรือชุดข้อมูล, และส่งต่อไป. การเข้าถึงฐานข้อมูลเป็นโดเมนของ DAL
  • แน่นอนว่าวัตถุ UI ควร จำกัด เฉพาะเลเยอร์ UI อีกครั้งฉันได้เห็นสิ่งนี้ละเมิดทั้งกล่องรายการที่บรรจุข้อมูลผู้ใช้ที่ส่งผ่านไปยังเลเยอร์ BL แทนที่จะเป็นอาเรย์ / DTO ของเนื้อหาและ (เป็นรายการโปรดของฉัน) คลาส DAL ที่ดึงข้อมูลลำดับชั้นจาก DB และแทนที่จะส่งคืนโครงสร้างข้อมูลแบบลำดับชั้นมันเพิ่งสร้างและบรรจุวัตถุ TreeView และส่งกลับไปยัง UI เพื่อเพิ่มแบบไดนามิกให้กับฟอร์ม

อย่างไรก็ตามหากอินสแตนซ์ที่คุณกำลังผ่านเป็น DTO หรือเอนทิตีเองก็อาจตกลง


1
สิ่งนี้อาจฟังดูน่าตกใจ แต่ในยุคแรก ๆ ของ. NET ซึ่งเป็นวิธีปฏิบัติที่แนะนำโดยทั่วไปและอาจดีกว่ากองอื่น ๆ ส่วนใหญ่ที่กำลังทำอยู่
ไวแอตต์บาร์เน็ตต์

1
ฉันไม่เห็นด้วย. มันเป็นความจริงที่ Microsoft รับรองการฝึกฝนของแอพชั้นเดียวที่ไคลเอนต์ Winforms เข้าถึง DB และ DataAdapter ได้ถูกเพิ่มโดยตรงไปยังแบบฟอร์มเป็นการควบคุมที่มองไม่เห็น แต่นั่นเป็นเพียงสถาปัตยกรรมเฉพาะที่แตกต่างจากการตั้งค่า N-tier ของ OP . แต่ในสถาปัตยกรรมหลายเลเยอร์และสิ่งนี้เป็นจริงสำหรับ VB6 / DNA แม้กระทั่งก่อนหน้า. NET วัตถุฐานข้อมูลยังคงอยู่ในเลเยอร์ DB
Avner Shahar-Kashtan

ในการชี้แจง: คุณเคยเห็นคนเข้าถึง UI โดยตรง (ในตัวอย่างกล่องรายการ) จาก "ชั้นข้อมูล" หรือไม่ ฉันไม่คิดว่าฉันเจอการละเมิดในรหัสการผลิต .. ว้าว ..
Simon Whitehead

7
@SimonWhitehead แน่นอน ผู้ที่สับสนเกี่ยวกับความแตกต่างระหว่างกล่องรายการและอาร์เรย์และใช้กล่องรายการเป็น DTO มันเป็นช่วงเวลาที่ช่วยให้ฉันรู้ว่าฉันมีสมมติฐานที่มองไม่เห็นมากมายเท่าไรที่คนอื่นไม่เข้าใจ
Avner Shahar-Kashtan

1
@SimonWhitehead - ใช่แล้วฉันเคยเห็นว่าในโปรแกรม VB6 และ VB.NET Framework 1.1 และ 2.0 และได้รับมอบหมายให้ดูแลสัตว์ประหลาดเหล่านี้ มันน่าเกลียดมากเร็วมาก
jfrankcarr

15

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

มีการอภิปรายที่ยอดเยี่ยมเกี่ยวกับความไม่สามารถเปลี่ยนแปลงได้ของ Eric Lippert ในบล็อกของเขา

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


13

การส่งอินสแตนซ์ของวัตถุรอบ ๆ เป็นเรื่องปกติที่ต้องทำ จะช่วยลดความจำเป็นในการรักษาสถานะ (เช่นตัวแปรอินสแตนซ์) และแยกรหัสออกจากบริบทการดำเนินการ

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


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

8

อาจจะน้อย แต่มีความเสี่ยงในการมอบหมายการอ้างอิงนี้ที่ใดที่หนึ่งในเลเยอร์ที่อาจทำให้เกิดการอ้างอิงห้อยหรือหน่วยความจำรั่วในภายหลัง ..


ประเด็นของคุณถูกต้อง แต่จากคำศัพท์ของ OP ("การผ่านวัตถุตัวอย่าง") ฉันรู้สึกว่าเขากำลังผ่านค่า (ไม่ใช่ตัวชี้) หรือเขากำลังพูดถึงสภาพแวดล้อมที่เก็บขยะ (Java, C #, Python, Go, .. )
Mohammad Dehghan

7

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


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

ฉันคิดว่าฉันได้พบคำตอบที่นี่: martinfowler.com/articles/injection.html
Puckl

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

4

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


3

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

ในโครงการของเราเราใช้แบบฝึกหัดนี้เพื่อส่งต่อDTOs (Data transfer Object)ระหว่างเลเยอร์และเป็นวิธีปฏิบัติที่มีประโยชน์มาก นอกจากนี้เรายังนำวัตถุ dto ของเรากลับมาใช้ใหม่เพื่อสร้างความซับซ้อนมากขึ้นหนึ่งครั้งเช่นสำหรับข้อมูลสรุป


3

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

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

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

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


2

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


2

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


2

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

ทางออกหนึ่งที่เป็นไปได้คือ "แบน" การอ้างอิงแบบซ้อนในคลาสคอนโทรลเลอร์

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

สิ่งนี้ถูกนำไปใช้อย่างไร (หรือหากเป็นโซลูชันที่ถูกต้อง) ก็ขึ้นอยู่กับการออกแบบระบบในปัจจุบันเช่น:

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

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

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


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