เหตุใดจึงเป็นความคิดที่ดีสำหรับเลเยอร์แอปพลิเคชัน“ ต่ำกว่า” ที่ไม่ควรระวังสำหรับเลเยอร์ "สูง"


66

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

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

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


10
อาจเป็นเพราะข้อมูล "ขึ้น" จากฐานข้อมูลไปยังมุมมอง มัน "เริ่มต้น" ที่ฐานข้อมูลและ "มาถึง" ที่มุมมอง การรับรู้เลเยอร์ไปในทิศทางตรงกันข้ามเมื่อข้อมูล "เดินทาง" ฉันชอบที่จะใช้ "คำพูด"
Jason Swett

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

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

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

1
เห็นได้ชัดว่ามุมมองตระหนักถึงรูปแบบมุมมองที่พิมพ์อย่างมาก
DazManCat

คำตอบ:


121

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

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

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


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

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

6
ดี @Andreas มีเสมอเมล
TRiG

6
ฉันคิดว่า "ง่ายต่อการเข้าใจ" ไม่เพียงพอ นอกจากนี้ยังเกี่ยวกับการทำให้ง่ายต่อการปรับขยายและบำรุงรักษาโค้ด
Mike Weller

1
@Peri: กฎหมายดังกล่าวไม่อยู่ดูen.wikipedia.org/wiki/Law_of_Demeter ไม่ว่าคุณจะเห็นด้วยกับเรื่องนี้หรือไม่ก็ตาม
Mike Chamberlain

61

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

ตัวอย่างที่ชัดเจนที่สุดคือการฉีกเลเยอร์ล่างออกมาและแทนที่อันอื่น นี่คือสิ่งที่คุณทำเมื่อคุณพัฒนาเลเยอร์บนกับการจำลองของฮาร์ดแวร์แล้วแทนที่ในฮาร์ดแวร์จริง

ตัวอย่างถัดไปคือเมื่อคุณฉีกเลเยอร์กลางออกและแทนที่เลเยอร์กลางที่แตกต่างกัน พิจารณาแอปพลิเคชั่นที่ใช้โปรโตคอลที่ทำงานผ่าน RS-232 วันหนึ่งคุณต้องเปลี่ยนการเข้ารหัสของโพรโทคอลอย่างสมบูรณ์เพราะ "สิ่งอื่นเปลี่ยนแปลง" (ตัวอย่าง: การสลับจากการเข้ารหัส ASCII แบบตรงเป็นการเข้ารหัสแบบ Reed-Solomon ของสตรีม ASCII เนื่องจากคุณทำงานผ่านลิงก์วิทยุจากตัวเมือง LA ไปยัง Marina Del Rey และตอนนี้คุณกำลังทำงานผ่านลิงก์วิทยุจากตัวเมือง LA ไปยังเครื่องมือสอบสวนที่โคจรรอบยูโรปา หนึ่งในดวงจันทร์ของดาวพฤหัสบดีและลิงก์นั้นต้องการการแก้ไขข้อผิดพลาดไปข้างหน้าได้ดีขึ้นมาก)

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

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

เลเยอร์ RS-232 ไม่ทราบว่ากำลังใช้งาน ASCII, Reed-Solomon, Unicode (หน้ารหัสภาษาอาหรับ, หน้ารหัสภาษาญี่ปุ่น, หน้ารหัส Rigellian Beta) หรืออะไร เพิ่งรู้ว่ามันได้รับลำดับของไบต์และมันกำลังเขียนไบต์เหล่านั้นไปยังพอร์ต สัปดาห์หน้าเขาอาจได้รับลำดับที่แตกต่างกันอย่างสิ้นเชิงจากสิ่งที่แตกต่างอย่างสิ้นเชิง เขาไม่สนใจ เขาแค่ย้ายไบต์

ครั้งแรก (และดีที่สุด) ชี้แจงของการออกแบบชั้นเป็น Dijkstra คลาสสิกของกระดาษ"โครงสร้างของ Multiprogramming ระบบ" มันจำเป็นต้องอ่านในธุรกิจนี้


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

+1 สำหรับตัวอย่างที่ยอดเยี่ยม ฉันชอบคำอธิบายของ
JRS

@ JasonSwett: ถ้าฉันพลิกเหรียญฉันจะพลิกมันจนกว่ามันจะระบุคำตอบ! ^^ +1 ถึงจอห์น
Olivier Dulac

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

Ding Ding Ding !!! ฉันคิดว่าคำที่คุณมองหาคือ 'decoupling' นั่นคือสิ่งที่ API ที่ดีมีไว้สำหรับ การกำหนดอินเตอร์เฟสสาธารณะของโมดูลเพื่อให้สามารถใช้งานได้ในระดับสากล
Evan Plaice

8

เพราะระดับที่สูงขึ้นอาจมีการเปลี่ยนแปลง

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


4

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

นี่คือข้อความที่ตัดตอนมา:

ข้อเสีย

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

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


4

IMO มันง่ายมาก คุณไม่สามารถใช้สิ่งที่อ้างอิงบริบทที่ใช้อยู่


4

เลเยอร์ไม่ควรมีการขึ้นต่อกันแบบสองทาง

ข้อดีของสถาปัตยกรรมแบบเลเยอร์คือเลเยอร์ควรใช้งานได้อย่างอิสระ:

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

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

ทิศทางการพึ่งพาควรเป็นไปตามทิศทางคำสั่ง

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

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

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

นอกจากนี้ยังมีวิธีอื่นในการรักษาทิศทางเดียวเช่น:

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

3

ฉันต้องการเพิ่มสองเซ็นต์ของฉันในสิ่งที่ Matt Fenwick และ Kilian Foth ได้อธิบายไว้แล้ว

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

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


3

แค่เล่น ๆ.

คิดถึงเชียร์ลีดเดอร์ปิรามิด แถวด้านล่างรองรับแถวด้านบน

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

หากเธอเงยหน้าขึ้นมองว่าทุกคนที่อยู่เหนือเธอกำลังทำอะไรเธอจะเสียความสมดุลทำให้กองทั้งกองล้มลง

ไม่ใช่ด้านเทคนิคจริงๆ แต่มันเป็นสิ่งที่ฉันคิดว่าอาจช่วยได้


3

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

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

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

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

มันแย่ลงในโครงการขนาดใหญ่ที่มีผู้พัฒนาจำนวนมาก (เช่นบางคนที่ทำงานเฉพาะชั้น A, ชั้น B และบางชั้น C) เนื่องจากมีแนวโน้มว่าการเปลี่ยนแปลงแต่ละครั้งจะต้องได้รับการตรวจสอบ / สนทนากับสมาชิกในเลเยอร์อื่น ๆ เพื่อให้แน่ใจว่าการเปลี่ยนแปลงของคุณจะไม่หยุดยั้งหรือบังคับให้ทำงานซ้ำกับสิ่งที่พวกเขากำลังทำอยู่ หากการเปลี่ยนแปลงของคุณบังคับให้ผู้อื่นเปลี่ยนแปลงคุณต้องโน้มน้าวใจพวกเขาว่าพวกเขาควรทำการเปลี่ยนแปลงเพราะพวกเขาจะไม่ต้องการทำงานเพิ่มมากขึ้นเพียงเพราะคุณมีวิธีการทำสิ่งใหม่ ๆ ในโมดูลของคุณ IOW ฝันร้ายของข้าราชการ

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


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

1

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

โปรดทราบว่า "เลเยอร์ที่ต่ำกว่า" เป็นชั้นกลางจริง ๆ

คิดเกี่ยวกับแอปพลิเคชันที่สื่อสารผ่านโลกภายนอกผ่านทางไดรเวอร์อุปกรณ์บางตัว ระบบปฏิบัติการเป็นที่อยู่ตรงกลาง

ระบบปฏิบัติการไม่ได้ขึ้นอยู่กับรายละเอียดภายในแอปพลิเคชันหรือภายในไดรเวอร์อุปกรณ์ มีไดรเวอร์อุปกรณ์ประเภทเดียวกันหลายชนิดและแบ่งใช้เฟรมเวิร์กไดรเวอร์อุปกรณ์เดียวกัน บางครั้งแฮกเกอร์เคอร์เนลต้องใส่เคสพิเศษเพื่อจัดการกับฮาร์ดแวร์หรืออุปกรณ์เฉพาะ (ตัวอย่างล่าสุดที่ฉันได้พบ: รหัสเฉพาะ PL2303 ในเฟรมเวิร์กแบบอนุกรมของ Linux) เมื่อเกิดเหตุการณ์ขึ้นพวกเขามักจะแสดงความคิดเห็นเกี่ยวกับปริมาณและปริมาณที่ควรลบออก แม้ว่าระบบปฏิบัติการจะเรียกใช้ฟังก์ชั่นในไดรเวอร์ แต่การโทรจะผ่าน hooks ที่ทำให้ไดรเวอร์ดูเหมือนกันในขณะที่เมื่อไดรเวอร์เรียกระบบปฏิบัติการพวกเขามักจะใช้ฟังก์ชั่นเฉพาะตามชื่อโดยตรง

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


ฉันมีความสุขตราบเท่าที่ฉันจะได้ไม่ต้องกังวลเกี่ยวกับการตั้งแรงดันไฟฟ้าที่เฉพาะเจาะจงเกี่ยวกับหมุด CPU เฉพาะ :)
CVn

1

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

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

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


1

ขยายคำตอบของ Kilian Foth ทิศทางของการฝังรากลึกนี้สอดคล้องกับทิศทางที่มนุษย์สำรวจระบบ

ลองนึกภาพว่าคุณเป็นนักพัฒนาซอฟต์แวร์คนใหม่ที่มีการแก้ไขข้อบกพร่องในระบบเลเยอร์

บั๊กมักจะไม่ตรงกันระหว่างสิ่งที่ลูกค้าต้องการและสิ่งที่เขาได้รับ ในขณะที่ลูกค้าสื่อสารกับระบบผ่าน UI และรับผลผ่าน UI (UI หมายถึง 'ส่วนต่อประสานผู้ใช้' อย่างแท้จริง) ข้อบกพร่องจะถูกรายงานในรูปของ UI เช่นกัน ดังนั้นในฐานะนักพัฒนาคุณไม่มีทางเลือกมากนัก แต่เริ่มมองหา UI ด้วยเพื่อที่จะเข้าใจว่าเกิดอะไรขึ้น

นั่นเป็นเหตุผลที่จำเป็นต้องมีการเชื่อมต่อเลเยอร์จากบนลงล่าง ทีนี้ทำไมเราถึงไม่มีการเชื่อมต่อทั้งสองทาง

คุณมีสามสถานการณ์ว่าบั๊กนั้นจะเกิดขึ้นได้อย่างไร

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

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

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

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

Layering เป็นเครื่องมือและมุ่งเป้าไปที่นักพัฒนาที่สนับสนุนระบบที่มีอยู่เป็นหลัก การเชื่อมต่อระหว่างเลเยอร์นั้นก็สะท้อนเช่นกัน


-1

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

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

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

สมมติว่าคุณต้องเขียนไดรเวอร์ 5 ตัวสำหรับอุปกรณ์ Modbus หนึ่งในนั้นใช้ Modbus TCP, สองตัวใช้ Modbus บน RS485 และที่เหลือไปผ่าน RS232 คุณจะไม่ใช้ Modbus 5 ครั้งเพราะคุณเขียนไดร์เวอร์ 5 ตัว นอกจากนี้คุณจะไม่ใช้ Modbus อีกครั้ง 3 ครั้งเพราะคุณมีชั้นกายภาพต่างกัน 3 ชั้นด้านล่าง

สิ่งที่คุณทำคือคุณเขียน TCP Media Access, RS485 Media Access และอาจเป็น RS232 Media Access มันฉลาดหรือไม่ที่จะรู้ว่าจะมีชั้น modbus ด้านบน ณ จุดนี้? อาจจะไม่. ไดรเวอร์ถัดไปที่คุณจะใช้งานอาจใช้ Ethernet แต่ใช้ HTTP-REST คงจะเป็นเรื่องน่าเสียดายถ้าคุณต้องใช้ Ethernet Media Access อีกครั้งเพื่อสื่อสารผ่าน HTTP

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

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


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