“ โปรแกรมไปยังส่วนต่อประสาน” หมายความว่าอย่างไร


813

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

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

เป็นเช่นนั้นถ้าคุณต้องทำ:

IInterface classRef = new ObjectWhatever()

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

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


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

18
@ Ande Turner: นั่นเป็นคำแนะนำที่แย่ 1) "โปรแกรมของคุณต้องดีที่สุด" ไม่ใช่เหตุผลที่ดีในการเปลี่ยนอินเทอร์เฟซ! จากนั้นคุณพูดว่า "แจกจ่ายรหัสของคุณที่ตั้งโปรแกรมไปยังส่วนต่อประสานแม้ว่า ... " ดังนั้นคุณจึงแนะนำให้ทำตามข้อกำหนดที่ได้รับ (1) คุณจึงปล่อยรหัสย่อยที่ดีที่สุด?!?
มิทช์ข้าวสาลี

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

2
@ apollodude217 ที่จริงคำตอบที่ดีที่สุดในหน้าทั้งหมด อย่างน้อยสำหรับคำถามในชื่อเนื่องจากมีอย่างน้อย 3 คำถามค่อนข้างแตกต่างกันที่นี่ ...
แอนดรู Spencer

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

คำตอบ:


1633

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

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

ตัวอย่างเช่น - สมมติว่าคุณมีเกมซิมและมีคลาสต่อไปนี้:

class HouseFly inherits Insect {
    void FlyAroundYourHead(){}
    void LandOnThings(){}
}

class Telemarketer inherits Person {
    void CallDuringDinner(){}
    void ContinueTalkingWhenYouSayNo(){}
}

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

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

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

interface IPest {
    void BeAnnoying();
}

class HouseFly inherits Insect implements IPest {
    void FlyAroundYourHead(){}
    void LandOnThings(){}

    void BeAnnoying() {
        FlyAroundYourHead();
        LandOnThings();
    }
}

class Telemarketer inherits Person implements IPest {
    void CallDuringDinner(){}
    void ContinueTalkingWhenYouSayNo(){}

    void BeAnnoying() {
        CallDuringDinner();
        ContinueTalkingWhenYouSayNo();
    }
}

ตอนนี้เรามีสองคลาสที่แต่ละคนสามารถสร้างความรำคาญในแบบของพวกเขา และพวกเขาไม่จำเป็นต้องได้รับจากชั้นฐานเดียวกันและแบ่งปันลักษณะโดยทั่วไปที่พบบ่อย - พวกเขาเพียงแค่ต้องทำตามสัญญาIPest- สัญญานั้นง่าย BeAnnoyingคุณเพียงแค่ต้อง ในเรื่องนี้เราสามารถสร้างโมเดลต่อไปนี้:

class DiningRoom {

    DiningRoom(Person[] diningPeople, IPest[] pests) { ... }

    void ServeDinner() {
        when diningPeople are eating,

        foreach pest in pests
        pest.BeAnnoying();
    }
}

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

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

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


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

4
วิธีการไม่ได้มีไว้เพื่อแสดงวิธีการแบบนามธรรม - การใช้งานของพวกเขาไม่เกี่ยวข้องกับคำถามที่มุ่งเน้นไปที่อินเตอร์เฟซ
Peter Meyer

33
พฤติกรรมการห่อหุ้มเช่น IPest เป็นที่รู้จักกันว่าเป็นรูปแบบกลยุทธ์ในกรณีที่ใครก็ตามที่สนใจติดตามเนื้อหาเพิ่มเติมในหัวข้อนั้น ...
nckbrz

9
น่าสนใจคุณไม่ได้ชี้ให้เห็นว่าเนื่องจากวัตถุในการIPest[]อ้างอิง IPest คุณสามารถโทรBeAnnoying()เพราะพวกเขามีวิธีการในขณะที่คุณไม่สามารถเรียกวิธีการอื่น ๆ โดยไม่ต้องโยน อย่างไรก็ตามแต่ละอ๊อบเจคBeAnnoying()จะเรียกแต่ละเมธอด
D. Ben Knoble

4
คำอธิบายที่ดีมาก ... ฉันต้องพูดที่นี่: ฉันไม่เคยได้ยินว่าอินเตอร์เฟสเป็นกลไกการสืบทอดแบบหลวม ๆ แต่ฉันรู้ว่าการใช้การสืบทอดเป็นกลไกที่ไม่ดีสำหรับการกำหนดอินเตอร์เฟส (เช่นใน Python ปกติคุณ ทำมันตลอดเวลา)
Carlos H Romano

282

ตัวอย่างเฉพาะที่ฉันเคยให้กับนักเรียนคือพวกเขาควรเขียน

List myList = new ArrayList(); // programming to the List interface

แทน

ArrayList myList = new ArrayList(); // this is bad

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

List myList = new TreeList();

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

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

public ArrayList doSomething(HashMap map);

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

public List doSomething(Map map);

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


ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
Yvette

คำอธิบายที่ชัดเจนมาก มีประโยชน์มาก
samuel luswata

ฉันมีคำถามเกี่ยวกับเหตุผลที่คุณพูดถึง "การประกาศครั้งแรกทำให้แน่ใจได้ว่าคุณเรียกใช้เมธอดใน myList ที่กำหนดโดยอินเตอร์เฟสเท่านั้น (ดังนั้นจึงไม่มีวิธีการเฉพาะของ ArrayList) หากคุณตั้งโปรแกรมให้อินเทอร์เฟซด้วยวิธีนี้ สามารถตัดสินใจว่าคุณต้องการ List myList = new TreeList () และคุณต้องเปลี่ยนรหัสของคุณในจุดเดียว " บางทีฉันอาจเข้าใจผิดฉันสงสัยว่าทำไมคุณต้องเปลี่ยน ArrayList เป็น TreeList หากคุณต้องการ "ทำให้แน่ใจได้ว่าคุณเรียกเมธอดบน myList" เท่านั้น?
user3014901

1
@ user3014901 มีหลายเหตุผลที่คุณอาจต้องการเปลี่ยนประเภทของรายการที่คุณใช้ หนึ่งอาจมีประสิทธิภาพการค้นหาที่ดีขึ้นเช่น ประเด็นก็คือว่าถ้าคุณโปรแกรมไปยังส่วนต่อประสานรายการจะทำให้เปลี่ยนรหัสของคุณเป็นการนำไปใช้ที่แตกต่างกันได้ง่ายขึ้นในภายหลัง
Bill the Lizard

73

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

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


1
เห็นด้วยอย่างสิ้นเชิงนั่นคือความเป็นอิสระระหว่างสิ่งที่ทำกับสิ่งที่ทำ โดยการแบ่งพาร์ติชันระบบตามส่วนประกอบอิสระคุณจะได้ระบบที่เรียบง่ายและนำกลับมาใช้ใหม่ได้ (ดูSimple Made Easyโดยผู้ชายที่สร้าง Clojure)
beluchin

38

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

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

แก้ไข:ด้านล่างนี้เป็นลิงค์ไปยังบทความที่ Erich Gamma กล่าวถึงคำพูดของเขา "โปรแกรมไปยังส่วนต่อประสานงานไม่ใช่การติดตั้งใช้งาน"

http://www.artima.com/lejava/articles/designprinciples.html


3
ได้โปรดอ่านบทสัมภาษณ์นี้อีกครั้ง: Gamma พูดถึงแนวคิดของอินเตอร์เฟส OO ไม่ใช่ JAVA หรือคลาสพิเศษ C # (ISomething) ปัญหาคือคนส่วนใหญ่แม้ว่าเขากำลังพูดถึงคำหลักดังนั้นตอนนี้เรามีอินเทอร์เฟซที่ไม่ต้องมีผู้ใช้จำนวนมาก (ISomething)
Sylvain Rodrigue

สัมภาษณ์ที่ดีมาก โปรดระวังสำหรับผู้อ่านในอนาคตมีสี่หน้าในการสัมภาษณ์ ฉันเกือบจะปิดเบราว์เซอร์ก่อนที่จะเห็นมัน
Ad Infinitum

37

การเขียนโปรแกรมไปยังส่วนต่อประสานไม่มีอะไรเกี่ยวข้องกับส่วนต่อประสานแบบนามธรรมอย่างที่เราเห็นใน Java หรือ. NET มันไม่ได้เป็นแนวคิดของ OOP

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

แก้ไข:และด้วยฐานข้อมูลหมายถึงการใช้มุมมองและขั้นตอนการจัดเก็บแทนการเข้าถึงตารางโดยตรง


5
คำตอบที่ดีที่สุด แกมมาให้คำอธิบายที่คล้ายกันที่นี่: artima.com/lejava/articles/designprinciples.html (ดูหน้า 2) เขาอ้างถึงแนวคิด OO แต่คุณพูดถูก: มันใหญ่กว่านั้น
Sylvain Rodrigue

36

คุณควรตรวจสอบ Inversion of Control:

ในสถานการณ์ดังกล่าวคุณจะไม่เขียนสิ่งนี้:

IInterface classRef = new ObjectWhatever();

คุณจะเขียนอะไรแบบนี้:

IInterface classRef = container.Resolve<IInterface>();

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

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

สิ่งนี้จะมีประโยชน์เมื่อผ่านพารามิเตอร์

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

public void DoSomethingToAnObject(IInterface whatever) { ... }

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

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

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

ขอยกตัวอย่างที่เป็นรูปธรรม

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

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

  1. สร้างการสนับสนุนสำหรับระบบการแปลของเราเพื่อตรวจจับเฉพาะชนิดของการควบคุมที่ใช้งานได้และแปลบิตที่ถูกต้อง (ฝันร้ายการบำรุงรักษา)
  2. สร้างการสนับสนุนในคลาสฐาน (เป็นไปไม่ได้เนื่องจากตัวควบคุมทั้งหมดสืบทอดมาจากคลาสที่กำหนดไว้ล่วงหน้าที่ต่างกัน)
  3. เพิ่มการรองรับอินเตอร์เฟส

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


31
เหตุใดการกล่าวถึง IoC ในตอนแรกเพราะจะทำให้เกิดความสับสนมากขึ้นเท่านั้น
Kevin Le - Khnle

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

28

รหัสไปยังส่วนต่อประสานงานไม่ใช่การนำไปปฏิบัติไม่เกี่ยวกับ Java หรือส่วนต่อประสานที่สร้างขึ้น

แนวคิดนี้ถูกนำมาใช้เพื่อความโดดเด่นในรูปแบบ / Gang of Four หนังสือ แต่ส่วนใหญ่อาจจะดีก่อนหน้านั้น แนวคิดนี้มีอยู่จริงก่อนที่ Java จะมีอยู่จริง

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

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

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

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


1
ประโยคแรกบอกว่ามันทั้งหมด นี่ควรเป็นคำตอบที่ยอมรับได้
madumlao

14

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

// if I want to add search capabilities to my application and support multiple search
// engines such as Google, Yahoo, Live, etc.

interface ISearchProvider
{
    string Search(string keywords);
}

ฉันสามารถสร้าง GoogleSearchProvider, YahooSearchProvider, LiveSearchProvider เป็นต้น

// if I want to support multiple downloads using different protocols
// HTTP, HTTPS, FTP, FTPS, etc.
interface IUrlDownload
{
    void Download(string url)
}

// how about an image loader for different kinds of images JPG, GIF, PNG, etc.
interface IImageLoader
{
    Bitmap LoadImage(string filename)
}

จากนั้นสร้าง JpegImageLoader, GifImageLoader, PngImageLoader เป็นต้น

Add-in และระบบปลั๊กอินส่วนใหญ่จะทำงานนอกอินเตอร์เฟส

การใช้งานที่ได้รับความนิยมอีกอย่างหนึ่งก็คือรูปแบบ Repository สมมติว่าฉันต้องการโหลดรายการรหัสไปรษณีย์จากแหล่งต่าง ๆ

interface IZipCodeRepository
{
    IList<ZipCode> GetZipCodes(string state);
}

จากนั้นฉันสามารถสร้าง XMLZipCodeRepository, SQLZipCodeRepository, CSVZipCodeRepository และอื่น ๆ สำหรับเว็บแอปพลิเคชันของฉันฉันมักจะสร้างที่เก็บ XML ก่อนเพื่อให้ฉันสามารถทำงานก่อนที่ฐานข้อมูล SQL จะพร้อมใช้งาน เมื่อฐานข้อมูลพร้อมฉันเขียน SQLRepository เพื่อแทนที่เวอร์ชัน XML ส่วนที่เหลือของรหัสของฉันยังคงไม่เปลี่ยนแปลงเนื่องจากมันทำงานนอกอินเตอร์เฟสเท่านั้น

วิธีการสามารถรับอินเทอร์เฟซเช่น:

PrintZipCodes(IZipCodeRepository zipCodeRepository, string state)
{
    foreach (ZipCode zipCode in zipCodeRepository.GetZipCodes(state))
    {
        Console.WriteLine(zipCode.ToString());
    }
}

10

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

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

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

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


9

มีคำอธิบายมากมายอยู่ที่นั่น แต่เพื่อให้ง่ายยิ่งขึ้น Listใช้ตัวอย่างเช่น หนึ่งสามารถใช้รายการด้วย:

  1. อาร์เรย์ภายใน
  2. รายการที่เชื่อมโยง
  3. การใช้งานอื่น ๆ

Listโดยการสร้างการอินเตอร์เฟซกล่าวว่า คุณเพียงแค่กำหนดนิยามของ List หรือListในความเป็นจริง

คุณสามารถใช้การใช้งานชนิดใดก็ได้ภายในเป็นการพูดการarrayใช้งาน แต่สมมติว่าคุณต้องการเปลี่ยนการใช้งานด้วยเหตุผลบางอย่างที่บอกว่ามีข้อบกพร่องหรือประสิทธิภาพ แล้วคุณก็ต้องมีการเปลี่ยนแปลงการประกาศไปList<String> ls = new ArrayList<String>()List<String> ls = new LinkedList<String>()

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


8

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


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

3
JDBC แย่มากจนต้องเปลี่ยนใหม่ ค้นหาตัวอย่างอื่น
Joshua

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

8

การเขียนโปรแกรมไปยังส่วนต่อประสานนั้นยอดเยี่ยมมาก ตามที่ @lassevk พูดถึง Inversion of Control เป็นประโยชน์อย่างมากสำหรับสิ่งนี้

นอกจากนี้ยังมีลักษณะเป็นผู้ว่าจ้าง SOLID นี่คือวิดีโอซีรีย์

มันจะผ่านการเข้ารหัสอย่างหนัก (ตัวอย่างที่ดีมาก) จากนั้นดูที่อินเทอร์เฟซในที่สุดก็พัฒนาเป็นเครื่องมือ IoC / DI (NInject)


7

ฉันเป็นผู้มาสายสำหรับคำถามนี้ แต่ฉันต้องการพูดถึงที่นี่ว่าบรรทัด "โปรแกรมไปยังส่วนต่อประสานกับผู้อื่นไม่ใช่การนำไปปฏิบัติ" มีการอภิปรายที่ดีในหนังสือรูปแบบการออกแบบ GoF (แก๊งสี่)

มันระบุไว้ในหน้า 18:

โปรแกรมไปยังส่วนต่อประสานไม่ใช่การใช้งาน

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

และเหนือสิ่งนั้นมันเริ่มต้นด้วย:

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

  1. ลูกค้ายังไม่ได้ตระหนักถึงประเภทของวัตถุเฉพาะที่ใช้ตราบใดที่วัตถุยังคงอยู่กับส่วนต่อประสานที่ลูกค้าคาดหวัง
  2. ลูกค้ายังไม่ทราบคลาสที่ใช้วัตถุเหล่านี้ ลูกค้ารู้เพียงเกี่ยวกับคลาสนามธรรม (es) ที่กำหนดอินเทอร์เฟซ

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


6

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


5

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


4

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


4

มันอาจเป็นประโยชน์ในการโปรแกรมไปยังส่วนต่อประสานแม้ว่าเราจะไม่ได้ขึ้นอยู่กับนามธรรม

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

  1. ป้องกันเราจากการทำสิ่งที่ไม่เหมาะสมตามบริบทและ
  2. ให้เราเปลี่ยนการใช้งานได้อย่างปลอดภัยในอนาคต

ตัวอย่างเช่นพิจารณาPersonคลาสที่ใช้FriendและEmployeeอินเตอร์เฟส

class Person implements AbstractEmployee, AbstractFriend {
}

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

function party() {
    const friend: Friend = new Person("Kathryn");
    friend.HaveFun();
}

ในบริบทของการทำงานของบุคคลนั้นเราเขียนโปรแกรมไปยังEmployeeส่วนต่อประสานเพื่อป้องกันไม่ให้ขอบเขตการทำงานเบลอ

function workplace() {
    const employee: Employee = new Person("Kathryn");
    employee.DoWork();
}

ยิ่งใหญ่ เราทำงานอย่างเหมาะสมในบริบทที่แตกต่างกันและซอฟต์แวร์ของเราทำงานได้ดี

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

  1. ฟังก์ชั่นpartyใช้เฉพาะส่วนย่อยของFriendPerson
  2. ฟังก์ชั่นworkplaceใช้เฉพาะส่วนย่อยของEmployeePerson
  3. คลาสDogใช้ทั้งFriendและEmployeeอินเตอร์เฟส

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

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


1
สมมติว่าคุณไม่มีอินเทอร์เฟซแบบกว้างมากเกินไปนั่นคือ
Casey

4

ถ้าผมเขียนคลาสใหม่Swimmerที่จะเพิ่มฟังก์ชันการทำงานswim()และความจำเป็นในการใช้วัตถุของคลาสพูดDogและนี้Dogอินเตอร์เฟซการดำเนินการระดับที่ประกาศAnimalswim()

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

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


3

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

ตัวอย่างเช่นฉันอาจมีส่วนต่อประสานการเคลื่อนไหว วิธีการที่ทำให้บางสิ่งบางอย่าง 'ย้าย' และวัตถุใด ๆ (Person, Car, Cat) ที่ใช้อินเตอร์เฟซการเคลื่อนไหวสามารถส่งผ่านและบอกให้ย้าย โดยปราศจากวิธีการที่ทุกคนรู้ว่ามันเป็นประเภทใด


3

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

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

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

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


3

คำอธิบาย C ++

คิดว่าอินเตอร์เฟสเป็นวิธีสาธารณะในชั้นเรียนของคุณ

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

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

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

คุณอาจต้องการออกแบบคอนเทนเนอร์อื่น ๆ (รายการคิว) ที่จะควบคุมอัลกอริทึมการค้นหาเดียวกับ Vector โดยให้พวกเขาปฏิบัติตามอินเตอร์เฟส / สัญญาที่อัลกอริทึมการค้นหาของคุณขึ้นอยู่กับ

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

สำหรับ 'พลาด' เกี่ยวกับวิธีการทำงาน; ครั้งใหญ่ (อย่างน้อยใน C ++) เช่นนี้เป็นกรอบการทำงานส่วนใหญ่ของ Standard TEMPLATE Library

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

นี่เป็นหัวข้อใหญ่และเป็นหนึ่งในหลักการสำคัญของรูปแบบการออกแบบ


3

ใน Java คลาสที่เป็นรูปธรรมเหล่านี้ทั้งหมดใช้อินเตอร์เฟส CharSequence:

CharBuffer, String, StringBuffer, StringBuilder

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

แต่แต่ละคลาสเหล่านี้สามารถใช้วิธีการอินเทอร์เฟซ CharSequence ได้อย่างเหมาะสม:

char charAt(int index)
int length()
CharSequence subSequence(int start, int end)
String toString()

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

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

BufferedWriter, CharArrayWriter, CharBuffer, FileWriter, FilterWriter, LogStream, OutputStreamWriter, PipedWriter, PrintStream, PrintWriter, StringBuffer, StringBuilder, StringWriter, นักเขียน


มันเป็นอินเตอร์เฟซที่ไม่ดีเกินไปเช่นCharSequenceเป็นโรคโลหิตจางดังนั้น ฉันหวังว่า Java และ. NET อนุญาตให้ส่วนต่อประสานนั้นมีการใช้งานเริ่มต้นเพื่อที่ผู้คนจะไม่ได้ตัดส่วนเชื่อมต่ออย่างหมดจดเพื่อจุดประสงค์ในการลดรหัสต้นแบบ เมื่อทำการCharSequenceติดตั้งที่ถูกต้องตามกฎหมายสามารถเลียนแบบฟังก์ชั่นส่วนใหญ่ของการStringใช้เพียงสี่วิธีข้างต้น น่าเสียดายที่แม้ว่าการใช้งานเฉพาะของCharSequenceทุกอย่างจะเก็บไว้ในที่เดียวchar[]และสามารถแสดงได้มากมาย ...
supercat

... การดำเนินการindexOfอย่างรวดเร็วไม่มีวิธีที่ผู้โทรที่ไม่คุ้นเคยกับการนำไปปฏิบัติบางอย่างCharSequenceอาจขอให้ทำเช่นนั้นแทนที่จะต้องใช้charAtเพื่อตรวจสอบอักขระแต่ละตัว
supercat

3

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

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

ดังนั้นควรห่อหุ้มด้วยดีกว่า (ในเรื่องราวของเรามันคือส่วนต่อประสาน) จากนั้นเขาจะทำงานให้ดี

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

สร้างประเภทของ interfaceไม่ใช่ประเภทจริง แต่ใช้กับประเภทจริง

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

ฉันยกตัวอย่างให้คุณ

คุณมีส่วนต่อประสาน AirPlane ดังนี้

interface Airplane{
    parkPlane();
    servicePlane();
}

สมมติว่าคุณมีวิธีการในคลาส Controller ของคุณเช่น Planes

parkPlane(Airplane plane)

และ

servicePlane(Airplane plane)

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

เพราะมันจะยอมรับเครื่องบินใด ๆ แม้จะมีประเภทที่เกิดขึ้นจริงflyer, highflyr,fighterฯลฯ

นอกจากนี้ในคอลเล็กชัน:

List<Airplane> plane; // จะใช้เครื่องบินของคุณทั้งหมด

ตัวอย่างต่อไปนี้จะล้างความเข้าใจของคุณ


คุณมีเครื่องบินรบที่ใช้มันดังนั้น

public class Fighter implements Airplane {

    public void  parkPlane(){
        // Specific implementations for fighter plane to park
    }
    public void  servicePlane(){
        // Specific implementatoins for fighter plane to service.
    }
}

สิ่งเดียวกันสำหรับ HighFlyer และ clasess อื่น ๆ :

public class HighFlyer implements Airplane {

    public void  parkPlane(){
        // Specific implementations for HighFlyer plane to park
    }

    public void  servicePlane(){
        // specific implementatoins for HighFlyer plane to service.
    }
}

ตอนนี้คิดว่าคลาสคอนโทรลเลอร์ของคุณใช้AirPlaneหลายครั้ง

สมมติว่าคลาสคอนโทรลเลอร์ของคุณเป็น ControlPlane เหมือนด้านล่าง

public Class ControlPlane{ 
 AirPlane plane;
 // so much method with AirPlane reference are used here...
}

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

คุณสามารถเพิ่มอินสแตนซ์ ...

JumboJetPlane // implementing AirPlane interface.
AirBus        // implementing AirPlane interface.

คุณสามารถลบอินสแตนซ์ของประเภทที่สร้างไว้ก่อนหน้านี้ด้วย


2

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

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


2

ถาม: - ... "คุณสามารถใช้คลาสใดที่ใช้อินเทอร์เฟซได้หรือไม่"
ตอบ: ใช่

ถาม: - ... "เมื่อไหร่คุณจะต้องทำอย่างนั้น?"
ตอบ: - ทุกครั้งที่คุณต้องการคลาสที่ใช้อินเทอร์เฟซ

หมายเหตุ: เราไม่สามารถยกตัวอย่างอินเทอร์เฟซที่ไม่ใช้โดยคลาสได้ - จริง

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

AnIntf anInst = new Aclass();
// เราสามารถทำสิ่งนี้ได้ก็ต่อเมื่อ Aclass ใช้ AnIntf
// anInst จะมีการอ้างอิง Aclass


หมายเหตุ: ตอนนี้เราสามารถเข้าใจสิ่งที่เกิดขึ้นถ้า Bclass และ Cclass ใช้ Dintf เดียวกัน

Dintf bInst = new Bclass();  
// now we could call all Dintf functions implemented (defined) in Bclass.

Dintf cInst = new Cclass();  
// now we could call all Dintf functions implemented (defined) in Cclass.

สิ่งที่เรามี:ต้นแบบอินเทอร์เฟซแบบเดียวกัน

บรรณานุกรม: ต้นแบบ - วิกิพีเดีย


1

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

IInterface classRef = new ObjectWhatever()

คุณสามารถใช้คลาสใดก็ได้ที่ใช้ IInterface? คุณจะต้องทำเมื่อไหร่

ลองดูคำถาม SE นี้เพื่อเป็นตัวอย่างที่ดี

ทำไมอินเตอร์เฟซสำหรับคลาส Java จึงเป็นที่ต้องการ

ใช้การเชื่อมต่อที่มีประสิทธิภาพหรือไม่

ถ้าเป็นเช่นนั้นเท่าไหร่

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

คุณจะหลีกเลี่ยงได้อย่างไรโดยไม่ต้องดูแลโค้ดสองบิต?

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

กรณีใช้งานที่ดีอย่างหนึ่ง: การนำรูปแบบกลยุทธ์ไปปฏิบัติ

ตัวอย่างโลกแห่งความจริงของรูปแบบกลยุทธ์


1

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

ฉันพบกฎเหล่านี้ช่วยฉัน:

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

2 . หากตามที่ระบุไว้ข้างต้นคุณต้องการนำ decoupling จากระบบภายนอก (ระบบจัดเก็บข้อมูล) ไปยังระบบของคุณเอง (local DB) จากนั้นใช้อินเตอร์เฟส

สังเกตว่ามีสองวิธีในการพิจารณาว่าจะใช้เมื่อใด หวังว่านี่จะช่วยได้


0

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

การทดสอบหน่วย

ในช่วงสองปีที่ผ่านมาฉันได้เขียนโปรเจ็กต์งานอดิเรกและฉันไม่ได้เขียนบททดสอบสำหรับเรื่องนี้ หลังจากเขียนประมาณ 50K บรรทัดฉันพบว่ามันจำเป็นจริงๆในการเขียนการทดสอบหน่วย ฉันไม่ได้ใช้อินเทอร์เฟซ (หรือประหยัดมาก) ... และเมื่อฉันทำการทดสอบหน่วยแรกฉันพบว่ามันซับซ้อน ทำไม?

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

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

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

public interface IPricable
{
    int Price { get; }
}

public interface ICar : IPricable

public abstract class Article
{
    public int Price { get { return ... } }
}

public class Car : Article, ICar
{
    // Price does not need to be defined here
}

วิธีนี้ไม่จำเป็นต้องใช้รหัสในการคัดลอกในขณะที่ยังมีประโยชน์ในการใช้รถยนต์เป็นอินเตอร์เฟส (ICar)


0

มาเริ่มด้วยคำจำกัดความกันก่อน:

อินเตอร์เฟส n ชุดของลายเซ็นทั้งหมดที่กำหนดโดยการทำงานของวัตถุนั้นเรียกว่าส่วนต่อประสานกับวัตถุ

พิมพ์ n อินเทอร์เฟซเฉพาะ

ตัวอย่างง่ายๆของอินเตอร์เฟซที่กำหนดไว้ดังกล่าวข้างต้นจะเป็นทุกวิธี PDO วัตถุเช่นquery(), commit(), close()ฯลฯ รวมทั้งไม่แยกจากกัน เมธอดเหล่านี้คืออินเทอร์เฟซของมันกำหนดชุดข้อความทั้งหมดคำขอที่สามารถส่งไปยังวัตถุ

ประเภทตามที่กำหนดไว้ข้างต้นเป็นอินเตอร์เฟซที่โดยเฉพาะอย่างยิ่ง ฉันจะใช้อินเตอร์เฟซรูปทรงทำขึ้นเพื่อแสดงให้เห็น: draw(), getArea(), getPerimeter()ฯลฯ ..

ถ้าวัตถุเป็นชนิดของฐานข้อมูลที่เราหมายความว่ามันยอมรับข้อความ / คำขอของอินเตอร์เฟซฐานข้อมูลquery(), commit()ฯลฯ .. วัตถุที่สามารถมากมายหลายชนิด คุณสามารถมีวัตถุฐานข้อมูลเป็นชนิดรูปร่างตราบเท่าที่มันดำเนินการติดต่อของตนซึ่งในกรณีนี้จะย่อยพิมพ์

วัตถุจำนวนมากสามารถมีอินเตอร์เฟซ / ประเภทที่แตกต่างกันมากมายและใช้อินเตอร์เฟสนั้นแตกต่างกัน สิ่งนี้ช่วยให้เราสามารถแทนที่วัตถุทำให้เราเลือกได้ว่าจะใช้วัตถุใด หรือที่เรียกว่า polymorphism

ลูกค้าจะรับรู้ถึงส่วนต่อประสานและไม่นำไปปฏิบัติ

ดังนั้นในการเขียนโปรแกรมสาระสำคัญในการอินเตอร์เฟซที่จะเกี่ยวข้องกับการทำประเภทของระดับนามธรรมบางอย่างเช่นShapeมีอินเตอร์เฟซที่ระบุไว้เท่านั้นเช่นdraw(), getCoordinates(), getArea()ฯลฯ .. และแล้วต้องเรียนคอนกรีตที่แตกต่างกันใช้อินเตอร์เฟซผู้เช่นระดับ Circle, ชั้นสแควร์, สามเหลี่ยมระดับ ดังนั้นโปรแกรมไปยังส่วนต่อประสานที่ไม่ใช้งาน


0

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

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