ปัญหาการตั้งชื่อ: ควรเปลี่ยนชื่อ“ ISomething” เป็น“ Something” หรือไม่ [ปิด]


68

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

สมมติว่า:

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

ยกตัวอย่างเช่นสองคนนี้ควรตั้งชื่ออะไร ParserและConcreteParser? ParserและParserImplementation?

public interface IParser {
    string Parse(string content);
    string Parse(FileInfo path);
}

public class Parser : IParser {
     // Implementations
}

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


30
นั่นไม่ได้ทำให้ศาสนามีน้อย คุณต้องมองหาเหตุผลไม่ใช่แค่ใครบางคน X พูดว่า Y
Mike Dunlavey

35
@ MikeDunlavey และนั่นคือเหตุผลสำหรับคำถาม!
Vinko Vrsalovic

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

9
@TripeHound กฎส่วนใหญ่ไม่ใช่ตัวเลือกที่ดีที่สุดเสมอไป หากมีเหตุผลบางอย่างที่ดีกว่าคนอื่นคนเดียวสามารถโน้มน้าวให้คนอื่น ๆ ในทีมทำคดีตามเหตุผลเหล่านั้น เพียงแค่สุ่มสี่สุ่มห้าส่งให้คนส่วนใหญ่โดยไม่ต้องประเมินสิ่งต่าง ๆ ผ่านทางไปสู่ความเมื่อยล้า ฉันเห็นจากคำตอบที่ว่าสำหรับกรณีที่เป็นรูปธรรมนี้ไม่มีเหตุผลที่ดีที่จะลบ Is แต่ก็ไม่ได้ทำให้โมฆะเมื่อถูกถามในตอนแรก
Vinko Vrsalovic

35
หนังสือ "Uncle Bob's" เน้นที่ JAVA ไม่ใช่ C # กระบวนทัศน์ส่วนใหญ่เหมือนกันกับ C # แต่อนุสัญญาการตั้งชื่อบางอย่างแตกต่างกัน (ดูที่ชื่อฟังก์ชั่น lowerCamelCase ใน Java) เมื่อคุณเขียนใน C # ให้ใช้ข้อกำหนดการตั้งชื่อ C # แต่อย่างไรก็ตามให้ทำตามจุดสำคัญของบทของเขาในการตั้งชื่อ: ชื่อที่อ่านได้ซึ่งสื่อสารความหมายของรายการ!
Bernhard Hiller

คำตอบ:


191

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


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

9
คุณไม่มีพื้นฐานสำหรับ "โดยทั่วไปแล้วควรหลีกเลี่ยง" ใน Java ฉันควรหลีกเลี่ยง ใน C # ควรสวมกอด ภาษาอื่น ๆ ปฏิบัติตามอนุสัญญาของตนเอง
พื้นฐาน

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

39

อินเตอร์เฟสเป็นแนวคิดเชิงตรรกะที่สำคัญดังนั้นอินเตอร์เฟสควรมีชื่อสามัญ ดังนั้นฉันควรจะมี

interface Something
class DefaultSomething : Something
class MockSomething : Something

กว่า

interface ISomething
class Something : ISomething
class MockSomething : ISomething

หลังมีหลาย isues:

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

59
ทำไมคุณไม่ปฏิบัติตามมาตรฐานที่ดีที่ C # ให้? สิ่งนี้มีประโยชน์อย่างไรที่ทำให้เกิดความสับสนและระคายเคืองโปรแกรมเมอร์ C # ทุกคนที่ติดตามคุณ
Robert Harvey

6
@ Wallenborn ไม่เป็นไร แต่ในความเป็นจริงคุณมักจะมีการใช้งานอินเทอร์เฟซที่ถูกต้องเพียงครั้งเดียวเพราะมันเป็นเรื่องปกติที่จะใช้อินเทอร์เฟซสำหรับการเยาะเย้ยในการทดสอบหน่วย ฉันไม่ต้องการที่จะใส่DefaultหรือRealหรือImplในจำนวนมากดังนั้นของชื่อชั้นเรียนของฉัน
เบน Aaronson

4
ฉันเห็นด้วยกับคุณ แต่ถ้าคุณเดินทางบนถนนสายนี้คุณจะต้องต่อสู้กับทุก linter ที่เคยทำเพื่อ C #
RubberDuck

3
ฉันไม่เห็นปัญหาใด ๆ กับการมีคลาส instantiable ที่มีชื่อเดียวกันกับอินเตอร์เฟสถ้าอินสแตนซ์ส่วนใหญ่ที่ใช้อินเตอร์เฟสนั้นจะเป็นคลาสนั้น ตัวอย่างเช่นโค้ดจำนวนมากต้องการการใช้งานทั่วไปที่ตรงไปตรงมาIList<T>และไม่สนใจว่าจะเก็บไว้ภายในอย่างไร ความสามารถในการตัดเพียงของIและมีชื่อชั้นที่ใช้งานดูเหมือน nicer กว่าต้องรู้อย่างน่าอัศจรรย์ (เช่นใน Java) รหัสที่ต้องการการดำเนินงานปกติของควรใช้List<T> ArrayList<T>
supercat

2
@ArtB เหตุผลสองประการ CFoo : Fooหนึ่งมีภาษาที่ประชุมเป็นเครื่องหมายสั้นที่ดีในชั้นเรียนไม่เหมือน ดังนั้นตัวเลือกนั้นจึงใช้ไม่ได้จริง ๆ สองคุณจะได้รับความไม่ลงรอยกันที่แปลกแล้วเพราะบางคลาสจะมีตัวทำเครื่องหมายและอื่น ๆ จะไม่ขึ้นอยู่กับว่าอาจมีการใช้งานอื่น ๆ ของอินเทอร์เฟซเดียวกัน แต่CFoo : Foo SqlStore : Storeนั่นคือปัญหาหนึ่งที่ฉันมีImplซึ่งเป็นเพียงเครื่องหมายที่น่าเกลียดที่สุด บางทีถ้ามีภาษาที่มีการประชุมของที่มักจะเพิ่มC(หรืออะไรก็ตาม) ที่อาจจะดีกว่าI
เบน Aaronson

27

นี่ไม่ใช่แค่การตั้งชื่อแบบแผน C # ไม่รองรับการสืบทอดหลายแบบดังนั้นการใช้สัญลักษณ์ฮังการีแบบดั้งเดิมนั้นมีประโยชน์เล็กน้อยแม้ว่าคุณจะได้รับมรดกจากคลาสฐานและใช้อินเตอร์เฟสอย่างน้อยหนึ่งอินเตอร์เฟส ดังนั้นนี่ ...

class Foo : BarB, IBarA
{
    // Better...
}

... ชอบสิ่งนี้มากกว่า ...

class Foo : BarB, BarA
{
    // Dafuq?
}

IMO


7
มันเป็นที่น่าสังเกตว่า Java เรียบร้อยหลีกเลี่ยงปัญหานี้โดยใช้คำหลักที่แตกต่างกันให้เป็นมรดกชั้นเรียนและใช้อินเตอร์เฟซคือVSinherits implements
Konrad Rudolph

6
@KonradRudolph extendsคุณหมายถึง
OrangeDog

2
@Andy มูลค่าเพิ่มคือเราสามารถหลีกเลี่ยง cruft สัญกรณ์ฮังการี แต่ยังรักษาความคมชัดทั้งหมด touted ในคำตอบนี้
Konrad Rudolph

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

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

15

ไม่ไม่ไม่.

ระเบียบการตั้งชื่อไม่ใช่หน้าที่ของลุงบ็อบแฟนคลับของคุณ พวกเขาไม่ใช่ฟังก์ชั่นของภาษา c # หรืออย่างอื่น

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

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

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

หากคุณพบว่าตัวเองอยู่ในบริบทของการตั้งค่าแบบอย่างนี้สำหรับฐานรหัสของคุณฉันขอให้คุณพิจารณาสิ่งนี้:

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

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

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

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

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


6
ความคงเส้นคงวาเป็นกุญแจสำคัญ แต่ในภาษาที่พิมพ์แบบคงที่และด้วยเครื่องมือการปรับโครงสร้างใหม่นั้นค่อนข้างง่ายและปลอดภัยในการเปลี่ยนชื่อ ดังนั้นหากนักพัฒนาคนแรกสร้างทางเลือกที่ไม่ดีในการตั้งชื่อคุณไม่จำเป็นต้องอยู่กับมันตลอดไป แต่คุณสามารถเปลี่ยนการตั้งชื่อในฐานรหัสของคุณเอง แต่คุณไม่สามารถเปลี่ยนได้ในกรอบงานหรือไลบรารีของบุคคลที่สาม เนื่องจาก I-prefix (ชอบหรือไม่) เป็นอนุสัญญาที่จัดตั้งขึ้นทั่วโลก. net จึงเป็นการดีกว่าที่จะปฏิบัติตามอนุสัญญามากกว่าที่จะไม่ทำตาม
JacquesB

หากคุณกำลังใช้ C #, คุณ gonna เห็นบรรดาIs หากรหัสของคุณใช้พวกเขาคุณจะเห็นพวกเขาทุกที่ หากรหัสของคุณไม่ได้ใช้คุณจะเห็นรหัสจากกรอบงานนำไปสู่ส่วนผสมที่คุณไม่ต้องการ
เซบาสเตียนเรดล

8

มีความแตกต่างเล็กน้อยระหว่าง Java และ C # ที่เกี่ยวข้องที่นี่ ใน Java สมาชิกทุกคนจะเป็นเสมือนโดยค่าเริ่มต้น ใน C # สมาชิกทุกคนจะถูกปิดผนึกตามค่าเริ่มต้น - ยกเว้นสมาชิกส่วนต่อประสาน

สมมติฐานที่มีอิทธิพลต่อแนวทางนี้ - ใน Java ทุกประเภทสาธารณะควรได้รับการพิจารณาว่าไม่เป็นไปตามหลักการทดแทนของ Liskov [1] หากคุณมีเพียงหนึ่งในการดำเนินงานที่คุณจะชื่อชั้นParser; หากคุณพบว่าคุณต้องการการนำไปใช้งานที่หลากหลายคุณเพียงแค่เปลี่ยนคลาสเป็นอินเทอร์เฟซที่มีชื่อเดียวกัน

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

นอกจากนี้ยังมีคุณสมบัติการใช้งานที่ดีในการทิ้งชื่อที่ไม่ใช่ I-prefixed ไว้สำหรับฟังก์ชั่นที่เกี่ยวข้องกับส่วนต่อประสานนั้นโดยไม่ต้องหันกลับไปใช้ส่วนต่อประสานกับคลาสนามธรรม (ซึ่งอาจเจ็บเนื่องจากไม่มีคลาสหลายสืบทอดใน C #) สิ่งนี้ได้รับความนิยมจาก LINQ ซึ่งใช้IEnumerable<T>เป็นอินเตอร์เฟสและEnumerableเป็นที่เก็บของวิธีการที่ใช้กับอินเตอร์เฟสนั้น สิ่งนี้ไม่จำเป็นใน Java ซึ่งอินเตอร์เฟสสามารถมีวิธีการใช้งานได้เช่นกัน

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

ฉันคิดว่าการให้เหตุผลของลุงบ็อบเป็นเช่นนี้:

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

หากคุณใช้สิ่งนี้กับIParserอินเทอร์เฟซตัวอย่างคุณจะเห็นได้อย่างชัดเจนว่าสิ่งที่เป็นนามธรรมนั้นอยู่ในอาณาเขต "ไร้ความหมาย" ไม่ว่าจะมีอะไรบางอย่างที่เฉพาะเจาะจงเกี่ยวกับการดำเนินงานที่เป็นรูปธรรมของตัวแยกวิเคราะห์ (เช่นJsonParser, XmlParser, ... ) หรือคุณก็ควรจะใช้ในชั้นเรียน ไม่มีสิ่งเช่น "การใช้งานเริ่มต้น" (แม้ว่าในบางสภาพแวดล้อมการทำเช่นนี้จะสมเหตุสมผล - โดยเฉพาะอย่างยิ่ง COM) ไม่ว่าจะมีการใช้งานที่เฉพาะเจาะจงหรือคุณต้องการระดับนามธรรมหรือวิธีการขยายสำหรับ "ค่าเริ่มต้น" อย่างไรก็ตามใน C # ยกเว้นว่า codebase ของคุณละเว้นI-prefix ไว้ให้เก็บไว้ เพียงจดบันทึกทุกครั้งที่คุณเห็นรหัสเช่นclass Something: ISomething- หมายความว่าบางคนไม่เก่งในการติดตาม YAGNI และสร้าง abstractions ที่สมเหตุสมผล

[1] - ในทางเทคนิคแล้วนี่ไม่ได้กล่าวถึงเฉพาะในกระดาษของ Liskov แต่เป็นหนึ่งในรากฐานของกระดาษ OOP ดั้งเดิมและในการอ่านของฉันใน Liskov เธอไม่ได้ท้าทายสิ่งนี้ ในการตีความที่เข้มงวดน้อยกว่า (ภาษาที่ใช้โดยภาษา OOP ส่วนใหญ่) นี่หมายความว่ารหัสใด ๆ ที่ใช้ประเภทสาธารณะที่มีไว้สำหรับการทดแทน


3
Nitpick: LSP ไม่ได้บอกว่าทุกประเภทจะไม่เป็นที่สิ้นสุด มันก็บอกว่าประเภทที่มีไม่ใช่ครั้งสุดท้ายที่ผู้บริโภคจะต้องสามารถใช้ subclasses โปร่งใส - และแน่นอน Java ยังมีประเภทสุดท้าย
Konrad Rudolph

@KonradRudolph ฉันได้เพิ่มเชิงอรรถที่เกี่ยวข้องกับเรื่องนี้; ขอบคุณสำหรับข้อเสนอแนะ :)
Luaan

4

ยกตัวอย่างเช่นสองคนนี้ควรตั้งชื่ออะไร Parser และ ConcreteParser? Parser และ Parser ใช้งานอย่างไร

ในโลกอุดมคติคุณไม่ควรเติมชื่อของคุณด้วยชื่อย่อ ("I ... ") แต่ให้ชื่อแสดงแนวคิด

ฉันอ่านที่ใดที่หนึ่ง (และไม่สามารถหาแหล่งที่มาได้) ความเห็นที่ว่าการประชุม ISomething นี้นำคุณไปสู่การกำหนดแนวคิด "IDollar" เมื่อคุณต้องการคลาส "Dollar" แต่การแก้ปัญหาที่ถูกต้องจะเป็นแนวคิดที่เรียกว่า "สกุลเงิน"

กล่าวอีกนัยหนึ่งอนุสัญญาฉบับนี้ให้ความสะดวกในการตั้งชื่อเรื่องต่าง ๆ และความผิดพลาดอย่างง่าย


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

การประชุม ISomething แนะนำชื่อ cruft / bad ที่กล่าวว่า cruft ไม่ใช่ cruft จริง *) เว้นแต่อนุสัญญาและแนวทางปฏิบัติที่ใช้ในการเขียนรหัสจะไม่เกิดขึ้นจริงอีกต่อไป หากการประชุมบอกว่า "ใช้ ISomething" ก็จะเป็นการดีที่สุดที่จะใช้เพื่อให้สอดคล้อง (และทำงานได้ดีขึ้น) กับผู้พัฒนารายอื่น


*) ฉันสามารถออกไปพูดแบบนี้เพราะ "cruft" ไม่ใช่แนวคิดที่แน่นอน :)


2

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

แต่ในกรณีของ Java และภาษาที่คล้ายคลึงกันซึ่งเป็นที่ต้องการอินเตอร์เฟสแทนที่จะเป็นคลาสที่เป็นรูปธรรมลุงลุงบ๊อบก็Iควรที่จะลบออกและ Interface ควรมีชื่อที่ง่ายที่สุดParserเช่นอินเทอร์เฟซ parser ของคุณ แต่แทนที่จะเป็นชื่อตามบทบาทParserDefaultคลาสควรมีชื่อที่อธิบายถึงวิธีการใช้งาน parser :

public interface Parser{
}

public class RegexParser implements Parser {
    // parses using regular expressions
}

public class RecursiveParser implements Parser {
    // parses using a recursive call structure
}

public class MockParser implements Parser {
    // BSes the parsing methods so you can get your tests working
}

นี่คือตัวอย่างเช่นวิธีการSet<T>, HashSet<T>และTreeSet<T>มีการตั้งชื่อ


5
อินเทอร์เฟซเป็นที่ต้องการใน C # เช่นกัน ใครบอกคุณว่าเป็นที่นิยมใช้ประเภทคอนกรีตใน dotnet?
RubberDuck

@RubberDuck มันซ่อนอยู่ในบางส่วนของสมมติฐานที่ภาษากำหนดให้คุณ เช่นความจริงที่ว่าสมาชิกโดยค่าเริ่มต้นและคุณต้องทำให้พวกเขาอย่างชัดเจนsealed virtualการเป็นเสมือนเป็นกรณีพิเศษใน C # แน่นอนว่าวิธีการเขียนโค้ดที่แนะนำในช่วงหลายปีที่ผ่านมามีสิ่งที่ต้องทำในอดีตมากมายเพื่อให้เข้ากันได้ :) ประเด็นสำคัญคือถ้าคุณได้รับส่วนต่อประสานใน C # (ซึ่งเริ่มต้นด้วยI) คุณรู้ว่ามันเป็น ประเภทนามธรรม หากคุณได้รับส่วนต่อประสานที่ไม่ได้ใช้ข้อสันนิษฐานทั่วไปคือเป็นประเภทที่คุณต้องการ LSP จะถูกสาป
Luaan

1
สมาชิกจะถูกปิดผนึกโดยค่าเริ่มต้นเพื่อให้เราทราบชัดเจนว่าผู้สืบทอดสามารถขี่ @Luaan ได้อย่างไร เป็นที่ยอมรับว่าบางครั้งก็เป็น PITA แต่ก็ไม่มีอะไรเกี่ยวข้องกับประเภทคอนกรีต เสมือนจริงไม่ใช่กรณีพิเศษใน C # ดูเหมือนคุณโชคร้ายที่ทำงานใน codebase C # ที่เขียนโดยมือสมัครเล่น ฉันขอโทษที่เป็นประสบการณ์ของคุณกับภาษา เป็นการดีที่สุดที่จะจำไว้ว่า dev ไม่ใช่ภาษาและในทางกลับกัน
RubberDuck

@RubberDuck ฮ่า ๆ ฉันไม่เคยพูดว่าฉันไม่ชอบ ฉันชอบมากเพราะคนส่วนใหญ่ไม่เข้าใจตรง แม้แต่ในหมู่โปรแกรมเมอร์ ความเห็นของฉันถูกปิดผนึกโดยค่าเริ่มต้นดีกว่า และเมื่อฉันเริ่มต้นด้วย C # ทุกคนเป็นมือสมัครเล่นรวมถึงทีม. NET :) ใน "OOP จริง" ทุกอย่างควรจะเป็นเสมือนจริง - ทุกประเภทควรเปลี่ยนได้ด้วยประเภทที่ได้รับซึ่งควรจะสามารถแทนที่พฤติกรรมใด ๆ . แต่ "OOP จริง" ได้รับการออกแบบสำหรับผู้เชี่ยวชาญไม่ได้คนที่เปลี่ยนจากการแทนที่หลอดไฟกับการเขียนโปรแกรมเพราะมันทำงานน้อยลงสำหรับเงินได้มากขึ้น :)
Luaan

ไม่ ... ไม่นั่นไม่ใช่วิธีที่ "OOP จริง" ทำงานเลย ใน Java คุณควรสละเวลาในการปิดผนึกวิธีที่ไม่ควรแทนที่ไม่ทิ้งไว้เหมือน Wild West ที่ใคร ๆ ก็สามารถขี่ได้ทุกอย่าง LSP ไม่ได้หมายความว่าคุณจะสามารถเอาชนะสิ่งที่คุณต้องการได้ LSP หมายความว่าคุณสามารถแทนที่ชั้นหนึ่งด้วยชั้นเรียนอื่นได้อย่างปลอดภัย Smh
RubberDuck

-8

คุณถูกต้องแล้วที่ I = ระเบียบการใช้อินเทอร์เฟซนี้ทำลายกฎ (ใหม่)

ในสมัยก่อนคุณจะต้องเติมทุกอย่างด้วยประเภท intCounter boolFlag เป็นต้น

หากคุณต้องการตั้งชื่อสิ่งที่ไม่มีคำนำหน้า แต่หลีกเลี่ยง 'ConcreteParser' คุณสามารถใช้เนมสเปซ

namespace myapp.Interfaces
{
    public interface Parser {...
}


namespace myapp.Parsers
{
    public class Parser : myapp.Interfaces.Parser {...
}

11
ใหม่? เก่าหรือไม่ เดี๋ยวก่อนฉันคิดว่าการเขียนโปรแกรมเกี่ยวกับความมีเหตุผลมากกว่าโฆษณา หรือว่าเป็นในสมัยก่อน?

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

8
ฉันเชื่อว่าคุณกำลังนึกถึงสัญกรณ์ฮังการีซึ่งคุณใช้ชื่อนำหน้าด้วยประเภท แต่มีเวลาในที่ที่คุณต้องการเขียนบางสิ่งบางอย่างที่ไม่เหมือนใครหรือintCounter boolFlagนี่เป็น "ประเภท" ที่ผิด แต่คุณจะเขียนpszName(ตัวชี้ไปยังสตริงที่ลงท้ายด้วย NUL ที่มีชื่อ), cchName(จำนวนตัวอักษรในสตริง "ชื่อ"), xControl(พิกัด x ของตัวควบคุม), fVisible(ตั้งค่าสถานะเพื่อแสดงสิ่งที่มองเห็นได้) pFile(ตัวชี้ไปยัง ไฟล์) hWindow(จัดการกับหน้าต่าง) และอื่น ๆ
Cody Gray

2
ยังคงมีค่ามากในเรื่องนี้ เรียบเรียงไม่ได้ไปจับข้อผิดพลาดเมื่อคุณเพิ่มไปxControl cchNameพวกเขาทั้งสองประเภทintแต่พวกเขามีความหมายที่แตกต่างกันมาก คำนำหน้าทำให้ความหมายเหล่านี้ชัดเจนต่อมนุษย์ ตัวชี้ไปยังสตริงที่สิ้นสุดด้วย NUL จะเหมือนกับตัวชี้ไปยังสตริงสไตล์ Pascal ไปยังคอมไพเลอร์ ในทำนองเดียวกันIคำนำหน้าสำหรับอินเตอร์เฟซที่เกิดขึ้นในภาษา C ++ ที่อินเตอร์เฟซเป็นเพียงชั้นเรียน (และแน่นอนสำหรับ C ที่ไม่มีสิ่งเช่นชั้นเรียนหรืออินเตอร์เฟซที่ระดับภาษามันเป็นเพียงการประชุม)
Cody Gray

4
@CodyGray Joel Spolsky เขียนบทความที่ดีทำรหัสผิดดูผิดเกี่ยวกับสัญกรณ์ฮังการีและวิธีการที่เข้าใจผิด (mis) เขามีความเห็นที่คล้ายกัน: คำนำหน้าควรเป็นประเภทระดับที่สูงกว่าไม่ใช่ประเภทที่จัดเก็บข้อมูลดิบ เขาใช้คำว่า "ชนิด" อธิบายว่า"ฉันใช้คำแบบนี้โดยมีจุดประสงค์เพราะ Simonyi ใช้คำชนิดผิดในกระดาษของเขาผิดพลาด
Joshua Taylor
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.