เชื่อมต่อกับคลาสนามธรรม


30

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

interface IFooWorker { void Work(); }

abstract class BaseWorker {
    ... base class behaviors ...
    public abstract void Work() { }
    protected string CleanData(string data) { ... }
}

class DbWorker : BaseWorker, IFooWorker {
    public void Work() {
        Repository.AddCleanData(base.CleanData(UI.GetDirtyData()));
    }
}

DbWorker คือสิ่งที่ได้รับอินเตอร์เฟส IFooWorker เนื่องจากเป็นการใช้งานอินเทอร์เฟซที่รวดเร็ว สมบูรณ์ตามสัญญา เพื่อนร่วมงานของฉันชอบสิ่งที่เกือบจะเหมือนกัน:

interface IFooWorker { void Work(); }

abstract class BaseWorker : IFooWorker {
    ... base class behaviors ...
    public abstract void Work() { }
    protected string CleanData(string data) { ... }
}

class DbWorker : BaseWorker {
    public void Work() {
        Repository.AddCleanData(base.CleanData(UI.GetDirtyData()));
    }
}

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

อะไรคือข้อดีข้อเสียของวิธีการของเขากับของฉันและทำไมหนึ่งควรใช้มากกว่าอีก?


คำแนะนำของคุณคล้ายกับการสืบทอดเพชรซึ่งอาจทำให้เกิดความสับสนมากขึ้น
Spoike

@Spike: วิธีการที่เห็น?
Niing

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

@Spike: ทำไมคำถามนี้จะทำให้เกิดการสืบทอดเพชร?
Niing

1
@Niing: สืบทอด BaseWorker และ IFooWorker ด้วยวิธีการเดียวกันที่เรียกว่าWorkเป็นปัญหาที่เพชรมรดก (ในกรณีที่พวกเขาทั้งสองตอบสนองสัญญามากกว่าWorkวิธีการ) ในจาวาคุณจำเป็นต้องใช้วิธีการของส่วนต่อประสานเพื่อWorkหลีกเลี่ยงปัญหาที่โปรแกรมควรใช้ในวิธีนั้น ภาษาเช่น C ++ ไม่ได้แปลความหมายนี้ให้คุณ
Spoike

คำตอบ:


24

ฉันเชื่อว่าคลาสไม่ควรใช้อินเทอร์เฟซเว้นแต่ว่าคลาสนั้นสามารถใช้ได้เมื่อต้องการการใช้อินเทอร์เฟซ

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

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

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


1
+1 สำหรับการชี้ให้เห็นว่าเพียงเพราะBaseWorkerไม่ instantiable โดยตรงไม่ได้หมายความว่าให้ไม่ใช้BaseWorker myWorker IFooWorker
Avner Shahar-Kashtan

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

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

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

46

ฉันต้องเห็นด้วยกับเพื่อนร่วมงานของคุณ

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

หากคุณไม่ได้กำหนด Work () เป็นวิธีของ BaseWorker หรือถ้า IFooWorker มีวิธีอื่น ๆ ที่ subclasses ของ BaseWorker ไม่ต้องการหรือจำเป็นต้องใช้ (แน่นอน) คุณจะต้องระบุว่า subclasses ใดที่ใช้ IFooWorker จริง


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

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

@Aaraught จุดที่ดีแม้ว่าอาจเป็นได้ว่ามีบางสิ่งที่การใช้งาน IFoo ทั้งหมดหรือส่วนใหญ่จำเป็นต้องสืบทอด (เช่นการจัดการกับบริการหรือการใช้งาน DAO ของคุณ)
Matthew Flynn

2
จากนั้นไม่ควรชั้นฐานจะเรียกMyDaoFooหรือSqlFooหรือHibernateFooหรืออะไรก็ตามที่จะแสดงให้เห็นว่ามันเป็นเพียงต้นไม้ต้นหนึ่งที่เป็นไปได้ของการเรียนการดำเนินการIFoo? ก็จะยิ่งมากขึ้นในการดมกลิ่นกับผมถ้าชั้นฐานเป็นคู่ไปยังห้องสมุดที่เฉพาะเจาะจงหรือแพลตฟอร์มและมีการกล่าวถึงนี้ในชื่อ ...
Aaronaught

@Aaronaught - ใช่ ฉันเห็นด้วยอย่างสมบูรณ์
Matthew Flynn

11

โดยทั่วไปฉันเห็นด้วยกับเพื่อนร่วมงานของคุณ

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

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

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


ฉันชอบเสียงของคำตอบนี้ แต่ไม่ค่อยทำตามส่วนสุดท้าย คุณกำลังแนะนำว่า BetterBaseWorker จะเป็นคลาสนามธรรมอิสระที่ใช้ IFooWorker ดังนั้นปลั๊กอินสมมุติของฉันสามารถพูดได้ว่า "oh boy! The Better Base Worker นั้นสิ้นสุดแล้ว!" และเปลี่ยนการอ้างอิง BaseWorker ใด ๆ กับ BetterBaseWorker และเรียกมันว่าวัน (อาจเล่นกับค่าที่ส่งคืนใหม่ ๆ ที่ให้ฉันดึงแถบขนาดใหญ่ของรหัสซ้ำซ้อนของฉัน ฯลฯ )
แอนโธนี

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

6

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

ใช้คลาสเฟรมเวิร์กการรวบรวมของ Java:

abstract class AbstractList
class LinkedList extends AbstractList implements Queue

ความจริงที่ว่าคิวสัญญาจะดำเนินการโดยLinkedListไม่ได้ผลักดันความกังวลลงไปในAbstractList

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


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

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

1
ฉันได้รับคำเตือนแล้ว แต่คุณจะสังเกตได้ว่าคลาสนามธรรมของ OP ได้รวมวิธีนามธรรมที่ตรงกับอินเทอร์เฟซ: BaseWorker นั้นเป็น IFooWorker โดยปริยาย การทำให้ชัดเจนทำให้ข้อเท็จจริงนี้ใช้งานได้มากขึ้น มีวิธีการมากมายที่รวมอยู่ในคิวที่ไม่รวมอยู่ใน AbstractList อย่างไรก็ตามและเช่นนี้ AbstractList ไม่ใช่คิวแม้ว่ามันอาจเป็นลูก AbstractList และ Queue เป็นมุมฉาก
Matthew Flynn

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

แดงมันเป็นเวลาหกปีแล้ว :)
Mihai Danila

2

ฉันจะถามคำถามว่าจะเกิดอะไรขึ้นเมื่อคุณเปลี่ยนแปลงIFooWorkerเช่นการเพิ่มวิธีการใหม่

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

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


1

ก่อนอื่นให้คิดว่าคลาสฐานนามธรรมคืออะไรและอินเตอร์เฟสคืออะไร พิจารณาเมื่อคุณจะใช้อย่างใดอย่างหนึ่งและอื่น ๆ และเมื่อคุณจะไม่

เป็นเรื่องธรรมดาสำหรับคนที่คิดว่าทั้งสองเป็นแนวคิดที่คล้ายกันมาก ๆ ในความเป็นจริงมันเป็นคำถามสัมภาษณ์ที่พบบ่อย

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

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

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

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


1

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

ครั้งแรก:

มีคำอธิบายทั่วไปสองอย่างของอินเตอร์เฟส:

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

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

มีคำอธิบายทั่วไปสองแบบของคลาสนามธรรม:

  1. คลาสนามธรรมคือ yknow สิ่งที่ไม่ใช่สิ่งที่สามารถทำได้ มันเป็นสาระสำคัญไม่ใช่ของจริงเลย รอก่อนนี่จะช่วยได้ เรือเป็นคลาสนามธรรม แต่เพลย์บอยยอชต์เซ็กซี่น่าจะเป็นคลาสย่อยของ BaseBoat

  2. ฉันอ่านหนังสือในชั้นเรียนนามธรรมและบางทีคุณควรอ่านหนังสือเล่มนั้นเพราะคุณอาจไม่เข้าใจและจะทำผิดถ้าคุณยังไม่ได้อ่านหนังสือเล่มนั้น

โดยวิธีการที่หนังสือ Citers ดูน่าประทับใจเสมอแม้ว่าฉันจะยังคงเดินสับสน

ประการที่สอง:

บน SO มีคนถามคำถามแบบง่ายกว่านี้คลาสสิคว่า "ทำไมต้องใช้ส่วนต่อประสานต่าง ๆ ? อะไรคือความแตกต่างอะไร และหนึ่งคำตอบใช้นักบินกองทัพอากาศเป็นตัวอย่างง่ายๆ มันไม่ได้ขึ้นบก แต่มันได้จุดประกายความคิดเห็นที่ยอดเยี่ยมซึ่งหนึ่งในนั้นกล่าวถึงส่วนต่อประสาน IFlyable ที่มีวิธีการเช่น TakeOff, pilotEject เป็นต้นและนี่เป็นการคลิกจริง ๆ สำหรับฉันเป็นตัวอย่างในโลกแห่งความเป็นจริงว่าทำไมอินเตอร์เฟสไม่เพียงมีประโยชน์ แต่สำคัญมาก อินเทอร์เฟซทำให้วัตถุ / คลาสใช้งานง่ายหรืออย่างน้อยก็ให้ความรู้สึกว่าเป็น อินเทอร์เฟซไม่ใช่เพื่อประโยชน์ของวัตถุหรือข้อมูล แต่เป็นสิ่งที่ต้องมีการโต้ตอบกับวัตถุนั้น ผลไม้คลาสสิค -> Apple-> Fuji หรือ Shape-> Triangle-> ตัวอย่างด้านการสืบทอดด้านเท่ากันหมดเป็นตัวอย่างที่ดีสำหรับการทำความเข้าใจเกี่ยวกับอนุกรมวิธานของวัตถุที่อ้างอิงจากลูกหลานของมัน มันแจ้งให้ผู้บริโภคและโปรเซสเซอร์ทราบถึงคุณสมบัติทั่วไปพฤติกรรมไม่ว่าวัตถุจะเป็นกลุ่มของสิ่งต่าง ๆ จะเชื่อมต่อระบบของคุณหากคุณใส่ผิดที่หรืออธิบายข้อมูลทางประสาทสัมผัสตัวเชื่อมต่อไปยังแหล่งข้อมูลเฉพาะหรือ ข้อมูลทางการเงินที่จำเป็นในการทำบัญชีเงินเดือน

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

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

ที่สาม:

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

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


0

มีแง่มุมอื่นว่าทำไมDbWorkerต้องดำเนินการIFooWorkerตามที่คุณแนะนำ

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


-1

ในการเริ่มต้นฉันต้องการกำหนดอินเทอร์เฟซและคลาสนามธรรมแตกต่างกัน:

Interface: a contract that each class must fulfill if they are to implement that interface
Abstract class: a general class (i.e vehicle is an abstract class, while porche911 is not)

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

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

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


-3

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


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

มันเป็นตัวอย่างที่แย่มากจากมุมมองของ OOP ในขณะที่คุณพูดว่า "คลาสพื้นฐานเป็นเพียงการนำไปใช้งาน" (ควรมีหรือไม่มีจุดสำหรับอินเตอร์เฟส) คุณกำลังพูดว่าจะมีคลาสที่ไม่ใช่ผู้ปฏิบัติงานที่ใช้อินเทอร์เฟซ IFooWorker จากนั้นคุณจะมีคลาสฐานที่เป็น fooworker พิเศษ (เราควรถือว่ามี barworkers ด้วย) และคุณจะมี fooworker อื่น ๆ ที่ไม่ได้เป็นคนทำงานพื้นฐาน! นั่นคือถอยหลัง มากกว่าหนึ่งวิธี
Martin Maat

1
บางทีคุณอาจติดอยู่กับคำว่า "คนทำงาน" ในอินเทอร์เฟซ สิ่งที่เกี่ยวกับ "แหล่งข้อมูล" ถ้าเรามี IDatasource <T> เราสามารถมี SqlDatasource <T>, RestDatasource <T>, MongoDatasource <T> ได้เล็กน้อย ธุรกิจตัดสินใจว่าการปิดอินเทอร์เฟซของข้อมูลทั่วไปไปสู่การปิดการใช้งานของแหล่งข้อมูลนามธรรม
Bryan Boettcher

มันเหมาะสมที่จะใช้การสืบทอดเพียงเพื่อประโยชน์ของ DRY และนำการใช้งานทั่วไปไปใช้ในคลาสฐาน แต่คุณจะไม่จัดตำแหน่งวิธีการใด ๆ ที่ถูกแทนที่ด้วยวิธีการอินเทอร์เฟซใด ๆ คลาสพื้นฐานจะประกอบด้วยตรรกะการสนับสนุนทั่วไป หากพวกเขาเข้าแถวนั่นจะแสดงการรับมรดกแก้ปัญหาของคุณได้ดีและอินเทอร์เฟซจะไม่ตอบสนองวัตถุประสงค์ คุณใช้อินเทอร์เฟซในกรณีที่การสืบทอดไม่สามารถแก้ปัญหาของคุณได้เนื่องจากลักษณะทั่วไปที่คุณต้องการสร้างแบบจำลองไม่เหมาะกับแผนผังชั้น Singke และใช่การใช้ถ้อยคำในตัวอย่างทำให้ผิดทั้งหมด
Martin Maat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.