มันสมเหตุสมผลหรือไม่ที่จะนิยามอินเตอร์เฟสถ้าฉันมีคลาสนามธรรมอยู่แล้ว?


12

ฉันมีชั้นเรียนพร้อมฟังก์ชั่นเริ่มต้น / แชร์ ฉันใช้abstract classมัน

public interface ITypeNameMapper
{
    string Map(TypeDefinition typeDefinition);
}

public abstract class TypeNameMapper : ITypeNameMapper
{
    public virtual string Map(TypeDefinition typeDefinition)
    {
        if (typeDefinition is ClassDefinition classDefinition)
        {
            return Map(classDefinition);
        }
        ...

        throw new ArgumentOutOfRangeException(nameof(typeDefinition));
    }

    protected abstract string Map(ClassDefinition classDefinition);
}

ITypeNameMapperที่คุณสามารถดูฉันยังมีอินเตอร์เฟซ มันเหมาะสมหรือไม่ที่จะกำหนดอินเทอร์เฟซนี้ถ้าฉันมีคลาสนามธรรมTypeNameMapperหรือabstract classเพียงพอหรือไม่?

TypeDefinition ในตัวอย่างที่น้อยที่สุดนี้ก็เป็นนามธรรมเช่นกัน


4
เพียงแค่ FYI - ใน OOD การตรวจสอบประเภทของรหัสเป็นการระบุว่าลำดับชั้นของคลาสไม่ถูกต้องซึ่งเป็นการบอกให้คุณสร้างคลาสที่ได้รับจากประเภทที่คุณกำลังตรวจสอบ ดังนั้นในกรณีนี้คุณต้องการอินเตอร์เฟส ITypeMapper ที่ระบุเมธอด Map () จากนั้นใช้อินเตอร์เฟสนั้นในคลาส TypeDefinition และ ClassDefinition ด้วยวิธีนี้ ClassAssembler ของคุณจะทำซ้ำผ่านรายการ ITypeMappers เรียก Map () ในแต่ละรายการและรับค่าสตริงที่ต้องการจากทั้งสองประเภทเนื่องจากแต่ละประเภทมีการใช้งานของตนเอง
Rodney P. Barbati

1
การใช้คลาสพื้นฐานแทนส่วนติดต่อนั้นเรียกว่า "การเบิร์นฐาน" หากสามารถทำได้ด้วยส่วนต่อประสานให้ทำกับส่วนต่อประสาน ระดับนามธรรมนั้นจะกลายเป็นประโยชน์เสริม artima.com/intv/dotnet.html เฉพาะการควบคุมระยะไกลคุณจะต้องได้รับจาก MarshalByRefObject ดังนั้นหากคุณต้องการ remoting คุณไม่สามารถได้รับจากสิ่งอื่นใด
Ben

@ RodneyP.Barbati จะไม่ทำงานหากฉันต้องการมีผู้ทำแผนที่จำนวนมากสำหรับประเภทเดียวกันที่ฉันสามารถเปลี่ยนได้ตามสถานการณ์
Konrad

1
@Ben เผาฐานที่น่าสนใจ ขอบคุณสำหรับอัญมณี!
Konrad

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

คำตอบ:


31

ใช่เพราะC #ไม่อนุญาตให้มีการสืบทอดหลายรายการยกเว้นกับส่วนต่อประสาน

ดังนั้นถ้าฉันมีคลาสที่เป็น TypeNameMapper และ SomethingelseMapper ฉันสามารถทำได้:

class MultiFunctionalClass : ITypeNameMapper, ISomethingelseMapper 
{
    private TypeNameMapper map1
    private SomethingelseMapper map2

    public string Map(TypeDefinition typeDefinition) { return map1.Map(typeDefintion);}

    public string Map(OtherDef otherDef) { return map2.Map(orderDef); }
}

4
@Liath: ความรับผิดชอบเดี่ยวเป็นปัจจัยที่มีส่วนร่วมว่าทำไมถึงต้องมีการสืบทอด ICanFlyพิจารณาสามลักษณะที่เป็นไปได้: ICanRun, ICanSwim, คุณต้องสร้าง 8 คลาสสิ่งมีชีวิตหนึ่งคลาสสำหรับทุกชุดของลักษณะ (YYY, YYN, YNY, ... , NNY, NNN) SRP กำหนดให้คุณใช้แต่ละพฤติกรรม (บิน, วิ่ง, ว่ายน้ำ) เพียงครั้งเดียวแล้วนำไปใช้กับสัตว์ 8 ตัว การประพันธ์จะดียิ่งขึ้นที่นี่ แต่การเพิกเฉยการแต่งเพลงเป็นครั้งที่สองคุณควรเห็นความจำเป็นในการสืบทอด / ใช้พฤติกรรมที่สามารถนำมาใช้ซ้ำได้หลายครั้งเนื่องจาก SRP
Flater

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

3
(1) การปรับใช้อินเตอร์เฟสคือการสืบทอด? (2) คำถามและคำตอบทั้งคู่ควรมีคุณสมบัติ "มันขึ้นอยู่กับ." (3) การสืบทอดหลายแบบต้องการสติกเกอร์คำเตือน (4) ฉันสามารถแสดงให้คุณเห็น K ของการinterfaceทุจริตอย่างเป็นทางการ craptacular .. การใช้งานซ้ำซ้อนที่ควรจะอยู่ในฐาน - ที่มีการพัฒนาอย่างอิสระ! การเปลี่ยนแปลงตรรกะเงื่อนไขขับขยะนี้สำหรับความต้องการของแม่แบบวิธีการห่อหุ้มที่แตกสลาย สิ่งที่ฉันชอบ: ชั้นเรียน 1 วิธีแต่ละชั้นใช้วิธีของตนเอง 1 วิธีinterfaceโดยแต่ละคลาสจะมีอินสแตนซ์เดียวเท่านั้น ... ฯลฯ ฯลฯ และอื่น ๆ
Radarbob

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

4
@radarbob Poetic ^ _ ^ ฉันเห็นด้วย; ในขณะที่ค่าใช้จ่ายของอินเทอร์เฟซต่ำ แต่ก็ไม่มีอยู่จริง ไม่มากในการเขียนพวกเขา แต่ในสถานการณ์การดีบักเป็น 1-2 ขั้นตอนเพิ่มเติมเพื่อให้ได้พฤติกรรมจริงซึ่งสามารถเพิ่มขึ้นอย่างรวดเร็วเมื่อคุณได้รับระดับครึ่งโหลลึกนามธรรม ในห้องสมุดมักจะคุ้มค่า แต่ในแอปพลิเคชันการปรับเปลี่ยนจะดีกว่าการวางนัยทั่วไปก่อนกำหนด
Errorsatz

1

อินเทอร์เฟซและคลาสนามธรรมมีจุดประสงค์ที่แตกต่างกัน:

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

ในตัวอย่างของคุณinterface ITypeNameMapperกำหนดความต้องการของลูกค้าและabstract class TypeNameMapperไม่เพิ่มมูลค่าใด ๆ


2
คลาสนามธรรมเป็นของลูกค้าเช่นกัน ฉันไม่รู้ว่าคุณหมายถึงอะไร ทุกอย่างที่เป็นpublicของลูกค้า TypeNameMapperกำลังเพิ่มมูลค่าจำนวนมากเพราะช่วยให้ผู้ใช้ไม่สามารถเขียนตรรกะทั่วไปบางอย่างได้
Konrad

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

0

แนวคิดทั้งหมดของอินเตอร์เฟสถูกสร้างขึ้นเพื่อสนับสนุนตระกูลของคลาสที่มี API ที่แบ่งใช้

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

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

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


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

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

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

1
@supercat ฉันสามารถยอมรับว่าคุณกำลังเขียนห้องสมุด หากเป็นแอปพลิเคชันฉันไม่เห็นปัญหาใด ๆ ในการแทนที่เหตุการณ์ทั้งหมดในครั้งเดียว My Eclipse สามารถทำมันได้ (Java ไม่ใช่ C #) แต่ถ้ามันทำไม่ได้มันก็เหมือนกับfind ... -exec perl -pi -e ...และอาจแก้ไขข้อผิดพลาดในการคอมไพล์เล็กน้อย ประเด็นของฉันคือ: ข้อดีของการไม่มีสิ่งไร้ประโยชน์ (ในปัจจุบัน) นั้นใหญ่กว่าการประหยัดในอนาคตที่เป็นไปได้
maaartinus

ประโยคแรกจะถูกต้องถ้าคุณทำเครื่องหมายinterfaceเป็นรหัส (คุณกำลังพูดถึง C # - interfaces ไม่ใช่อินเทอร์เฟซแบบนามธรรมเช่นชุดของคลาส / ฟังก์ชั่น / ฯลฯ ) และจบมัน "... แต่ก็ยังผิดกฎหมายเต็ม มรดก." ไม่จำเป็นต้องinterfaceมีถ้าคุณมี MI
Deduplicator

0

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

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

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

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


-1

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

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

เหตุผลสำหรับ:

  • คุณจะได้รับมันฟรีอย่างมีประสิทธิภาพเนื่องจากคลาสพื้นฐานนั้นinterfaceคุณไม่จำเป็นต้องกังวลเกี่ยวกับมันในการใช้งานลูกใด ๆ
  • กรอบ IoC ส่วนใหญ่และห้องสมุด Mocking คาดหวังinterfaces ในขณะที่หลายคนทำงานกับคลาสนามธรรมมันไม่ได้ถูกกำหนดเสมอไปและฉันเป็นผู้เชื่อที่ยิ่งใหญ่ในการติดตามเส้นทางเดียวกันกับผู้ใช้ห้องสมุดอีก 99% หากเป็นไปได้

เหตุผลต่อ:

  • พูดอย่างเคร่งครัด (ตามที่คุณชี้ให้เห็น) คุณไม่จำเป็นต้องทำจริงๆ
  • หากclass/ interfaceการเปลี่ยนแปลงมีค่าใช้จ่ายเพิ่มเติมเล็กน้อย

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

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