แก้ไขข้อคิดเห็นเพื่อใส่อาร์กิวเมนต์ของฟังก์ชันบูลีนที่เป็น“ false” หรือไม่


19

จากโครงการโอเพนซอร์สบางโครงการฉันรวบรวมสไตล์การเข้ารหัสต่อไปนี้

void someFunction(bool forget);

void ourFunction() {
  someFunction(false /* forget */);
}    

ฉันมีข้อสงสัยเสมอเกี่ยวกับความfalseหมายของที่นี่ มันหมายถึง "ลืม" หรือ "ลืม" อ้างถึงพารามิเตอร์ที่เกี่ยวข้อง (เช่นในกรณีข้างต้น) และ "false" มีไว้เพื่อลบล้างหรือไม่

มีการใช้รูปแบบใดบ่อยที่สุดและวิธีใดดีที่สุด (หรือวิธีที่ดีกว่า) เพื่อหลีกเลี่ยงความคลุมเครือ


38
ใช้ enums (แม้ว่าจะมีเพียง 2 ตัวเลือก) แทนที่จะเป็นคน
โง่

21
บางภาษารองรับอาร์กิวเมนต์ที่มีชื่อ ในภาษาดังกล่าวคุณสามารถใช้someFunction(forget: true);
Brian

3
ฉันรู้สึกผูกพันที่จะต้องให้ข้อโต้แย้งของ Martin Fowler ในหัวข้ออาร์กิวเมนต์ของธง (เช่นพูดคุยเกี่ยวกับ setters บูลีน) โดยทั่วไปแล้วพยายามหลีกเลี่ยง
FGreg

3
การปฏิเสธความเห็นที่ชัดเจนสามารถแสดงความคิดเห็นได้ ดังนั้นจะดีกว่าเสมอที่จะทำให้รหัสของคุณตนเอง documenting เพราะบางคนจะเปลี่ยนtrueไปfalseและไม่ได้อัปเดตความคิดเห็น หากคุณไม่สามารถเปลี่ยน API ได้วิธีที่ดีที่สุดในการแสดงความคิดเห็นคือsomeFunction( false /* true=forget, false=remember */)
Mark Lakata

1
@Bakuriu - จริง ๆ แล้วฉันอาจยังคงใช้ API สาธารณะได้สองวิธีแยกกัน ( sortAscendingและsortDescendingหรือคล้ายกัน) ตอนนี้ข้างในพวกเขาทั้งคู่อาจเรียกวิธีการส่วนตัวเดียวกันซึ่งอาจมีพารามิเตอร์ประเภทนี้ ที่จริงถ้าภาษาที่สนับสนุนมันอาจจะเป็นสิ่งที่ฉันต้องการผ่านในจะเป็นฟังก์ชั่นแลมบ์ดาที่มีทิศทางการเรียงลำดับ ...
ลาน-Muse

คำตอบ:


33

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

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

เพื่อหลีกเลี่ยงอาร์กิวเมนต์แฟล็กแบ่งฟังก์ชันออกเป็นสองฟังก์ชันที่อธิบายความแตกต่างในชื่อฟังก์ชัน

อาร์กิวเมนต์ตั้งค่าสถานะ

serveIceCream(bool lowFat)

ไม่มีอาร์กิวเมนต์ธง

serveTraditionalIceCream()
serveLowFatIceCream()

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


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


4
+17, เปิดหมวก ร่างกายserveTraditionalIceCreamและserveLowFatIceCreamหน้าตาเป็นอย่างไร ฉันมีส่วนผสมของไอศกรีม 14 ชนิด
JohnMark13

13
สำหรับวิธีสาธารณะการประชุมนี้ดี แต่เนื่องจาก JohnMark alludes อีกครึ่งหนึ่งของ SRP คือ "วิธีที่ดีควรเป็นวิธีเดียวที่ทำในสิ่งที่มันทำ" ฉันเห็นว่ามี N + 1 สายพันธุ์ของวิธีนี้ N พับลิกโดยไม่มีพารามิเตอร์ซึ่งทั้งหมดเรียกเมธอดไพรเวตหนึ่งที่มีพารามิเตอร์ที่คุณพยายามหลีกเลี่ยงการเปิดเผย ในบางจุดคุณเพียงแค่ยอมแพ้และเปิดเผยพารามิเตอร์ damn กลิ่นรหัสไม่จำเป็นต้องเป็นรหัสหมายถึงควรจะ refactored; พวกเขาเป็นเพียงสิ่งที่สมควรได้รับการตรวจสอบโค้ดครั้งที่สอง
KeithS

@Aaron โดย "คุณสมบัติอิจฉา" สิ่งที่คุณหมายถึงอะไร
Geek

27

ในคำพูดของคนธรรมดา:

  • false เป็นตัวอักษร
  • คุณกำลังผ่านตัวอักษร false
  • คุณกำลังบอกที่someFunctionจะไม่ลืม
  • คุณกำลังบอกsomeFunctionว่าพารามิเตอร์ที่ลืมคือfalse
  • คุณกำลังบอกsomeFunctionให้จำ

ในความคิดของฉันมันจะดีกว่าถ้าฟังก์ชั่นเป็นเช่นนี้:

void someFunction(bool remember);

คุณสามารถเรียกมันว่า

void ourFunction() {
  someFunction(true);
} 

หรือเก็บชื่อเดิมไว้ แต่เปลี่ยนฟังก์ชั่น wrapper ของคุณเป็น

void ourFunctionWithRemember() {
  someFunction(false);
} 

แก้ไข:

ตามที่ @Vorac ระบุให้พยายามใช้คำพูดในเชิงบวกเสมอ การปฏิเสธสองครั้งทำให้เกิดความสับสน


15
+1 สำหรับแนวคิดที่จะพยายามใช้คำในเชิงบวกเสมอ การปฏิเสธสองครั้งทำให้เกิดความสับสน
Vorac

1
ตกลงความคิดที่ดี การบอกให้ระบบไม่ทำสิ่งที่สับสน หากยอมรับพารามิเตอร์บูลีนให้แสดงค่าบวก
Brandon

ในกรณีส่วนใหญ่ฉันคิดว่าคุณต้องการมีความเฉพาะเจาะจงมากกว่าrememberนอกจากชื่อฟังก์ชันที่ทำให้ความหมายชัดเจน remember มาก rememberToCleanUp* or *persistหรือบางสิ่งบางอย่าง.
itsbruce

14

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

การใช้ชื่อตัวแปรเชิงบวก (แนะนำในCode Complete ) อาจเป็นการเปลี่ยนแปลงที่ง่ายที่สุดที่จะทำให้ตัวอย่างข้อมูลนี้อ่านง่ายขึ้นเช่น

void someFunction(boolean remember);

จากนั้นourFunctionกลายเป็น:

void ourFunction() {
    someFunction(true /* remember */);
}

อย่างไรก็ตามการใช้ enum ทำให้การเรียกใช้ฟังก์ชันเข้าใจง่ายยิ่งขึ้นโดยมีค่าใช้จ่ายของรหัสสนับสนุนบางตัว:

public enum RememberFoo {
    REMEMBER,
    FORGET
}

...

void someFunction(RememberFoo remember);

...

void ourFunction() {
    someFunction(RememberFoo.REMEMBER);
}

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

void someFunction(boolean remember);

...

void ourFunction() {
    boolean remember = false;
    someFunction(remember);
}

1
การตั้งค่าrememberเป็นวิธีที่แท้จริงที่จะลืม (ในตัวอย่างของคุณsomeFunction(true /* forget */);)?
Brian

2
นี่enumคือทางออกที่ดีที่สุด เพียงเพราะประเภทสามารถแสดงเป็นbool- ฉันพวกเขา isomorphic - มันไม่ได้หมายความว่ามันควรจะเป็นเช่นนี้ อาร์กิวเมนต์เช่นเดียวกับและแม้กระทั่งstring int
Jon Purdy

10

เปลี่ยนชื่อตัวแปรเพื่อให้ค่าบูลสมเหตุสมผล

นี่เป็นล้านเท่าดีกว่าการเพิ่มความคิดเห็นเพื่ออธิบายการขัดแย้งกับฟังก์ชั่นเพราะชื่อไม่ชัดเจน


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

@Brandon นั่นไม่ใช่ข้อโต้แย้งในการเรียกบูลีน doNotPersist (หรือดีกว่า Persist) การเรียกมันว่า "ลืม" โดยไม่บอกว่าจะลืมไม่ได้ช่วยอะไรตรงไปตรงมา โอ้และวิธีการที่ใช้บูลีนหลายตัวเป็นตัวเลือกที่ติดอยู่บนสวรรค์
itsbruce

5

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

void ourFunction() {
    bool takeAction = false;  /* false means to forget */
    someFunction( takeAction );
}    

หากคุณไม่สามารถเปลี่ยนชื่อตัวแปรได้ความคิดเห็นควรมีความหมายมากกว่านี้เล็กน้อย:

void ourFunction() {
    /* false means that the method should forget what was requested */
    someFunction( false );
}    

1
แน่นอนคำแนะนำที่ดี แต่ผมไม่คิดว่ามันอยู่ในประเด็นที่ว่าความคิดเห็นที่มีไปยังที่อยู่ซึ่งเป็นว่าไม่มีการประกาศฟังก์ชั่นที่เหมาะสมในหน้าของคุณก็อาจเป็นเรื่องยากที่จะจำสิ่งที่จะถูกกำหนดให้/* forget */ false(ซึ่งเป็นเหตุผลที่ฉันคิดว่าคำแนะนำของ @ Esailija เพื่อเพิ่ม enum นั้นดีกว่าและทำไมฉันถึงรักภาษาที่อนุญาตให้ใช้พารามิเตอร์ที่มีชื่อ)
Gort the Robot

@StevenBurnap - ขอบคุณ! คุณพูดถูกแล้วคำตอบเดิมของฉันยังไม่ชัดเจนในการตอบคำถามของ OP ฉันได้แก้ไขเพื่อให้ชัดเจนยิ่งขึ้น

3

มีบทความที่ดีที่กล่าวถึงสถานการณ์ที่แน่นอนนี้เนื่องจากอ้างถึง Qt-Style API ตรงนี้มันเรียกว่าThe Boolean Parameter Trapและมันก็คุ้มค่าที่จะอ่าน

ส่วนสำคัญของมันคือ:

  1. การใช้ฟังก์ชั่นมากเกินไปจนไม่ต้องการบูลดีกว่า
  2. การใช้ enum (ตามที่ Esailija แนะนำ) ดีที่สุด

2

นี่คือความคิดเห็นที่แปลกประหลาด

จากมุมมองของคอมไพเลอร์someFunction(false /* forget */);เป็นจริงsomeFunction(false);(ความคิดเห็นถูกปล้น) ดังนั้นทุกสิ่งที่สายจะเป็นสายsomeFunctionแรก (และ) falseอาร์กิวเมนต์ชุด

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


1

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

หลังจากนั้นฉันจะลบความคิดเห็น ท้ายที่สุดแล้ว IDEs ที่ทันสมัย ​​(เช่น eclipse) จะปรากฏกล่องพร้อมรหัสเมื่อคุณวางเมาส์ไว้เหนือฟังก์ชัน การดูรหัสควรล้างความคลุมเครือ


1 การคอมเม้นท์รหัสที่ซับซ้อนบางอย่างก็โอเค


btw ใครพูดแบบนี้: "ปัญหาที่เลวร้ายที่สุดของโปรแกรมเมอร์คือวิธีตั้งชื่อตัวแปรและชดเชยโดยหนึ่ง"?
BЈовић

4
คุณกำลังมองหา martinfowler.com/bliki/TwoHardThings.htmlเป็นแหล่งที่มาของมัน ฉันเคยได้ยิน tweaked เพื่อ "มีเพียงสองสิ่งที่ยากในวิทยาศาสตร์คอมพิวเตอร์: การทำให้ใช้แคชไม่ได้การตั้งชื่อสิ่งและปิดโดยข้อผิดพลาดเดียว"

1

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

หากคุณไม่สามารถเปลี่ยน API ได้ฉันเลือกใช้ตัวเลือก 2 ตัว

  • เปลี่ยนความคิดเห็นเพื่อให้เป็นจริงเสมอโดยไม่คำนึงถึงรหัส ถ้าคุณเรียกสิ่งนี้เพียงครั้งเดียวนี่เป็นวิธีแก้ปัญหาที่ดี
     someFunction (false / * true = forget, false = Remember * /); `
  • ใช้ #defines โดยเฉพาะถ้าคุณโทรหามากกว่าหนึ่งครั้ง
     #define FORGET จริง
     #demine REMEMBER false
     someFunction (จำ);

1

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

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


-1

ใน C # ฉันใช้พารามิเตอร์ที่มีชื่อเพื่อให้ชัดเจนขึ้น

someFunction(forget: false);

หรือenum:

enum Memory { Remember, Forget };

someFunction(Memory.Forget);

หรือเกินพิกัด:

someFunctionForget();

หรือความแตกต่าง `:

var foo = new Elephantine();

foo.someFunction();

-2

การตั้งชื่อควรแก้ไขความกำกวมสำหรับบูลีนเสมอ ฉันมักจะตั้งชื่อบูลีนบางอย่างเช่น 'isThis' หรือ 'shouldDoThat' เช่น:

void printTree(Tree tree, bool shouldPrintPretty){ ... }

และอื่น ๆ แต่เมื่อคุณอ้างถึงรหัสของคนอื่นมันเป็นการดีที่สุดที่จะแสดงความคิดเห็นเมื่อผ่านค่า


คำถามนี้ถามคำถามนี้อย่างไร
ริ้น

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