เราสามารถแทนที่การสืบทอดโดยใช้รูปแบบกลยุทธ์และการฉีดการพึ่งพาได้หรือไม่


10

ตัวอย่างเช่น:

var duckBehaviors = new Duckbehavior();
duckBehaviors.quackBehavior = new Quack();
duckBehaviors.flyBehavior = new FlyWithWings();
Duck mallardDuck = new Duck(DuckTypes.MallardDuck, duckBehaviors)

เนื่องจากคลาส Duck มีพฤติกรรมทั้งหมด (นามธรรม) การสร้างคลาสใหม่MallardDuck(ซึ่งขยายDuck) จึงไม่จำเป็นต้องมี

การอ้างอิง: รูปแบบการออกแบบส่วนหัวแรกบทที่ 1


ประเภทDuckbehavior.quackBehaviorและเขตข้อมูลอื่น ๆ ในรหัสของคุณคืออะไร
max630

ในตัวอย่างของคุณไม่มีการพึ่งพาการฉีด
David Conrad

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


5
@DavidConrad: สหกรณ์ไม่ได้ทำใหม่ไอเอ็นจีขึ้นภายในชั้นเรียน DI / IoC นั้นเกี่ยวกับการฉีดการพึ่งพาไม่ใช่เกี่ยวกับภาชนะบรรจุหรือไม่เคยใช้ "ใหม่"; นั่นเป็นปัญหามุมฉากทั้งหมด นอกจากนี้คุณต้องเปลี่ยนรหัสที่อื่นเสมอ - ไม่ว่าจะเป็น root การแต่งเพลงหรือไฟล์ปรับแต่ง การควบคุมจะกลับด้านที่นี่ภายในประเภทเป็ดเนื่องจากสิ่งที่ควบคุมการสร้างการพึ่งพาไม่ใช่ ctor เป็ด แต่มีบริบทภายนอกบางอย่าง นี่เป็นตัวอย่างของเล่นที่ให้ไว้เพื่อความชัดเจนมันเป็นเรื่องที่สมบูรณ์แบบที่บริบทภายนอกนั้นถูกแสดงด้วยรหัสการโทร
Filip Milovanović

คำตอบ:


21

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

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

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

ถ้าฉันมีคลาสเป็ดที่เป็นรูปธรรมฉันสามารถใช้มันให้เป็นพฤติกรรมการบินได้ ฉันยังสามารถให้มันสลับพฤติกรรมจาก fly-with-wing เป็น fly-with-Delta ตามตัวแปรสถานะ ตัวแปรนั้นอาจเป็นบูลีน, int หรืออาจเป็นวิธีFlyBehaviorที่มีflyรูปแบบการบินใด ๆ โดยที่ฉันไม่ต้องทดสอบด้วยถ้า ตอนนี้ฉันสามารถเปลี่ยนรูปแบบการบินได้โดยไม่ต้องเปลี่ยนประเภทเป็ด ตอนนี้ Mallards สามารถกลายเป็นนักบิน นี่คือองค์ประกอบและคณะผู้แทน เป็ดประกอบด้วย FlyBehavior และสามารถมอบหมายการบินให้กับมันได้ คุณสามารถแทนที่พฤติกรรมเป็ดทั้งหมดของคุณในคราวนี้ด้วยวิธีนี้หรือถือบางสิ่งบางอย่างสำหรับแต่ละพฤติกรรมหรือการรวมกันในระหว่าง

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

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

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

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

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

public class LoginFailure : System.ApplicationException {}

การสืบทอดช่วยให้คุณสร้างข้อยกเว้นด้วยชื่อที่มีความหมายที่เฉพาะเจาะจงมากขึ้นในหนึ่งบรรทัดเท่านั้น

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


1
" ไม่ประมาทค่าของชื่อที่ดี " เวลาเสื้อผ้าใหม่ของจักรพรรดิ: LoginExceptionไม่ใช่ชื่อที่ดี มันคือ "การตั้งชื่อ smurf" แบบคลาสสิกและถ้าฉันเอาไปExceptionทั้งหมดที่ฉันมีก็คือLoginมันจะบอกฉันว่าไม่มีอะไรผิดพลาด
David Arno

น่าเสียดายที่เราติดอยู่กับการประชุมที่ไร้สาระของการวางExceptionในตอนท้ายของทุกระดับยกเว้น แต่โปรดอย่าสับสน "ติดกับมัน" กับ "ดี"
David Arno

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

ฉันชื่อพวกเขา, StackOverflow, OutOfMemory, NullReferenceAccess, LoginFailureฯลฯ โดยทั่วไปใช้ "ข้อยกเว้น" ปิดชื่อ และถ้าจำเป็นต้องแก้ไขพวกเขาเพื่อที่จะอธิบายสิ่งที่ผิดพลาด
David Arno

@DavidArno โดยคำสั่งของคุณ
candied_orange

7

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

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

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

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


-1

การรับมรดกไม่สำคัญเท่าที่เคยคิดไว้ มันยังคงมีความสำคัญและการลบมันจะเป็นความผิดพลาดที่ไม่ดี

ตัวอย่างสุดขีดคือ Objective-C ซึ่งทุกอย่างเป็นคลาสย่อยของ NSObject (คอมไพเลอร์ไม่อนุญาตให้คุณประกาศคลาสที่ไม่มี baseclass ดังนั้นทุกสิ่งที่ไม่ได้สร้างไว้ในคอมไพเลอร์จะต้องเป็นคลาสย่อยของบางสิ่ง สร้างไว้ในคอมไพเลอร์) มีสิ่งที่มีประโยชน์มากมายที่สร้างขึ้นใน NSObject ที่คุณไม่ควรพลาด

อีกตัวอย่างที่น่าสนใจคือ UIView คลาส "มุมมอง" พื้นฐานในการพัฒนา UIKit สำหรับ iOS นี่เป็นคลาสที่อยู่ในมือข้างหนึ่งคล้ายกับคลาสนามธรรมที่ประกาศฟังก์ชันที่ subclasses ควรจะกรอก แต่มันก็มีประโยชน์ในตัวของมันเอง มันมีคลาสย่อยที่จัดทำโดย UIKIT ซึ่งมักจะใช้ตามที่เป็นอยู่ มีการจัดวางโดยนักพัฒนาติดตั้ง subviews ในมุมมอง และมักจะมีคลาสย่อยที่ผู้พัฒนากำหนดซึ่งมักจะใช้องค์ประกอบ ไม่มีกฎเดียวที่เข้มงวดหรือกฎแม้แต่คุณใช้สิ่งที่เหมาะกับความต้องการของคุณที่ดีที่สุด


"ตัวอย่างสุดขีด" ของคุณนั้นคร่าวๆว่าภาษา OO ที่ทันสมัยที่สุดทำงานอย่างไร: คลาสย่อย Python typeทุกคลาสที่ไม่ใช่แบบดั้งเดิมใน Java สืบทอดมาObjectทุกอย่างใน JavaScript มีต้นแบบที่ลงท้ายด้วยObjectเป็นต้น
Delioth

-1

[ฉันจะเสี่ยงต่อการตอบคำถามหน้าด้าน]

เราสามารถแทนที่การสืบทอดโดยใช้รูปแบบกลยุทธ์และการฉีดการพึ่งพาได้หรือไม่

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


2
" รูปแบบกลยุทธ์ใช้ได้กับการสืบทอดส่วนต่อประสาน " โปรดจำไว้ว่ารูปแบบกลยุทธ์คือรูปแบบการออกแบบ ไม่ใช่รูปแบบการนำไปปฏิบัติ ดังนั้นจึงสามารถใช้งานได้โดยใช้ส่วนต่อประสาน แต่สามารถนำไปใช้อย่างเท่าเทียมกันโดยใช้เช่นแฮช / พจนานุกรมของฟังก์ชัน
David Arno

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

บวกหนึ่งเพื่อน: ... ทำงานในการสืบทอดอินเทอร์เฟซรุ่งโรจน์ในการใช้งานที่เหมาะสมของคำว่า "อินเตอร์เฟซทั่วไป" ฉันคิดว่าการออกแบบชั้นเรียนไม่ดีหรือไร้ความสามารถเป็นเหตุผลพื้นฐานที่คำถามนี้หยิบยกขึ้นมา เพื่ออธิบายว่าทำไม / อย่างไรจึงจะใช้หนังสือ ปีศาจอยู่ในรายละเอียด ฉันทำงานออกแบบ w / มรดกที่มีความสุขที่ได้เห็นและในเวลาเดียวกันมรดกลึกที่เป็นนรก ฉันเคยเห็นinterfaceการออกแบบที่บ้าคลั่งแก้ไขด้วยการสืบทอด ปัญหาที่ซ่อนอยู่ที่นี่คือการใช้งานที่ไม่สอดคล้องกันซึ่งแปรเปลี่ยนพฤติกรรมตาม P, S การสืบทอดและส่วนต่อประสานไม่ได้เป็นเอกสิทธิ์เฉพาะบุคคล
Radarbob

@DavidArno ความคิดเห็น : ความคิดเห็นนี้จะดีถ้าแนบกับคำถาม OP คำถาม OP มีความผิดพลาดทางเทคนิค แต่เรายังคงได้รับมัน ปัญหาไม่ใช่คำตอบเฉพาะนี้
Radarbob

@DeepakMishra ความคิดเห็น: . "อินเตอร์เฟส" เป็นสมาชิกสาธารณะทั้งหมดของชั้นเรียน น่าเสียดายที่ "อินเทอร์เฟซ" มีความหมายมากเกินไป เราต้องใช้ความระมัดระวังในการแยกความหมายทั่วไปด้วยคำสำคัญของภาษาโปรแกรมinterface
Radarbob

-2

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

นอกจากนี้ฉันอาจต้องการตรรกะบางอย่างรอบประเภทเพื่อให้ลำดับชั้นของประเภทอาจจะดีกว่า

ตอนนี้หากการใช้ซ้ำรหัสกลายเป็นปัญหาสำหรับฟังก์ชันการทำงานบางอย่างเราสามารถเขียนได้โดยส่งผ่านฟังก์ชันผ่านตัวสร้าง

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

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

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