เหตุใดอินเทอร์เฟซจึงมีประโยชน์


158

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

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

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


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

37
ลืม C # ลืมจาวาลืมภาษา มันแค่คิดในแง่ของ OO ฉันอยากจะแนะนำให้คุณอ่านเนื้อหาจากคนอย่างโรเบิร์ตซีมาร์ตินมาร์ตินฟาวเลอร์ไมเคิลฟีเธอร์แก๊งของโฟร์ ฯลฯ เพราะมันจะช่วยขยายความคิดของคุณ
Anthony Pegram

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

37
คุณมีเพื่อนมากมายที่จะเรียนรู้
ChaosPandion

60
@ChaosPandion เราทุกคนมีจำนวนมากที่จะเรียนรู้
kenwarner

คำตอบ:


151

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

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

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

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

ฉันดีใจที่คุณชอบ

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


ฉันเพิ่งอ่านว่า "Microsoft สร้างปัญหาโดยไม่อนุญาตให้มีการสืบทอดหลายครั้ง" และฉันคิดว่า Eric Lippert จะมีบางอย่างที่จะพูดเกี่ยวกับเรื่องนั้น
กำหนดค่า

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

17
@configurator: มันไม่สมบูรณ์เพราะมันไม่สมบูรณ์ การตรวจสอบโปรแกรมแบบสแตติกด้วยสัญญาตามอำเภอใจนั้นเทียบเท่ากับการแก้ปัญหาการหยุดงาน (ตัวอย่างเช่นคุณสามารถเขียนสัญญาโค้ดที่ระบุว่าอาร์กิวเมนต์ของวิธีการนั้นจะต้องเป็นตัวอย่างของทฤษฎีบทสุดท้ายของแฟร์มาต์ แต่ตัวตรวจสอบแบบคงที่จะไม่สามารถยืนยันได้ว่าไม่มีข้อโต้แย้งดังกล่าว) ต้องใช้อย่างรอบคอบหากคุณคาดว่าตัวตรวจสอบแบบคงที่จะทำงานให้เสร็จก่อนที่ความร้อนจากเอกภพจะตาย (หากการร้องเรียนของคุณคือ BCL นั้นมีหมายเหตุประกอบไม่เพียงพอ: ฉันเห็นด้วย)
Eric Lippert

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

9
+1 สำหรับ "ฉันดีใจที่คุณชอบ" และทุกสิ่งอื่น ๆ ด้วย
Robert S.

235

ป้อนคำอธิบายรูปภาพที่นี่

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

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


4
วัตถุด้านล่างซ้ายดูเหมือนจะใช้ IPowerPlug =)
Steven Striga

100
ประณาม แต่เราจะต้องใช้รูปแบบอะแดปเตอร์เพื่อใช้ IPowerPlug ในประเทศต่างๆ!
Steven Jeuris

Jerry-rigging อุปกรณ์ที่ใช้ทำงานกับส่วนต่อประสานนั้นไม่ปลอดภัย (แต่บางคนก็ยังอ้างว่าเป็นข้ออ้างที่ยังไม่ผ่านการทดสอบ) และมักจะทำให้เกิดไฟไหม้
YoungJohn

4
คำตอบนี้เป็นที่น่าตื่นตาตื่นใจเพียงแค่ ...
มาริโอการ์เซีย

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

145

นอกเหนือจากการจัดเตรียมลายเซ็นของฟังก์ชันพวกเขาไม่ทำอะไรเลย ถ้าฉันจำชื่อและลายเซ็นของฟังก์ชั่นที่จำเป็นต้องใช้งานได้ก็ไม่จำเป็นต้องทำ

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

ขอบคุณอินเทอร์เฟซคุณสามารถบรรลุข้อต่อที่ต่ำมากระหว่างสองคลาส


เรากำลังคุยกันเรื่องคำตอบนี้ในการแชท
Daniel

ฉันไม่ชอบการใช้คำว่า "การพิมพ์เป็ด" เพราะมันหมายถึงสิ่งที่แตกต่างกันสำหรับผู้คนที่แตกต่างกัน เราใช้การจับคู่รูปแบบสำหรับลูป "foreach" เพราะเมื่อมันถูกออกแบบ IEnumerable <T> จะไม่สามารถใช้งานได้ เราใช้การจับคู่รูปแบบสำหรับ LINQ เนื่องจากระบบประเภท C # นั้นอ่อนแอเกินกว่าที่จะจับภาพ "รูปแบบ monad" ที่เราต้องการ คุณต้องการบางอย่างเช่นระบบประเภท Haskell
Eric Lippert

@Eric: "การจับคู่รูปแบบ" ไม่มีปัญหาเดียวกันหรือไม่ เมื่อฉันได้ยินฉันคิดว่า F # / Scala / Haskell แต่ฉันคิดว่ามันเป็นแนวคิดที่กว้างกว่าการพิมพ์โดยใช้เป็ด
Daniel

@Daniel: ใช่ฉันคิดว่ามันเป็นอย่างนั้น มันเป็นหนึ่งในหกครึ่งและอีกครึ่งหนึ่งที่ฉันเดา!
Eric Lippert

36

การเชื่อมต่อเป็นวิธีที่ดีที่สุดในการรักษาโครงสร้างที่แยกได้อย่างดี

เมื่อเขียนแบบทดสอบคุณจะพบว่าคลาสรูปธรรมจะไม่ทำงานในสภาพแวดล้อมการทดสอบของคุณ

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

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

ในทางกลับกัน WPF & Silverlight ไม่เล่นเลยด้วยอินเทอร์เฟซเมื่อพูดถึงการผูก นี่เป็นรอยย่นที่น่ารังเกียจ


2
ได้ยินได้ยิน! อินเทอร์เฟซถูกคิดค้นเพื่อแก้ปัญหาอื่น ๆ เช่น polymorphism อย่างไรก็ตามสำหรับฉันพวกเขามาเป็นของตัวเองเมื่อใช้รูปแบบการฉีดพึ่งพา
andy

29

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

คุณใช้ประโยชน์จากมันบ่อยแค่ไหนและอะไรทำให้คุณทำเช่นนั้น ??

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

ต้องการอัลกอริทึมที่แตกต่างเพื่อดำเนินการกับข้อมูลเดียวกันหรือไม่ ใช้อินเทอร์เฟซ ( ดูรูปแบบกลยุทธ์ )!

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

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


1
ไม่เคยได้ยินเรื่อง polymorphy คุณหมายถึง polymorphism หรือไม่?
Steven Jeuris

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

10
@Pankaj Upadhyay: การสืบทอดและอินเทอร์เฟซหลายคู่เป็นรองเท้าที่แตกต่างกันสองคู่ ถ้าคุณต้องการอินเทอร์เฟซของคลาสที่ไม่เกี่ยวข้องสองคลาสที่มีพฤติกรรมแตกต่างกัน คุณไม่สามารถแก้ไขได้ด้วยการรับมรดกหลายครั้ง คุณจะต้องใช้มันแยกกันอยู่ดี จากนั้นคุณจะต้องมีสิ่งที่จะอธิบายอินเทอร์เฟซเพื่อให้มีพฤติกรรมที่หลากหลาย การรับมรดกหลายครั้งเป็นตรอกซอกซอยที่จะเดินลงไปในหลาย ๆ กรณีและมันง่ายเกินไปที่จะยิงตัวเองในไม่ช้าก็เร็ว
เหยี่ยว

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

9
@Pankaj ถ้านั่นคือทั้งหมดที่คุณต้องการสำหรับอินเตอร์เฟสแล้วอย่าใช้มัน คุณใช้อินเทอร์เฟซเมื่อคุณมีโปรแกรมที่ต้องการที่จะไม่รู้ทุกแง่มุมของชั้นเรียนและเข้าถึงได้ตามประเภทฐานของมันเช่นอินเทอร์เฟซ พวกเขาไม่จำเป็นต้องรู้ subclasses ใด ๆ เพียงว่าเป็นประเภทของอินเทอร์เฟซ ดังนั้นคุณสามารถเรียกวิธีการใช้งานของคลาสย่อยผ่านการอ้างอิงถึงอินเทอร์เฟซของวัตถุ นี่คือสิ่งที่สืบทอดขั้นพื้นฐานและการออกแบบ ถ้าไม่มีมันคุณก็อาจใช้ C. แม้ว่าคุณจะไม่ได้ใช้มันอย่างชัดเจน แต่เฟรมเวิร์กจะไม่ทำงานหากไม่มีมัน
Jonathan Henson

12

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

แน่นอนว่าเป็นกรณีที่พูดถึงประโยชน์ของอินเทอร์เฟซ


5
ที่จริง foreach ไม่ต้องการ IEnumerable: msdn.microsoft.com/en-us/library/9yb8xew9%28VS.80%29.aspx
Matt H

1
ฉันได้ศึกษาทั้งหมดแล้ว แต่มันเหมือนกับการถือหูด้วยมืออื่น ๆ หากอนุญาตให้ใช้หลายมรดกได้อินเตอร์เฟสจะเป็นตัวเลือกที่ดี
Pankaj Upadhyay

2
การเชื่อมต่อเป็นมรดกหลายอย่างสิ่งที่มักถูกลืม อย่างไรก็ตามพวกเขาไม่อนุญาตให้สืบทอดพฤติกรรมและสถานะหลายอย่าง มิกซ์อินหรือลักษณะที่อนุญาตให้มีการสืบทอดพฤติกรรมหลายอย่าง แต่ไม่ใช่สถานะที่แชร์ซึ่งทำให้เกิดปัญหา: en.wikipedia.org/wiki/Mixin
แมตต์เอช

@Panaj, offtopic แต่คุณรังเกียจไหมถ้าฉันถามว่าภาษาพื้นเมืองของคุณคืออะไร? "การถือหูด้วยมืออื่น ๆ " เป็นสำนวนที่ดีและฉันก็อยากรู้ว่ามันมาจากไหน
เควิน

3
@Iceman ฮ่า ๆ .... ฉันมาจากอินเดีย และนี่มันเป็นสำนวนสามัญที่สะท้อนถึงการทำสิ่งง่าย ๆ ในแบบที่ยากลำบาก
Pankaj Upadhyay

11

อินเทอร์เฟซเป็นการเข้ารหัสวัตถุเช่นปลั๊กเป็นการเดินสายภายในบ้าน คุณจะประสานวิทยุของคุณเข้ากับสายไฟบ้านโดยตรงหรือไม่? เครื่องดูดฝุ่นของคุณล่ะ ไม่แน่นอน ปลั๊กและเต้ารับที่ใช้เป็นรูปแบบ "อินเทอร์เฟซ" ระหว่างการเดินสายไฟในบ้านของคุณและอุปกรณ์ที่ต้องการพลังงานจากมัน การเดินสายไฟภายในบ้านของคุณไม่จำเป็นต้องรู้อะไรเกี่ยวกับอุปกรณ์อื่นนอกจากใช้ปลั๊กที่มีสายดินสามขาและต้องใช้พลังงานไฟฟ้าที่ 120VAC <= 15A ในทางกลับกันอุปกรณ์ไม่จำเป็นต้องมีความรู้ที่เฉียบคมว่าบ้านของคุณมีสายอย่างไรนอกจากนั้นจะมีร้านสามขาอย่างน้อยหนึ่งแห่งตั้งอยู่ในทำเลที่สะดวกที่ให้ 120VAC <= 15A

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

ดังนั้นด้วยการทำให้วัตถุหลาย ๆ อันอยู่เบื้องหลังอินเทอร์เฟซเดียวกันทำให้คุณมีฟังก์ชั่นการใช้งานทั่วไปสำหรับผู้ใช้งานของวัตถุของอินเตอร์เฟสนั้น ๆ คุณไม่จำเป็นต้องรู้วัตถุตัวอย่างเช่น List, Dictionary, LinkedList, OrderedList หรืออะไรก็ตาม เนื่องจากคุณรู้ว่าสิ่งเหล่านี้คือ IEnumerables คุณสามารถใช้วิธีการของ IEnumerable เพื่อผ่านแต่ละองค์ประกอบในคอลเล็กชันเหล่านี้ทีละรายการ คุณไม่จำเป็นต้องรู้ว่าคลาสเอาต์พุตเป็น ConsoleWriter, FileWriter, NetworkStreamWriter หรือแม้แต่ MulticastWriter ที่ใช้ตัวเขียนชนิดอื่น สิ่งที่คุณต้องรู้ก็คือพวกเขาคือ IWriters ทั้งหมด (หรืออะไรก็ตาม) ดังนั้นพวกเขาจึงมีวิธีการ "เขียน" ที่คุณสามารถส่งผ่านสตริงไปยังและสตริงนั้นจะถูกส่งออก


7

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

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

สำหรับ C ++, google "ลำดับชั้นไดมอนด์ C ++" ของ Google และดูความอัปลักษณ์ที่กำลังจะครอบคลุมคุณ ข้อดี C ++ รู้วิธีใช้หลายมรดก คนอื่นมักจะเล่นรอบโดยไม่ทราบว่าผลลัพธ์จะเป็นอย่างไร อีกสิ่งหนึ่งที่แสดงให้เห็นว่ามีประโยชน์อย่างไรอินเตอร์เฟสคือความจริงที่ว่าในหลายกรณีคลาสอาจต้องการแทนที่พฤติกรรมของผู้ปกครองอย่างสมบูรณ์ ในกรณีดังกล่าวการใช้งานพาเรนต์จะไม่จำเป็นและเป็นภาระให้กับคลาสลูกด้วยหน่วยความจำสำหรับตัวแปรส่วนตัวของพาเรนต์ซึ่งอาจไม่สำคัญใน C # อายุ แต่มีความสำคัญเมื่อคุณทำการเขียนโปรแกรมแบบฝัง หากคุณใช้อินเทอร์เฟซปัญหานั้นจะไม่มีอยู่

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

นอกจากนี้ในอดีตความคิดสำหรับอินเทอร์เฟซถูกฝังรากลึกกว่ารายละเอียดการออกแบบ C # ของ Microsoft คนส่วนใหญ่คิดว่า C # นั้นเป็นการอัพเกรดเหนือ Java (โดยส่วนใหญ่) และคาดเดาว่า C # ได้รับส่วนต่อประสานจาก - Java โพรโทคอลเป็นคำเก่าสำหรับแนวคิดเดียวกันและเป็นวิธีเก่ากว่า. NET

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


6

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

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


5

ฉันชอบคลาสนามธรรมโดยส่วนตัวและใช้มากกว่าอินเทอร์เฟซ ความแตกต่างที่สำคัญมาพร้อมกับการรวมกับ. NET อินเตอร์เฟสเช่น IDisposable, IEnumerable และอื่น ๆ ... และด้วย COM interop นอกจากนี้อินเตอร์เฟสยังมีความพยายามในการเขียนน้อยกว่าคลาสนามธรรมและคลาสสามารถใช้อินเตอร์เฟซมากกว่าหนึ่งขณะที่มันสามารถสืบทอดจากคลาสหนึ่งได้เท่านั้น

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

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

ฉันใช้อินเทอร์เฟซอย่างกว้างขวางกับการเขียนสภาพแวดล้อมปลั๊กอินโดยใช้เนมสเปซ System.ComponentModel พวกเขามาค่อนข้างสะดวก


1
ใส่ได้ดีมาก! ฉันคาดเดาว่าคุณจะชอบบทความของฉันในคลาสนามธรรม เป็นทุกสิ่งที่เป็นนามธรรม
Steven Jeuris

5

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

ให้ฉันลองสองวิธี และให้อภัยฉันสำหรับภาพรวม

ลอง 1

สมมติว่าคุณเป็นเจ้าของภาษา คุณไปประเทศอื่นที่ภาษาอังกฤษไม่ใช่ภาษาพื้นเมือง คุณต้องการความช่วยเหลือ. คุณต้องการคนที่สามารถช่วยคุณได้

คุณถาม: "เฮ้คุณเกิดในสหรัฐอเมริกาหรือไม่" นี่คือมรดก

หรือคุณถามว่า "เฮ้คุณพูดภาษาอังกฤษได้ไหม" นี่คือส่วนต่อประสาน

หากคุณสนใจสิ่งที่ทำคุณสามารถพึ่งพาอินเทอร์เฟซ หากคุณสนใจว่าอะไรคือสิ่งที่คุณต้องพึ่งพามรดก

ไม่ต้องพึ่งพามรดก หากคุณต้องการใครสักคนที่พูดภาษาอังกฤษชอบดื่มน้ำชาและชอบเล่นฟุตบอล :)

ลอง 2

ตกลงลองอีกตัวอย่างหนึ่ง

คุณใช้ฐานข้อมูลที่แตกต่างกันและคุณต้องใช้คลาสนามธรรมเพื่อทำงานกับพวกเขา คุณจะผ่านชั้นเรียนของคุณไปยังชั้นเรียนจากผู้จำหน่าย DB

public abstract class SuperDatabaseHelper
{
   void Connect (string User, string Password)
}

public abstract class HiperDatabaseHelper
{
   void Connect (string Password, string User)
}

หลายมรดกคุณพูดว่า ลองดูด้วยตัวพิมพ์ด้านบน คุณทำไม่ได้ คอมไพเลอร์จะไม่ทราบวิธีการเชื่อมต่อที่คุณพยายามโทร

interface ISuperDatabaseHelper
{
  void Connect (string User, string Password)
}

interface IHiperDatabaseHelper
{
   void Connect (string Password, string User)
}

ตอนนี้มีบางอย่างที่เราสามารถใช้งานได้ - อย่างน้อยใน C # - ที่ซึ่งเราสามารถใช้อินเตอร์เฟสได้อย่างชัดเจน

public class MyDatabaseHelper : ISuperDatabaseHelper, IHiperDatabaseHelper
{
   IHiperDataBaseHelper.Connect(string Password, string User)
   {
      //
   }

   ISuperDataBaseHelper.Connect(string User, string Password)
   {
      //
   }

}

ข้อสรุป

ตัวอย่างไม่ได้ดีที่สุด แต่ฉันคิดว่ามันเป็นประเด็นที่ได้รับ

คุณจะ "รับ" อินเทอร์เฟซเฉพาะเมื่อคุณรู้สึกว่าต้องการมัน จนกว่าคุณจะคิดว่าไม่เหมาะกับคุณ


ความพยายามครั้งแรกคือสิ่งที่ให้คะแนนของฉัน
osundblad

1
ตอนนี้ฉันกำลังใช้การเปรียบเทียบอเมริกันเทียบกับผู้พูดภาษาอังกฤษต่อไป นั่นวิเศษมาก
ไบรอัน Boettcher

อธิบายในวิธีที่ง่ายกว่า! น่าอัศจรรย์
Aimal Khan

4

มี 2 ​​เหตุผลหลัก:

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

จุดที่ 1 คือเหตุผลและเหตุผลที่พัฒนาโดยนักพัฒนาไมโครซอฟท์เอง
Pankaj Upadhyay

1
@Pankja จริง ๆ แล้วพวกเขาใช้แนวคิดอินเทอร์เฟซจาก Java (เช่นส่วนที่ดีของคุณสมบัติของ C #)
Oliver Weiler

3

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


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

3

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


2

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

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


2

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


1

การใช้งานในโลกแห่งความจริง:

คุณสามารถส่งวัตถุเป็นประเภทอินเตอร์เฟส:

IHelper h = (IHelper)o;
h.HelperMethod();

คุณสามารถสร้างรายการส่วนต่อประสาน

List<IHelper> HelperList = new List<IHelper>();

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


0

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

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

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

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

ตัวอย่างเช่นคุณสามารถแทนที่ ...

Me.PublishDate.Clear()
Me.Subject.Clear()
Me.Body.Clear()

... ด้วย:

For Each ctl As IClear In Me.Controls.OfType(Of IClear)()
    ctl.Clear()
Next

ซึ่งฟังดูมีประสิทธิภาพมากเช่น:

ได้ยินเจ้าได้ยินเจ้า! ทุกคนที่จะเข้าใจการล้างโปรดในClearขณะนี้!

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


0

ต่อไปนี้คือ pseudocode:

class MyClass{

    private MyInterface = new MyInterfaceImplementationB();

    // Code using Thingy 

}

interface MyInterface{

    myMethod();

}

class MyInterfaceImplementationA{ myMethod(){ // method implementation A } }

class MyInterfaceImplementationB{ myMethod(){ // method implementation B } }

class MyInterfaceImplementationC{ myMethod(){ // method implementation C } }

คลาสสุดท้ายอาจเป็นการปรับใช้ที่แตกต่างกันอย่างสิ้นเชิง

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

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

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