เหตุใดอินเทอร์เฟซจึงมีประโยชน์มากกว่าซูเปอร์คลาสในการรับคลัปหลวม


15

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

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

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

ตัวอย่างเช่นพิจารณาชั้นCarและสอง subclasses และVolvoMazda

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

นอกจากนี้Car- ซึ่งเป็นนามธรรม - มีโอกาสน้อยที่จะมีการเปลี่ยนแปลงกว่าหรือVolvo Mazdaโดยทั่วไปรถยนต์มักจะเหมือนกันมาระยะหนึ่งแล้ว แต่ Volvos และ Mazdas มีแนวโน้มที่จะเปลี่ยนไป I abstractions มีความเสถียรมากกว่าแบบคอนกรีต

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

สิ่งที่ฉันไม่เข้าใจคือ:

Abstractions สามารถเป็น superclasses หรืออินเทอร์เฟซ

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

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

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


3
ภาษาส่วนใหญ่ (เช่น Java, C #) ที่มี "อินเทอร์เฟซ" สนับสนุนการสืบทอดเดียวเท่านั้น เนื่องจากแต่ละคลาสสามารถมีซูเปอร์คลาสได้ทันทีหนึ่งคลาสเท่านั้น (นามธรรม) ซูเปอร์คลาสนั้น จำกัด เกินไปที่จะให้วัตถุหนึ่งชิ้นที่สนับสนุน abstractions หลายรายการ ตรวจสอบลักษณะ (เช่นScalaหรือบทบาทของ Perl ) สำหรับทางเลือกที่ทันสมัยซึ่งหลีกเลี่ยง " ปัญหาเพชร " ที่มีการสืบทอดหลายแบบ
amon

@ มอนดังนั้นคุณกำลังบอกว่าข้อดีของอินเทอร์เฟซสำหรับคลาสนามธรรมเมื่อพยายามที่จะบรรลุคลัปคับคือพวกเขาไม่ได้ถูก จำกัด ด้วยการสืบทอดเพียงครั้งเดียว?
Aviv Cohn

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

2
ดูเหมือน @amon อยู่ในเส้นทางที่ถูกต้องฉันพบโพสต์นี้ซึ่งมีการกล่าวว่า: interfaces are essential for single-inheritance languages like Java and C# because that's the only way in which you can aggregate different behaviors into a single class(ซึ่งทำให้ฉันเปรียบเทียบกับ C ++ ซึ่งอินเตอร์เฟสเป็นเพียงคลาสที่มีฟังก์ชันเสมือนจริงเท่านั้น)
สีขาว

โปรดบอกผู้ที่บอกว่าซุปเปอร์คลาสแย่
Tulains Córdova

คำตอบ:


11

คำศัพท์: ฉันจะอ้างถึงการสร้างภาษาinterfaceเป็นอินเทอร์เฟซและอินเทอร์เฟซของชนิดหรือวัตถุเป็นพื้นผิว (สำหรับการขาดคำที่ดีกว่า)

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

แก้ไข.

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

ไม่ค่อยถูกต้องนัก ภาษาในปัจจุบันไม่คาดว่าจะมีการเปลี่ยนแปลง abstraction (แม้ว่าจะมีรูปแบบการออกแบบที่จะจัดการกับมัน) การแยกข้อมูลจำเพาะออกจากสิ่งทั่วไปคือสิ่งที่เป็นนามธรรม นี้มักจะทำโดยบางชั้นของนามธรรม เลเยอร์นี้สามารถเปลี่ยนเป็นข้อมูลจำเพาะอื่น ๆ ได้โดยไม่ต้องทำลายรหัสที่สร้างขึ้นบนสิ่งที่เป็นนามธรรมนี้ - การเชื่อมต่อแบบหลวม ๆ นั้นทำได้ ตัวอย่างที่ไม่ใช่ OOP: sortรูทีนอาจถูกเปลี่ยนจาก Quicksort ในเวอร์ชัน 1 เป็น Tim Sort ในเวอร์ชัน 2 โค้ดที่ขึ้นอยู่กับผลลัพธ์ที่เรียงลำดับเท่านั้น (เช่นการสร้างบนsortabstraction) จึงถูกแยกจากการใช้งานการเรียงลำดับจริง

สิ่งที่ฉันเรียกว่าพื้นผิวด้านบนคือส่วนทั่วไปของสิ่งที่เป็นนามธรรม ขณะนี้เกิดขึ้นใน OOP ว่าบางครั้งวัตถุหนึ่งต้องรองรับ abstractions หลายรายการ ตัวอย่างที่ไม่เหมาะสมที่สุด: Java java.util.LinkedListรองรับทั้งListอินเทอร์เฟซซึ่งเป็นเรื่องเกี่ยวกับสิ่งที่เป็นนามธรรม "ที่รวบรวมได้ซึ่งจัดทำดัชนีได้" และสนับสนุนQueueอินเทอร์เฟซที่ (ในแง่ที่หยาบ) เป็นเรื่องเกี่ยวกับสิ่งที่เป็นนามธรรม

วัตถุสามารถรองรับ abstractions หลายแบบได้อย่างไร

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

ปัญหาในที่นี้คือการสืบทอดหลายอย่างสามารถนำไปสู่ปัญหาเพชรซึ่งลำดับที่ค้นหาคลาสสำหรับการใช้งานวิธี (MRO: ลำดับการแก้ไขเมธอด) สามารถนำไปสู่ ​​"ความขัดแย้ง" มีสองคำตอบสำหรับสิ่งนี้:

  1. กำหนดคำสั่งที่มีสติและปฏิเสธคำสั่งที่ไม่สามารถทำให้เป็นเส้นตรงอย่างสมเหตุสมผล C3 MROเป็นธรรมสมเหตุสมผลและทำงานได้ดี ได้รับการตีพิมพ์ 2539

  2. ใช้เส้นทางที่ง่ายและปฏิเสธการรับมรดกหลายรายการตลอด

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

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

สิ่งนี้กลับกลายเป็นสิ่งที่ไม่น่าพึงพอใจเพราะบ่อยครั้งที่พฤติกรรมบางอย่างเป็นส่วนหนึ่งของพื้นผิว พิจารณาComparableอินเทอร์เฟซ:

interface Comparable<T> {
    public int cmp(T that);
    public boolean lt(T that);  // less than
    public boolean le(T that);  // less than or equal
    public boolean eq(T that);  // equal
    public boolean ne(T that);  // not equal
    public boolean ge(T that);  // greater than or equal
    public boolean gt(T that);  // greater than
}

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

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

Comparableอินเตอร์เฟซที่อาจจะแสดงออกใน Scala เป็น

trait Comparable[T] {
    def cmp(that: T): Int
    def lt(that: T): Boolean = this.cmp(that) <  0
    def le(that: T): Boolean = this.cmp(that) <= 0
    ...
}

เมื่อคลาสใช้ลักษณะนั้นเมธอดอื่น ๆ จะถูกเพิ่มเข้ากับนิยามคลาส:

// "extends" isn't different from Java's "implements" in this case
case class Inty(val x: Int) extends Comparable[Inty] {
    override def cmp(that: Inty) = this.x - that.x
    // lt etc. get added automatically
}

ดังนั้นInty(4) cmp Inty(6)จะเป็น-2และจะเป็นInty(4) lt Inty(6)true

หลายภาษามีการสนับสนุนคุณสมบัติบางอย่างและภาษาใดก็ตามที่มี“ Metaobject Protocol (MOP)” สามารถเพิ่มคุณสมบัติได้ การอัพเดต Java 8 ล่าสุดเพิ่มเมธอดดีฟอลต์ซึ่งคล้ายกับลักษณะ (วิธีการในอินเตอร์เฟสสามารถมีการนำไปใช้ทางเลือกเพื่อให้เป็นทางเลือกสำหรับการใช้คลาสเพื่อใช้เมธอดเหล่านี้)

น่าเสียดายที่คุณลักษณะเป็นสิ่งประดิษฐ์ที่เพิ่งเกิดขึ้นค่อนข้างเร็ว (2002) และหาได้ยากในภาษากระแสหลักที่มีขนาดใหญ่กว่า


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

4

สิ่งที่ฉันไม่เข้าใจคือ:

Abstractions สามารถเป็น superclasses หรืออินเทอร์เฟซ

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

อย่างแรกการพิมพ์ย่อยและนามธรรมเป็นสองสิ่งที่แตกต่างกัน การพิมพ์ย่อยเพียงหมายความว่าฉันสามารถทดแทนค่าประเภทหนึ่งเป็นค่าประเภทอื่นได้ - ไม่จำเป็นต้องเป็นนามธรรม

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

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


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

แก้ไข? ..... ....
Aviv Cohn

เหตุใดคุณจึงพิจารณาว่าอินเทอร์เฟซเป็นคู่กับอะไร
Michael Shaw

ฉันคิดว่าคำตอบนี้ตอกมันไว้บนหัว ฉันใช้ C ++ ค่อนข้างน้อยและตามที่ระบุไว้ในหนึ่งในคำตอบอื่น ๆ C ++ ไม่ค่อยมีอินเตอร์เฟส แต่คุณปลอมมันโดยใช้ superclasses ด้วยวิธีการทั้งหมดที่เหลือเป็น "pure virtual" (เช่นการใช้งานโดยเด็ก ๆ ) ประเด็นคือมันง่ายที่จะสร้างคลาสพื้นฐานที่ทำบางสิ่งบางอย่างพร้อมกับฟังก์ชั่นที่มอบหมาย ในหลาย ๆ กรณีหลาย ๆ ฉันและเพื่อนร่วมงานของฉันพบว่าการทำเช่นนั้นกรณีการใช้งานใหม่มาพร้อมและทำให้การทำงานของบิตที่ใช้ร่วมกันเป็นโมฆะ หากมีการใช้งานร่วมกันที่จำเป็นมันเป็นเรื่องง่ายพอที่จะทำให้ชั้นผู้ช่วย
J Trana

@Prog แนวความคิดของคุณส่วนใหญ่ถูกต้อง แต่สิ่งที่เป็นนามธรรมและการพิมพ์ย่อยเป็นสองสิ่งที่แยกจากกัน เมื่อคุณบอกว่าyou want an object named A to depend on an abstraction named B instead of a concrete implementation of that abstraction named Cคุณสมมติว่าคลาสนั้นไม่เป็นนามธรรม นามธรรมเป็นสิ่งที่ซ่อนรายละเอียดการใช้งานดังนั้นคลาสที่มีเขตข้อมูลส่วนตัวจึงมีความเป็นนามธรรมเหมือนกับอินเทอร์เฟซที่ใช้วิธีการสาธารณะแบบเดียวกัน
Doval

1

มีการเชื่อมต่อระหว่างคลาสพาเรนต์กับเด็กเนื่องจากเด็กขึ้นอยู่กับพาเรนต์

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

สมมติว่าเรามีส่วนต่อประสาน I และคลาส B ใช้งานได้ หากเราเปลี่ยนอินเตอร์เฟส I แม้ว่าคลาส B อาจไม่ใช้อีกต่อไปคลาส B ก็จะไม่เปลี่ยนแปลง


ฉันสงสัยว่าผู้ลงคะแนนเสียงมีเหตุผลหรือเพิ่งมีวันที่แย่
Michael Shaw

1
ฉันไม่ได้ลงคะแนน แต่ฉันคิดว่ามันอาจเกี่ยวข้องกับประโยคแรก ชั้นเรียนของเด็กเป็นแบบคู่กับชั้นผู้ปกครองไม่ใช่วิธีอื่น ผู้ปกครองไม่จำเป็นต้องรู้อะไรเกี่ยวกับเด็ก แต่เด็กต้องการความรู้อย่างใกล้ชิดเกี่ยวกับผู้ปกครอง

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