ส่วนต่อประสานกับคลาสฐาน


767

ฉันควรใช้อินเทอร์เฟซเมื่อใดและควรใช้คลาสพื้นฐานเมื่อใด

ควรเป็นอินเทอร์เฟซเสมอหรือไม่หากฉันไม่ต้องการกำหนดวิธีการใช้งานพื้นฐาน

ถ้าฉันมีคลาส Dog และ Cat เหตุใดฉันจึงต้องการใช้งาน IPet แทน PetBase ฉันสามารถเข้าใจว่ามีอินเตอร์เฟสสำหรับ ISheds หรือ IBarks (IMakesNoise?) เพราะสามารถวางสัตว์เลี้ยงบนพื้นฐานของสัตว์เลี้ยงได้ แต่ฉันไม่เข้าใจว่าจะใช้สัตว์เลี้ยงทั่วไปชนิดใด


11
เพียงจุดเดียวที่ฉันคิดว่าคุณควรคำนึงถึง - อินเตอร์เฟสมีหลายข้อ จำกัด ที่คุณอาจไม่ทราบจนกว่าจะถึงช่วงท้าย ๆ ตัวอย่างเช่นด้วย. NET คุณไม่สามารถทำให้เป็นอนุกรมตัวแปรสมาชิกอินเทอร์เฟซดังนั้นถ้าคุณมีคลาส Zoo และสมาชิกตัวแปรอาร์เรย์ของ IAnimals คุณจะไม่สามารถทำให้เป็นอันดับ Zoo (และนั่นหมายถึงการเขียน WebServices หรือสิ่งอื่น ๆ ความเจ็บปวด).
synhershko

1
คำถามนี้อาจช่วยให้เข้าใจแนวคิดของอินเตอร์เฟส stackoverflow.com/q/8531292/1055241
gprathour

ฉันแค่อยากรู้ ฉันพบในCLR ผ่านทาง C #ข้อความที่ตัดตอนมาต่อไปนี้: I tend to prefer using the interface technique over the base type technique because the base type technique doesn’t allow the developer to choose the base type that works best in a particular situation.. ฉันไม่สามารถเข้าใจสิ่งที่มีความหมายในข้อความที่ตัดตอนมา เราสามารถสร้างประเภทฐานสองสามประเภทและสร้างประเภทที่ได้มาสำหรับประเภทใดก็ได้ดังนั้นผู้พัฒนาสามารถเลือกประเภทฐานได้ มีใครช่วยอธิบายได้โปรดฉันหายไปอะไร ฉันเชื่อว่ามันสามารถเป็นส่วนหนึ่งของคำถามนี้ หรือฉันควรโพสต์อีกหนึ่งข้อความที่ตัดตอนมาโดยเฉพาะ?
qqqqqqq

คำตอบ:


501

ลองยกตัวอย่างสุนัขและแมวของคุณดูตัวอย่างการใช้ C #:

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

public abstract class Mammal

คลาสพื้นฐานนี้อาจมีวิธีการเริ่มต้นเช่น:

  • อาหาร
  • เพื่อน

ทั้งหมดนี้เป็นพฤติกรรมที่มีการใช้งานเหมือนกันไม่มากก็น้อย ในการกำหนดสิ่งนี้คุณจะมี:

public class Dog : Mammal
public class Cat : Mammal

ตอนนี้สมมติว่ามีสัตว์เลี้ยงลูกด้วยนมชนิดอื่นซึ่งเรามักจะเห็นในสวนสัตว์:

public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal

สิ่งนี้จะยังคงถูกต้องเพราะหัวใจของฟังก์ชันFeed()และMate()ยังคงเหมือนเดิม

อย่างไรก็ตามยีราฟ, แรดและฮิปโปไม่ใช่สัตว์ที่สามารถเลี้ยงสัตว์ได้ นั่นคือสิ่งที่อินเทอร์เฟซจะมีประโยชน์:

public interface IPettable
{
    IList<Trick> Tricks{get; set;}
    void Bathe();
    void Train(Trick t);
}

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

นิยามสุนัขและแมวของคุณควรมีลักษณะดังนี้:

public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable

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

ดังนั้นเพราะคุณมักจะสามารถสืบทอดจากระดับนามธรรมหนึ่ง (ในส่วนพิมพ์ statically ภาษา OO ที่เป็นข้อยกเว้น ... รวมถึง C ++) แต่สามารถที่จะใช้การเชื่อมต่อหลายมันช่วยให้คุณสามารถสร้างวัตถุในอย่างเคร่งครัดตามความจำเป็นพื้นฐาน


149
ฉันไม่คิดว่ามันง่ายขนาดนั้น คุณได้เปลี่ยนคำถาม (ข้อกำหนด) เล็กน้อยเพื่อให้อินเทอร์เฟซเหมาะสมกว่า คุณควรถามตัวเองเสมอว่าคุณกำลังกำหนดสัญญา (อินเทอร์เฟซ) หรือการใช้งานร่วม (ชั้นฐาน)
David Pokluda

18
อินเทอร์เฟซคือสัญญา คุณจะเปิดเผยเฉพาะส่วนของสัญญาที่บริการต้องการเท่านั้น หากคุณมี 'PettingZoo' แน่นอนว่าคุณไม่ต้องการเปิดเผย 'Mate'-ing ให้กับผู้ใช้!
Anthony Mastrean

10
@ David Touche ถึงแม้ว่าฉันจะทำมันให้ดีขึ้นเพื่อแสดงให้เห็นถึงสิ่งที่อินเตอร์เฟซที่มีไว้สำหรับและสิ่งที่เป็นนามธรรมชั้นเรียนสำหรับการทำความเข้าใจของเขา สุนัขและแมวไม่ได้เป็นข้อกำหนดที่เข้มงวด!
Jon Limjap

2
ควรสังเกตว่าในการตีความสภาพแวดล้อม JIT-ing (โดยเฉพาะ JVM) การเรียกใช้เมธอดเสมือนจะเร็วกว่าการเรียกใช้เมธอดอินเตอร์เฟสอย่างมีนัยสำคัญขึ้นอยู่กับบริบท ฉันเน้น "บริบท" เพราะ JVM สามารถปรับการค้นหาวิธีที่ช้าให้เหมาะสมได้ (เช่นหากรายชื่อผู้สืบทอดส่วนต่อประสานมีแนวโน้มที่จะเป็นอินสแตนซ์ของคลาสเดียวกันนี้น่าเศร้าที่ทำให้การเปรียบเทียบเป็นเรื่องยากลำบาก) หากคุณกำลังพยายามเพิ่มประสิทธิภาพบางอย่างที่มีความอ่อนไหวแล้วคุณควรพิจารณาสิ่งนี้
Philip Guin

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

146

ดี Josh Bloch กล่าวว่าตัวเองในEffective Java 2d :

ชอบอินเตอร์เฟสมากกว่าคลาสนามธรรม

ประเด็นหลักบางประการ:

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

  • การเชื่อมต่อเหมาะสำหรับการกำหนดมิกซ์อิน Mixin เป็นประเภทที่คลาสสามารถนำไปใช้นอกเหนือจาก“ ประเภทหลัก” ของมันเพื่อประกาศว่ามันมีพฤติกรรมที่เป็นทางเลือก ตัวอย่างเช่น Comparable เป็นอินเตอร์เฟส mixin ที่อนุญาตให้คลาสประกาศว่าอินสแตนซ์ของมันถูกเรียงลำดับด้วยความเคารพต่อวัตถุอื่นที่เทียบเคียงกัน

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

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

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

ในทางตรงกันข้ามอินเทอร์เฟซยากมากที่จะพัฒนา หากคุณเพิ่มวิธีการในอินเทอร์เฟซก็จะทำให้การใช้งานทั้งหมดของมันหยุดชะงัก

ป.ล. : ซื้อหนังสือ มันมีรายละเอียดมากขึ้น


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

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

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

118

อินเทอร์เฟซและคลาสพื้นฐานแสดงถึงความสัมพันธ์ที่แตกต่างกันสองรูปแบบ

การสืบทอด (คลาสฐาน) แทนความสัมพันธ์ "is-a" เช่นสุนัขหรือแมว "เป็นสัตว์เลี้ยง" ความสัมพันธ์นี้แสดงถึงจุดประสงค์ (เดี่ยว) ของชั้นเรียนเสมอ (ร่วมกับ"หลักการความรับผิดชอบเดี่ยว" )

ในทางกลับกันการเชื่อมต่อแทนคุณสมบัติเพิ่มเติมของชั้นเรียน ฉันเรียกมันว่าความสัมพันธ์แบบ "เป็น" เช่นเดียวกับใน " Fooเป็นแบบใช้ครั้งเดียว" ดังนั้นIDisposableอินเทอร์เฟซใน C #


10
จากคำตอบทั้งหมดคำตอบนี้ให้ส่วนผสมที่ดีที่สุดของความกะทัดรัดโดยไม่สูญเสียความชัดเจน
Matt Wilko

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

1
@berend ตลกที่คุณเขียนว่าเพราะหน้าจอมีการใช้งานผ่านอินเตอร์เฟซ - VGA, HDMI ฯลฯ
Konstantin

เพียงเพราะ OOP เราต้องขยายจินตนาการของเราให้เป็นไปตามสถานการณ์ในชีวิตจริง มันไม่ได้นำไปใช้เสมอ
Crismogram

110

สไตล์โมเดิร์นคือการกำหนด IPet และ PetBase

ข้อได้เปรียบของอินเทอร์เฟซคือรหัสอื่นสามารถใช้งานได้โดยไม่ผูกติดใด ๆ กับรหัสที่ปฏิบัติการได้อื่น ๆ "สะอาด" โดยสิ้นเชิง อินเตอร์เฟซยังสามารถผสม

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


มีเค้กของคุณและกินมันด้วย!
Darren Kopp

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

27
ไม่มีอะไร "ทันสมัย" ที่นี่ คลาสพื้นฐานและส่วนต่อประสานที่มี API เดียวกันนั้นซ้ำซ้อน คุณอาจใช้วิธีนี้ในบางกรณี แต่คุณไม่ควรพูดเกินจริง!
smentek

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

1
แก้ไข. มันไม่ใช่คำถามของ 1 หรืออื่น ๆ พวกเขามีอยู่เพื่อแก้ไขปัญหาที่แตกต่างกัน 2 อย่าง อินเทอร์เฟซคือสัญญาที่คลาสที่ใช้งานจะต้องจัดการ พวกเขาได้พบความโปรดปรานล่าสุด (บางครั้งคลั่ง) สำหรับ IoC และ TDD ด้าน คลาส Abstract / Base ทำหน้าที่จัดกลุ่มตรรกะและคุณสมบัติทั่วไปตามลำดับชั้น ช่วยลดรหัสที่ซ้ำซ้อนซึ่งจะช่วยเพิ่มความสามารถในการบำรุงรักษาและทำให้เกิดข้อผิดพลาดน้อยลง
ComeIn

63

อินเตอร์เฟซ

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

คลาสฐาน

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

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

59

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

The Gang of Four ระบุไว้ในหนังสือของพวกเขาว่า "เนื่องจากการสืบทอดทำให้คลาสย่อยมีรายละเอียดเกี่ยวกับการดำเนินการของผู้ปกครองมันมักจะกล่าวว่า 'มรดกแบ่งการห่อหุ้ม" ฉันเชื่อว่านี่เป็นจริง


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

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

3
มันเป็นแค่ฉันหรือว่า "คนทำงานทั่วไป" ส่วนใหญ่ใช้การใช้งานร่วมกันร่วมกันหรือไม่? ในบริบทนั้นขัดกับกฎทั่วไปของการเชื่อมต่อที่นิยม ฉันจะกล่าวย้ำลักษณะทั่วไปของคุณเป็น 2 ลักษณะทั่วไป: คลาสทั่วไปเหล่านั้นที่มีตรรกะไม่มากหรือน้อยควรใช้อินเตอร์เฟส คลาสทั่วไปเหล่านั้นที่มีจำนวนตรรกะ "ดี" ควรได้มาจากคลาสพื้นฐาน (เนื่องจากเป็นไปได้มากว่าพวกเขาจะแบ่งปันฟังก์ชันการทำงาน)
goku_da_master

@Kilhoffer "ส่วนต่อประสานอนุญาตให้มีความยืดหยุ่นและพกพาได้สูงสุดตามประเภทของขอบเขต" กรุณาอธิบายอย่างละเอียด
JAVA

49

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

Krzysztof Cwalina พูดว่าในหน้า 81:

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

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


19

ฮวน,

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

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

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

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

ดังนั้นหากคุณคิดเหมือนฉันคุณจะต้องพูดว่า Cat และ Dog นั้นเป็น IPettable มันเป็นลักษณะที่ตรงกับพวกเขาทั้งสอง

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

สมมติว่าฉันต้องการรวบรวมคลาสสัตว์ทั้งหมดและใส่ไว้ในตู้คอนเทนเนอร์ Ark ของฉัน

หรือพวกเขาจำเป็นต้องเป็นสัตว์เลี้ยงลูกด้วยนม? บางทีเราอาจต้องการโรงงานรีดนมวัวสำหรับสัตว์บ้าง?

พวกเขาจำเป็นต้องเชื่อมโยงเข้าด้วยกันหรือไม่? มันเพียงพอหรือไม่ที่จะรู้ว่าพวกเขามีทั้ง IPettable หรือไม่?

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

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


18

นี่คือนิยามขั้นพื้นฐานและเรียบง่ายของอินเตอร์เฟสและคลาสพื้นฐาน:

  • Base class = การสืบทอดวัตถุ
  • Interface = การสืบทอดการทำงาน

ไชโย


12

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

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

ดังนั้นแทนที่จะเป็น IPet หรือ PetBase คุณอาจท้ายด้วย Pet ซึ่งมีพารามิเตอร์ IFurBehavior พารามิเตอร์ IFurBehavior ถูกตั้งค่าโดยวิธีการ CreateDog () ของ PetFactory เป็นพารามิเตอร์นี้ซึ่งเรียกว่าเมธอด shed ()

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

ฉันแนะนำรูปแบบนี้แม้ในหลายภาษาที่สืบทอด


12

มันเป็นเรื่องที่อธิบายได้ดีในเรื่องนี้บทความ Java โลก

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

ไม่ใช่เรื่องแปลกที่ฉันจะมีคลาสที่ใช้อินเตอร์เฟสตั้งแต่หนึ่งอินเตอร์เฟสขึ้นไป

คลาสนามธรรมฉันใช้เป็นพื้นฐานสำหรับสิ่งอื่น

ต่อไปนี้เป็นสารสกัดจากบทความJavaWorld.comดังกล่าวข้างต้นบทความผู้เขียนโทนี่ซินต์, 04/20/01


อินเตอร์เฟสกับคลาสนามธรรม

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

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

นักพัฒนาหลายคนลืมว่าคลาสที่กำหนดวิธีนามธรรมสามารถเรียกวิธีนั้นได้เช่นกัน คลาสนามธรรมเป็นวิธีที่ยอดเยี่ยมในการสร้างลำดับชั้นการสืบทอดที่วางแผนไว้ พวกเขายังเป็นตัวเลือกที่ดีสำหรับคลาสที่ไม่ใช่ลีฟในลำดับชั้นของชั้นเรียน

คลาสกับอินเตอร์เฟส

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

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

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

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


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

และนอกเหนือจากนั้นให้ขยายการเขียนไปใช้หลังแต่ละวิธีของอินเทอร์เฟซของคุณ
วิศวกร

10

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


10

ฉันมีกฎคร่าวๆ

ฟังก์ชั่น:น่าจะแตกต่างกันในทุกส่วน: อินเตอร์เฟซ

ข้อมูลและฟังก์ชันการทำงานส่วนต่าง ๆ จะเหมือนกันส่วนต่าง ๆ :ระดับนามธรรม

ข้อมูลและฟังก์ชั่นใช้งานได้จริงหากขยายเฉพาะเมื่อมีการเปลี่ยนแปลงเล็กน้อย:ปกติ (คอนกรีต)

ข้อมูลและฟังก์ชั่นไม่มีการเปลี่ยนแปลงที่วางแผนไว้:คลาส (รูปธรรม) ธรรมดาพร้อมตัวปรับแต่งสุดท้าย

ข้อมูลและฟังก์ชั่นอาจ: อ่านอย่างเดียว:สมาชิก enum

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


7

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

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

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

คลาสนามธรรมอาจมีหลายอินเตอร์เฟส คลาส PetBase ที่เป็นนามธรรมของคุณอาจใช้ IPet (สัตว์เลี้ยงมีเจ้าของ) และ IDigestion (สัตว์เลี้ยงกินหรืออย่างน้อยก็ควร) อย่างไรก็ตาม PetBase อาจไม่ใช้ IMammal เนื่องจากสัตว์เลี้ยงทั้งหมดไม่ใช่สัตว์เลี้ยงลูกด้วยนมและไม่ใช่สัตว์เลี้ยงลูกด้วยนมทั้งหมดเป็นสัตว์เลี้ยง คุณสามารถเพิ่ม MammalPetBase ที่ขยาย PetBase และเพิ่ม IMammal FishBase สามารถมี PetBase และเพิ่ม IFish IFish จะมี ISwim และ IUnderwaterBreather เป็นอินเตอร์เฟส

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


7

ที่มา : http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/

C # เป็นภาษาที่ยอดเยี่ยมที่ได้พัฒนาและเติบโตในช่วง 14 ปีที่ผ่านมา นี่เป็นสิ่งที่ยอดเยี่ยมสำหรับนักพัฒนาซอฟต์แวร์ของเราเพราะภาษาที่พัฒนาขึ้นสำหรับผู้ใหญ่ทำให้เรามีฟีเจอร์ภาษามากมายที่เราพร้อมให้บริการ

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

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

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

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

public abstract class Dog
{
      public virtual void Bark()
      {
        Console.WriteLine("Base Class implementation of Bark");
      }
}

public class GoldenRetriever : Dog
{
   // the Bark method is inherited from the Dog class
}

public class Poodle : Dog
{
  // here we are overriding the base functionality of Bark with our new implementation
  // specific to the Poodle class
  public override void Bark()
  {
     Console.WriteLine("Poodle's implementation of Bark");
  }
}

// Add a list of dogs to a collection and call the bark method.

void Main()
{
    var poodle = new Poodle();
    var goldenRetriever = new GoldenRetriever();

    var dogs = new List<Dog>();
    dogs.Add(poodle);
    dogs.Add(goldenRetriever);

    foreach (var dog in dogs)
    {
       dog.Bark();
    }
}

// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark

// 

ดังที่คุณเห็นนี่จะเป็นวิธีที่ดีในการรักษารหัส DRY ของคุณและอนุญาตให้มีการเรียกใช้คลาสพื้นฐานเมื่อประเภทใดก็ตามสามารถพึ่งพา Bark เริ่มต้นแทนการใช้งานกรณีพิเศษ คลาสอย่าง GoldenRetriever, Boxer, Lab ทุกคนสามารถสืบทอด "ค่าเริ่มต้น" (เบสคลาส) เปลือกได้โดยไม่เสียค่าใช้จ่ายเพียงเพราะพวกเขาใช้คลาสนามธรรมของ Dog

แต่ฉันแน่ใจว่าคุณรู้แล้ว

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

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

// Create ISwimable interface
public interface ISwimable
{
      public void Swim();
}

// Have Human implement ISwimable Interface
public class Human : ISwimable

     public void Swim()
     {
        //Human's implementation of Swim
        Console.WriteLine("I'm a human swimming!");
     }

// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
     public void Swim()
     {
          // Duck's implementation of Swim
          Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
     }
}

//Now they can both be used in places where you just need an object that has the ability "to swim"

public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
     somethingThatCanSwim.Swim();
}

public void Main()
{
      var human = new Human();
      var duck = new Duck();

      var listOfThingsThatCanSwim = new List<ISwimable>();

      listOfThingsThatCanSwim.Add(duck);
      listOfThingsThatCanSwim.Add(human);

      foreach (var something in listOfThingsThatCanSwim)
      {
           ShowHowYouSwim(something);
      }
}

 // So at runtime the correct implementation of something.Swim() will be called
 // Output:
 // Quack! Quack! I'm a Duck swimming!
 // I'm a human swimming!

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

อีกครั้งนี้จะช่วยให้รหัสของคุณอยู่แห้งเพื่อที่คุณจะได้ไม่ต้องเขียนวิธีการต่าง ๆ ที่เรียกวัตถุเพื่อ preform ฟังก์ชันหลักเดียวกัน (ShowHowHumanSwims (มนุษย์), ShowHowDuckSwims (เป็ด) ฯลฯ )

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

สรุป:

ดังนั้นกฎหลักง่ายๆของฉันคือใช้คลาสนามธรรมเมื่อคุณต้องการใช้ฟังก์ชั่น“ เริ่มต้น” สำหรับลำดับชั้นของคลาสหรือ / และคลาสหรือประเภทที่คุณทำงานด้วยการแบ่งปันความสัมพันธ์“ คือ” (เช่นพุดเดิ้ล” คือ ” ประเภทของสุนัข)

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

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

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

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

หวังว่าสิ่งนี้จะช่วยให้บางคนดีขึ้น!

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


6

กรณีสำหรับคลาสพื้นฐานบนอินเตอร์เฟสได้อธิบายไว้ในแนวทางการเข้ารหัส Submain .NET:

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

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

  1. คลาสที่ไม่เกี่ยวข้องหลายคลาสต้องการสนับสนุนโปรโตคอล
  2. คลาสเหล่านี้ได้สร้างคลาสพื้นฐานแล้ว (ตัวอย่างเช่นบางตัวควบคุมเป็นส่วนต่อประสานผู้ใช้ (UI) และบางส่วนเป็น XML Web services)
  3. การรวมกันไม่เหมาะสมหรือทำได้ ในสถานการณ์อื่น ๆ การสืบทอดคลาสเป็นแบบอย่างที่ดีกว่า

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

5

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


4

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

ที่จะสร้างเพิ่มเติมเกี่ยวกับตัวอย่างที่ใช้มากในคำถามนี้โดยเฉพาะอย่างยิ่งมีจำนวนมากของสิ่งที่มีความ petable - แฟน, รถยนต์, ผ้าห่มเลือน ... - ดังนั้นฉันอาจมีระดับ Petable ที่ให้พฤติกรรมที่พบบ่อยและชั้นเรียนต่างๆสืบทอด จากมัน.

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

พฤติกรรมควรได้รับการกำหนดให้กับอินเทอร์เฟซก่อน (รวมถึงอินเทอร์เฟซเริ่มต้นของคลาส) และเลื่อนระดับเป็นคลาสพื้นฐานเฉพาะหากเป็น (ก) ทั่วไปสำหรับกลุ่มคลาสขนาดใหญ่ที่เป็นชุดย่อยของคลาสที่มีขนาดใหญ่ขึ้น "cat" และ "person" เป็นส่วนย่อยของ "mammal"

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

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


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

4

ชอบอินเตอร์เฟสมากกว่าคลาสนามธรรม

เหตุผลหลักที่ต้องพิจารณา [สองข้อที่กล่าวถึงแล้ว] คือ:

  • อินเทอร์เฟซมีความยืดหยุ่นมากขึ้นเนื่องจากคลาสสามารถใช้หลายอินเตอร์เฟส เนื่องจาก Java ไม่มีมรดกหลายอย่างการใช้คลาสนามธรรมจะป้องกันผู้ใช้ของคุณจากการใช้ลำดับชั้นของคลาสอื่น โดยทั่วไปให้เลือกใช้อินเตอร์เฟสเมื่อไม่มีการใช้งานหรือสถานะเริ่มต้น คอลเลกชัน Java เสนอตัวอย่างที่ดีของสิ่งนี้ (แผนที่ตั้งค่า ฯลฯ )
  • คลาสนามธรรมมีข้อได้เปรียบของการอนุญาตให้ส่งต่อความเข้ากันได้ดีขึ้น เมื่อลูกค้าใช้อินเทอร์เฟซคุณจะไม่สามารถเปลี่ยนได้ ถ้าพวกเขาใช้คลาสนามธรรมคุณยังสามารถเพิ่มพฤติกรรมโดยไม่ต้องทำลายรหัสที่มีอยู่ หากความเข้ากันได้เป็นปัญหาให้พิจารณาใช้คลาสนามธรรม
  • แม้ว่าคุณจะมีการใช้งานเริ่มต้นหรือรัฐภายใน พิจารณาเสนออินเตอร์เฟซและการดำเนินการที่เป็นนามธรรมของมัน สิ่งนี้จะช่วยลูกค้า แต่ก็ยังช่วยให้พวกเขามีอิสระมากขึ้นถ้าต้องการ [1]
    แน่นอนว่ามีการพูดคุยกันในเรื่องอื่น ๆ [2,3]

[1] มันเพิ่มรหัสเพิ่มเติมแน่นอน แต่ถ้าความกะทัดรัดเป็นข้อกังวลหลักของคุณคุณอาจควรหลีกเลี่ยง Java ตั้งแต่แรก!

[2] Joshua Bloch, Java ที่มีประสิทธิภาพ, รายการที่ 16-18

[3] http://www.codeproject.com/KB/ar ...


3

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


3

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

การรับมรดกสาธารณะมีการใช้มากเกินไปใน OOD และแสดงออกมากเกินกว่าที่นักพัฒนาส่วนใหญ่ตระหนักหรือเต็มใจที่จะใช้ชีวิต ดูหลักการทดแทน Liskov

กล่าวโดยย่อหาก A "คือ" B ดังนั้น A ต้องไม่เกิน B และส่งมอบไม่น้อยกว่า B สำหรับทุกวิธีที่เปิดเผย


3

ตัวเลือกอื่นที่ควรคำนึงถึงคือการใช้ความสัมพันธ์ "มี - a", aka "ถูกนำมาใช้ในแง่ของ" หรือ "องค์ประกอบ" บางครั้งนี่เป็นวิธีที่สะอาดและยืดหยุ่นกว่าในการจัดโครงสร้างสิ่งต่าง ๆ มากกว่าการใช้การสืบทอดแบบ "is-a"

มันอาจจะไม่สมเหตุสมผลที่จะบอกว่า Dog และ Cat ทั้งสอง "มี" สัตว์เลี้ยง แต่มันก็ช่วยหลีกเลี่ยงข้อผิดพลาดในการถ่ายทอดทางพันธุกรรมที่พบบ่อย:

public class Pet
{
    void Bathe();
    void Train(Trick t);
}

public class Dog
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

public class Cat
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

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

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

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


3

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

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

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

มีความแตกต่างระหว่างคลาสฐานและคลาสพื้นฐานที่เป็นนามธรรม (ABC) ABCs ผสมส่วนต่อประสานและการนำไปใช้ร่วมกัน บทคัดย่อนอกโปรแกรมคอมพิวเตอร์หมายถึง "สรุป" นั่นคือ "abstract == interface" ชั้นฐานนามธรรมแล้วสามารถอธิบายทั้งอินเตอร์เฟซเช่นเดียวกับที่ว่างเปล่าบางส่วนหรือทั้งหมดการดำเนินงานที่มีวัตถุประสงค์เพื่อจะได้รับมรดก

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

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


2

มันขึ้นอยู่กับความต้องการของคุณ ถ้า IPet นั้นง่ายพอฉันอยากจะใช้มัน มิฉะนั้นถ้า PetBase ใช้ฟังก์ชันมากมายคุณไม่ต้องการทำซ้ำ

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

สุดท้ายการสืบทอด. NET เพียงครั้งเดียวก็ฆ่าฉัน ตัวอย่างที่ไร้เดียงสา: UserControlสมมติว่าคุณกำลังทำให้การควบคุมผู้ใช้เพื่อให้คุณได้รับมรดก แต่ตอนนี้คุณถูกล็อคไม่ให้สืบทอดPetBaseเช่นกัน สิ่งนี้บังคับให้คุณจัดระเบียบใหม่เช่นทำให้PetBaseสมาชิกคลาสแทน


2

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


2

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


2

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


2

ฉันพบว่ารูปแบบของอินเตอร์เฟส> นามธรรม> คอนกรีตทำงานได้ในกรณีการใช้งานต่อไปนี้:

1.  You have a general interface (eg IPet)
2.  You have a implementation that is less general (eg Mammal)
3.  You have many concrete members (eg Cat, Dog, Ape)

คลาส abstract กำหนดแอ็ตทริบิวต์ที่แบ่งใช้ดีฟอลต์ของคลาสคอนกรีต แต่บังคับใช้อินเตอร์เฟส ตัวอย่างเช่น:

public interface IPet{

    public boolean hasHair();

    public boolean walksUprights();

    public boolean hasNipples();
}

ตอนนี้เนื่องจากสัตว์เลี้ยงลูกด้วยนมทุกคนมีผมและหัวนม (AFAIK ฉันไม่ใช่นักสัตววิทยา) เราสามารถม้วนมันลงในชั้นฐานนามธรรม

public abstract class Mammal() implements IPet{

     @override
     public walksUpright(){
         throw new NotSupportedException("Walks Upright not implemented");
     }

     @override
     public hasNipples(){return true}

     @override
     public hasHair(){return true}

และจากนั้นชั้นเรียนที่เป็นรูปธรรมเพียงกำหนดว่าพวกเขาเดินตรง

public class Ape extends Mammal(){

    @override
    public walksUpright(return true)
}

public class Catextends Mammal(){

    @override
    public walksUpright(return false)
}

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

ในกรณีนี้บทคัดย่ออาจเป็นรูปธรรมเช่นกัน อย่างไรก็ตามการกำหนดบทคัดย่อช่วยเน้นว่ารูปแบบนี้จะถูกใช้


1

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


1

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

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

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