ทำไมการเข้ารหัสชื่ออาร์กิวเมนต์ในชื่อฟังก์ชั่นจึงไม่เกิดขึ้นบ่อยนัก? [ปิด]


47

ในรหัสสะอาดผู้เขียนให้ตัวอย่างของ

assertExpectedEqualsActual(expected, actual)

VS

assertEquals(expected, actual)

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


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

54
หลายคนอาจโต้เถียงกับรูปแบบการตั้งชื่อครั้งแรกเพราะมันมีความละเอียดมากเกินกว่าที่จะช่วยให้ชัดเจน โดยเฉพาะอย่างยิ่งสำหรับassertEquals()วิธีการนั้นถูกใช้เป็นร้อย ๆ ครั้งในฐานของรหัสดังนั้นจึงคาดว่าผู้อ่านจะคุ้นเคยกับการประชุมครั้งเดียว กรอบการทำงานที่แตกต่างกันมีการประชุมที่แตกต่างกัน (เช่น(actual, expected) or an agnostic (ซ้าย, ขวา)) "แต่จากประสบการณ์ของฉันมันเป็นแหล่งของความสับสนเล็กน้อย
amon

5
เนื่องจากกำไรมีขนาดเล็กมากเมื่อเทียบกับผลประโยชน์ของตนว่าคนที่มีสติปัญญาอาจเดินจากไป หากคุณต้องการแนวทางที่คล่องแคล่วมากขึ้นคุณควรลองassert(a).toEqual(b)(แม้ว่า IMO จะยังคงเป็นคำพูดที่ไม่จำเป็น) ซึ่งคุณอาจเชื่อมโยงการยืนยันที่เกี่ยวข้องสองสามข้อ
Adriano Repetti

18
เราจะรู้ได้อย่างไรว่าค่าจริงและที่คาดหวังคือค่า? แน่นอนมันควรจะเป็นassertExpectedValueEqualsActualValueอย่างไร แต่รอทำอย่างไรเราจะจำไว้ว่าจะใช้==หรือ.equalsหรือObject.equals? มันควรจะเป็นassertExpectedValueEqualsMethodReturnsTrueWithActualValueParameterอย่างไร
253751

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

คำตอบ:


66

เพราะมันเป็นประเภทที่มากขึ้นและมากขึ้นในการอ่าน

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

นักพัฒนาหลายคนใช้ IDE

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

การเข้ารหัสข้อโต้แย้งแนะนำการทำซ้ำและการมีเพศสัมพันธ์

ชื่อของพารามิเตอร์ควรมีเอกสารว่าพวกเขาคืออะไร โดยการเขียนชื่อออกมาในชื่อเมธอดเรากำลังทำซ้ำข้อมูลนั้นในลายเซ็นเมธอดเช่นกัน นอกจากนี้เรายังสร้างการเชื่อมต่อระหว่างชื่อวิธีการและพารามิเตอร์ พูดexpectedและactualสับสนกับผู้ใช้ของเรา เปลี่ยนจากเป็นassertEquals(expected, actual)เป็นassertEquals(planned, real)ไม่ต้องการเปลี่ยนรหัสลูกค้าโดยใช้ฟังก์ชัน การเปลี่ยนจากassertExpectedEqualsActual(expected, actual)เป็นassertPlannedEqualsReal(planned, real)หมายถึงการเปลี่ยนการแตกหักเป็น API หรือเราจะไม่เปลี่ยนชื่อเมธอดซึ่งทำให้สับสนอย่างรวดเร็ว

ใช้ประเภทแทนอาร์กิวเมนต์ที่ไม่ชัดเจน

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

class Expected<T> {
    private T value;
    Expected(T value) { this.value = value; }
    static Expected<T> is(T value) { return new Expected<T>(value); }
}

class Actual<T> {
    private T value;
    Actual(T value) { this.value = value; }
    static Actual<T> is(T value) { return new Actual<T>(value); }
}

static assertEquals(Expected<T> expected, Actual<T> actual) { /* ... */ }

// How it is used
assertEquals(Expected.is(10), Actual.is(x));

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


5
ถ้าคุณใช้ IDE คุณมีชื่อพารามิเตอร์ในตัวช่วยบอลลูน หากคุณไม่ได้ใช้งานอย่างใดอย่างหนึ่งการจดจำชื่อฟังก์ชันจะเทียบเท่ากับการจดจำอาร์กิวเมนต์ดังนั้นจึงไม่มีสิ่งใดได้รับ
ปีเตอร์ - Reinstate Monica

29
หากคุณคัดค้านที่assertExpectedEqualsActual"เพราะมันเป็นประเภทที่มากขึ้นในการอ่านและอื่น ๆ " แล้วคุณจะสนับสนุนได้assertEquals(Expected.is(10), Actual.is(x))อย่างไร?
ruakh

9
@ruakh มันไม่ได้เทียบเคียง assertExpectedEqualsActualยังคงต้องการให้โปรแกรมเมอร์ดูแลเพื่อระบุอาร์กิวเมนต์ในลำดับที่ถูกต้อง assertEquals(Expected<T> expected, Actual<T> actual)ลายเซ็นใช้คอมไพเลอร์ในการบังคับใช้การใช้งานที่ถูกต้องซึ่งเป็นวิธีการที่แตกต่างกันอย่างสิ้นเชิง คุณสามารถเพิ่มประสิทธิภาพวิธีนี้สำหรับระยะเวลาสั้น ๆ เช่นexpect(10).equalsActual(x)แต่นั่นก็ไม่ได้คำถาม ...
โฮล

6
นอกจากนี้ในกรณีพิเศษนี้ (==) ลำดับของการขัดแย้งนั้นไม่เกี่ยวข้องกับค่าสุดท้าย คำสั่งซื้อมีผลเฉพาะกับผลข้างเคียงเท่านั้น (การรายงานความล้มเหลว) เมื่อทำการสั่งซื้อมีความสำคัญมากขึ้น ตัวอย่างเช่น strcpy (dest, src)
Kristian H

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

20

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

การใช้คำฟุ่มเฟื่อยไม่ได้หมายถึงความชัดเจนมากขึ้นเสมอ พิจารณา

copyFromSourceStreamToDestinationStreamWithoutBlocking(fileStreamFromChoosePreferredOutputDialog, heuristicallyDecidedSourceFileHandle)

กับ

copy(output, source)

ทั้งสองมีข้อผิดพลาดที่เหมือนกัน แต่จริง ๆ แล้วเราทำให้การค้นหาข้อผิดพลาดนั้นง่ายขึ้นหรือไม่ ตามกฎทั่วไปสิ่งที่ง่ายที่สุดในการดีบั๊กคือเมื่อทุกอย่างสั้นที่สุดยกเว้นบางสิ่งที่มีข้อบกพร่องและสิ่งเหล่านั้นมีความละเอียดเพียงพอที่จะบอกคุณว่าเกิดอะไรขึ้น

มีประวัติอันยาวนานในการเพิ่มคำฟุ่มเฟื่อย ตัวอย่างเช่นมีเป็นที่นิยมโดยทั่วไป " โน้ตฮังการี " lpszNameซึ่งทำให้เรามีชื่อที่ยอดเยี่ยมเช่น ที่ลดลงโดยทั่วไปตามข้างทางในประชาชนทั่วไปโปรแกรมเมอร์ อย่างไรก็ตามการเพิ่มตัวอักษรในชื่อตัวแปรสมาชิก (เช่นmNameหรือm_Nameหรือname_) ยังคงมีความนิยมในบางวงการ คนอื่นลดลงอย่างสิ้นเชิง ฉันทำงานกับ codebase จำลองฟิสิกส์ซึ่งเอกสารสไตล์การเข้ารหัสต้องการให้ฟังก์ชันใด ๆ ที่คืนค่าเวกเตอร์ต้องระบุเฟรมของเวกเตอร์ในการเรียกใช้ฟังก์ชัน ( getPositionECEF)

คุณอาจสนใจในบางภาษาที่ Apple ได้รับความนิยม Objective-C รวมถึงชื่ออาร์กิวเมนต์เป็นส่วนหนึ่งของฟังก์ชั่นลายเซ็น (ฟังก์ชั่นที่[atm withdrawFundsFrom: account usingPin: userProvidedPin]เขียนในเอกสารประกอบเป็นwithdrawFundsFrom:usingPin:. นั่นคือชื่อของฟังก์ชั่น) Swift ได้ทำการตัดสินใจที่คล้ายกันโดยกำหนดให้คุณใส่ชื่ออาร์กิวเมนต์ในการเรียกใช้ฟังก์ชัน ( greet(person: "Bob", day: "Tuesday"))


13
จุดอื่น ๆ ทั้งหมดกันว่าจะห่างไกลให้อ่านง่ายขึ้นถ้า ถูกเขียนขึ้นcopyFromSourceStreamToDestinationStreamWithoutBlocking(fileStreamFromChoosePreferredOutputDialog, heuristicallyDecidedSourceFileHandle) ดูสิว่ามันง่ายขนาดไหน! นั่นเป็นเพราะมันง่ายเกินไปที่จะพลาดการเปลี่ยนแปลงเล็ก ๆ น้อย ๆ ที่อยู่ตรงกลางผ่าน humungousunbrokenwordsalad และใช้เวลานานกว่าในการพิจารณาว่าขอบเขตของคำนั้นเป็นอย่างไร ยอดเยี่ยมสับสน copy_from_source_stream_to_destination_stream_without_blocking(file_stream_from_choose_preferred_output_dialog, heuristically_decided_source_file_handle)
tchrist

1
ไวยากรณ์ obj-C withdrawFundsFrom: account usingPin: userProvidedPinนั้นยืมมาจาก SmallTalk จริง ๆ
joH1

14
@tchrist ระวังให้แน่ใจว่าคุณมั่นใจในหัวข้อที่เกี่ยวข้องกับสงครามศักดิ์สิทธิ์ อีกด้านไม่ผิดเสมอไป
Cort Ammon

3
@tchrist Addingunderscoresnakesthingseasiertoreadnotharderasyouseeกำลังจัดการกับข้อโต้แย้ง คำตอบที่นี่ใช้ตัวพิมพ์ใหญ่ซึ่งคุณไม่ใช้ AddingCapitalizationMakesThingsEasyEnoughToReadAsYouCanSeeHere. ประการที่สอง 9 ครั้งจาก 10 ชื่อไม่ควรเติบโตเกิน[verb][adjective][noun](ซึ่งแต่ละบล็อกเป็นตัวเลือก) รูปแบบที่สามารถอ่านได้ดีโดยใช้ตัวพิมพ์ใหญ่อย่างง่าย:ReadSimpleName
Flater

5
@tchrist - วิทยาศาสตร์ของการศึกษาของคุณ ( ลิงค์ข้อความแบบเต็มฟรี ) เพียงแค่แสดงให้เห็นว่าโปรแกรมเมอร์ที่ได้รับการฝึกฝนให้ใช้รูปแบบขีดล่างจะเร็วกว่าในการอ่านรูปแบบขีดล่างกว่าตัวอูฐ ข้อมูลยังแสดงให้เห็นว่าความแตกต่างนั้นมีขนาดเล็กลงสำหรับวิชาที่มีประสบการณ์มากกว่า (และวิชาส่วนใหญ่ที่เป็นนักเรียนแนะนำว่าแม้กระทั่งวิชาที่ไม่น่าจะมีประสบการณ์โดยเฉพาะ) นี่ไม่ได้หมายความว่าโปรแกรมเมอร์ที่ใช้เวลามากขึ้นในการใช้กรณีของอูฐก็จะให้ผลเช่นเดียวกัน
Jules

8

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

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

actual.Should().Be(expected);

หรือคล้ายกัน ซึ่งเป็นแน่นอนมากชัดเจนกว่าแต่ยังดีกว่าassertEquals assertExpectedEqualsActualและมันก็ยังแต่งได้มากขึ้น


1
ฉันเป็นคนทวารหนักและฉันทำตามคำแนะนำที่แนะนำ แต่ฉันคิดว่าถ้าฉันคาดหวังผลลัพธ์fun(x)ที่ได้เป็น 5 แล้วจะเกิดอะไรขึ้นถ้าย้อนกลับคำสั่งซื้อ - assert(fun(x), 5)? มันกัดคุณยังไง?
emory

3
@ หน่วยความจำฉันรู้ว่า jUnit (อย่างน้อย) สร้างข้อความแสดงข้อผิดพลาด thourough จากค่าของ expectedและactualดังนั้นการย้อนกลับอาจทำให้ข้อความไม่ถูกต้อง แต่ฉันยอมรับว่ามันฟังดูเป็นธรรมชาติมากขึ้น :)
joH1

@ joH1 ดูเหมือนว่าอ่อนแอสำหรับฉัน รหัสความล้มเหลวที่จะล้มเหลวและรหัสผ่านจะผ่านไม่ว่าคุณจะทำหรือassert(expected, observed) assert(observed, expected)ตัวอย่างที่ดีกว่าน่าจะเป็นlocateLatitudeLongitude- ถ้าคุณย้อนกลับพิกัดมันจะเลอะอย่างจริงจัง
emory

1
@ ผู้คนที่ไม่สนใจข้อความแสดงข้อผิดพลาดที่สมเหตุสมผลในการทดสอบหน่วยเป็นสาเหตุที่ฉันต้องจัดการกับ "Assert.IsTrue ล้มเหลว" ในฐานรหัสเก่าบางแห่ง ซึ่งสนุกมากในการตรวจแก้จุดบกพร่อง แต่ใช่ในกรณีนี้ปัญหาอาจไม่จำเป็นอย่างนั้น (ยกเว้นถ้าเราทำการเปรียบเทียบที่คลุมเครือ การยืนยันอย่างคล่องแคล่วนั้นเป็นวิธีที่ดีในการหลีกเลี่ยงปัญหานี้และทำให้โค้ดมีความชัดเจนมากขึ้น
Voo

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

5

คุณกำลังพยายามหลีกทางของคุณระหว่าง Scylla และ Charybdis เพื่อความชัดเจนพยายามหลีกเลี่ยงการใช้คำฟุ่มเฟื่อยที่ไร้ประโยชน์

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

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

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

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

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

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


ลำดับอาจมีความสำคัญกับ jUnit ซึ่งสร้างข้อความแสดงข้อผิดพลาดเฉพาะจากค่าของexpectedและactual(อย่างน้อยกับ Strings)
joH1

ฉันคิดว่าฉันปกคลุมส่วนหนึ่งที่ ...
Deduplicator

คุณพูดถึง แต่ให้พิจารณา: assertEquals("foo", "doo")ให้ข้อความแสดงข้อผิดพลาดคือComparisonFailure: expected:<[f]oo> but was:<[d]oo>... การสลับค่าจะทำให้ความหมายของข้อความเปลี่ยนไปซึ่งฟังดูสมมาตรต่อต้านฉันมากกว่า อย่างไรก็ตามอย่างที่คุณพูดว่า dev มีตัวบ่งชี้อื่น ๆ เพื่อแก้ไขข้อผิดพลาด แต่มันอาจทำให้เข้าใจผิด IMHO และใช้เวลาในการดีบักอีกเล็กน้อย
joH1

แนวคิดที่ว่ามี "การประชุม" สำหรับคำสั่งการโต้แย้งเป็นเรื่องตลกโดยพิจารณาว่าทั้งค่าย (dest, src กับ src, ปลายทาง) ได้โต้เถียงกันเกี่ยวกับเรื่องนี้อย่างน้อยตราบใดที่ AT&T กับ Intel มีไวยากรณ์อยู่ และข้อความแสดงข้อผิดพลาดที่ไม่ช่วยเหลือในการทดสอบหน่วยเป็นโรคระบาดที่ควรกำจัดให้หมดไป มันเกือบจะไม่ดีเท่ากับ "Assert.IsTrue ล้มเหลว" ("เฮ้คุณต้องทำการทดสอบหน่วยเพื่อแก้จุดบกพร่องดังนั้นให้เรียกใช้อีกครั้งและใส่เบรกพอยต์ที่นั่น", "เฮ้คุณต้องดูรหัสอย่างใดดังนั้น เพียงตรวจสอบว่าคำสั่งซื้อนั้นถูกต้อง ")
Voo

@Voo: ประเด็นคือ "ความเสียหาย" สำหรับการทำผิดคือ miniscule (ตรรกะไม่ได้ขึ้นอยู่กับมันและยูทิลิตี้ข้อความจะไม่ลดลงไปในระดับที่สำคัญใด ๆ ) และเมื่อเขียน IDE จะแสดงชื่อพารามิเตอร์ และพิมพ์ต่อไป
Deduplicator

3

บ่อยครั้งที่มันไม่ได้เพิ่มความคมชัดตรรกะใด ๆ

เปรียบเทียบ "เพิ่ม" กับ "AddFirstArgumentToSecondArgument"

หากคุณต้องการโอเวอร์โหลดที่จะเพิ่มสามค่า อะไรจะทำให้รู้สึกมากกว่านี้

อีก "เพิ่ม" กับสามข้อโต้แย้ง?

หรือ

"AddFirstAndSecondAndThirdArgument"?

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


4
Addแสดงให้เห็นถึงการดำเนินการสลับ OP เกี่ยวข้องกับสถานการณ์ที่คำสั่งนั้นสำคัญ
Rosie F

ใน Swift คุณต้องการตัวอย่างการโทรเพิ่ม (5, ถึง: x) หรือเพิ่ม (5, บวก: 7, ถึง: x) หรือเพิ่ม (5, บวก: 7, ให้: x) ถ้าคุณกำหนดฟังก์ชันเพิ่ม () ตาม
gnasher729

โอเวอร์โหลดที่สามควรตั้งชื่อว่า "Sum"
StingyJack

@StringyJack อืมมมม. ไม่ใช่คำสั่งมันเป็นคำนามที่ทำให้ชื่อของเมธอดไม่เหมาะสม แต่ถ้าคุณรู้สึกอย่างนั้นและถ้าคุณต้องการที่จะเป็นคนเจ้าระเบียบเกี่ยวกับเรื่องนี้รุ่นอาร์กิวเมนต์ทั้งสองก็ควรจะมีชื่อว่า Sum หากคุณต้องมีวิธีการเพิ่มก็ควรมีหนึ่งอาร์กิวเมนต์ที่ถูกเพิ่มไปยังอินสแตนซ์ของวัตถุตัวเอง (ซึ่งจะต้องเป็นประเภทที่เป็นตัวเลขหรือเวกเตอร์) อาร์กิวเมนต์อย่างน้อย 2 รายการขึ้นไป (สิ่งที่คุณจะตั้งชื่อ) จะคงที่ จากนั้นเวอร์ชันอาร์กิวเมนต์ 3 ตัวขึ้นไปจะซ้ำซ้อนและเราจะใช้ตัวดำเนินการบวก: - |
Martin Maat

1
@ มาร์ตินรออะไร sumเป็นทำเลที่ดีเลิศ cromulent คำกริยา เป็นเรื่องธรรมดาโดยเฉพาะอย่างยิ่งในวลี "เพื่อสรุป"
Voo

2

ฉันต้องการเพิ่มสิ่งอื่นที่คำตอบอื่น ๆ บอกไว้ แต่ฉันไม่คิดว่ามีการพูดถึงอย่างชัดเจน:

@puck พูดว่า "ยังไม่มีการรับประกันอาร์กิวเมนต์แรกที่กล่าวถึงในชื่อฟังก์ชั่นเป็นพารามิเตอร์ตัวแรกจริงๆ"

@cbojar พูดว่า "ใช้ชนิดแทนอาร์กิวเมนต์ที่ไม่ชัดเจน"

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

เปรียบเทียบassertExpectedEqualsActual(foo, bar)กับทางเลือกอื่น (จากหน้านี้และที่อื่น ๆ ) เช่น:

# Putting the arguments in a labelled structure
assertEquals({expected: foo, actual: bar})

# Using a keyword arguments language feature
assertEquals(expected=foo, actual=bar)

# Giving the arguments different types, forcing us to wrap them
assertEquals(Expected(foo), Actual(bar))

# Breaking the symmetry and attaching the code to one of the arguments
bar.Should().Be(foo)

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

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

การเรียกร้องเช่นassertEqual(expected, actual)ทำให้ความรู้สึกมากที่สุดเท่าassertEqual(actual, expected)ดังนั้นจึงเป็นเรื่องง่ายที่เราจะได้รับพวกเขาผสมขึ้นและสำหรับเครื่องไถข้างหน้าและทำสิ่งที่ผิด หากเราใช้assertExpectedEqualsActualแทนอาจทำให้เรามีข้อผิดพลาดน้อยลง แต่จะไม่ให้ข้อมูลเพิ่มเติมกับเครื่อง (ไม่สามารถเข้าใจภาษาอังกฤษได้และการเลือกชื่อไม่ควรส่งผลต่อความหมาย)

สิ่งที่ทำให้ "มีโครงสร้าง" เป็นที่นิยมมากกว่าเช่นอาร์กิวเมนต์คำหลักเขตข้อมูลที่มีป้ายกำกับประเภทที่แตกต่าง ฯลฯ คือข้อมูลเพิ่มเติมยังสามารถอ่านได้ด้วยเครื่อง assertEqualกรณีที่ไม่เลวร้ายเกินไปเนื่องจากปัญหาที่เกิดขึ้นก็จะเป็นแค่ข้อความที่ไม่ถูกต้อง ตัวอย่างที่น่ากลัวกว่าอาจเป็นString replace(String old, String new, String content)เรื่องที่สับสนได้ง่ายString replace(String content, String old, String new)ซึ่งมีความหมายแตกต่างกันมาก วิธีการรักษาที่ง่ายคือการจับคู่[old, new]ซึ่งจะทำให้เกิดข้อผิดพลาดทำให้เกิดข้อผิดพลาดได้ทันที (แม้จะไม่มีประเภท)

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

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


1

เพราะมันลบความจำเป็นในการจำที่ขัดแย้งไป

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

หากคุณอ่านรหัสคุณจะเห็นได้อย่างง่ายดายว่าเกิดอะไรขึ้นเมื่อตั้งชื่อพารามิเตอร์ตามที่ควรจะเป็น copy(source, destination)เป็นเรื่องง่ายที่จะเข้าใจกว่า soemthing copyFromTheFirstLocationToTheSecondLocation(placeA, placeB)เช่น

ทำไมผู้ไม่ใช้โคเดอร์จึงนำอดีตมาใช้ถ้าหากผู้เขียนอ้างว่าชัดเจนกว่าหลัง

เนื่องจากมีมุมมองที่แตกต่างกันในสไตล์ที่แตกต่างกันและคุณสามารถค้นหาผู้เขียน x ของบทความอื่นที่ระบุตรงกันข้าม คุณจะเสียสติเมื่อพยายามติดตามทุกสิ่งที่มีคนเขียนบางที่ ;-)


0

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

copyFromSourceToDestination( // "...ahh yes, the source directory goes first"

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

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

copy(sourceDir, destinationDir); // "...makes sense"

ความอดทนนี้จะชนะโปรแกรมเมอร์ส่วนใหญ่และฉันพบว่ามันง่ายต่อการอ่าน

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


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

@Blrfl จุดที่เรียกมันcopyFromSourceToDestinationคือถ้าคุณคิดว่ามันcopyToDestinationFromSourceคอมไพเลอร์จะพบข้อบกพร่องของคุณ แต่ถ้ามันถูกเรียกcopyมันจะไม่ การคัดลอกชุดคำสั่งของรูทีนในทางที่ผิดนั้นเป็นเรื่องง่ายเนื่องจาก strcpy, strcat และอื่น ๆ เป็นแบบอย่าง และง่ายต่อการอ่านหรือไม่ mergeLists (listA, listB, listC) สร้าง listA จาก listB & listC หรืออ่าน listA & listB และเขียน listC หรือไม่
Rosie F

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

OTOH, destinationDir.copy (sourceDir); // "... เข้าท่ามากขึ้น"
Kristian H

1
@KristianH ทิศทางdir1.copy(dir2)ทำงานอย่างไร? ไม่มีความเห็น. เกี่ยวกับdir1.copyTo(dir2)อะไร
maaartinus
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.