บูลีนเป็นอาร์กิวเมนต์ของวิธีการที่ยอมรับไม่ได้หรือไม่? [ปิด]


123

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

เข้าใจอะไรง่ายกว่ากัน?

file.writeData( data, true );

หรือ

enum WriteMode {
  Append,
  Overwrite
};

file.writeData( data, Append );

ตอนนี้ฉันเข้าใจแล้ว! ;-)
นี่เป็นตัวอย่างที่การแจงนับเป็นพารามิเตอร์ที่สองทำให้โค้ดอ่านง่ายขึ้นมาก

แล้วคุณมีความคิดเห็นอย่างไรกับหัวข้อนี้


7
นี่เป็นการอ่านที่น่าสนใจมากฉันจะใช้วิธีนี้บ่อยขึ้น
Sara Chipps

เอิ่มเคยทำมาก่อน แต่ไม่เคยรู้เลยว่ารูปแบบการออกแบบนี้ดีแค่ไหน ดังนั้น enum ไปในไฟล์?
Shawn

Enums มีความหมายมากขึ้นจาก pov เชิงความหมาย ในบันทึกอื่นมันน่าสนใจที่จะเห็นว่าโปรแกรมเมอร์บางคนคิดอะไรขึ้นมาเพื่อจัดการตรรกะที่คลุมเครือ
James P.

2
เพียงแค่ถามมะนาวกับ Adventure Time หากสิ่งนี้ยอมรับไม่ได้
ajax333221

คำตอบ:


131

บูลีนแสดงถึงตัวเลือก "ใช่ / ไม่ใช่" หากคุณต้องการแทนค่า "ใช่ / ไม่ใช่" ให้ใช้บูลีนควรอธิบายด้วยตนเอง

แต่ถ้าเป็นการเลือกระหว่างสองตัวเลือกซึ่งทั้งสองตัวเลือกไม่ชัดเจนว่าใช่หรือไม่ใช่บางครั้ง enum ก็สามารถอ่านได้มากกว่า


3
นอกจากนี้ชื่อเมธอดจะต้องชัดเจนเกี่ยวกับสิ่งที่อาร์กิวเมนต์ใช่หรือไม่ใช่คือการตั้งค่าที่ชัดเจนเป็นโมฆะ turnLightOn (บูล) จริงหรือใช่จะปรับให้ไฟสว่างขึ้น
ไซมอน

10
แม้ว่าในกรณีนี้ฉันอาจจะมี turnLightOn () และ turnLightOff () ขึ้นอยู่กับสถานการณ์
skaffman

14
"turnLightOn (false)" หมายความว่า "ไม่เปิดไฟ"? Confusiong
เจย์บาซูซี่

17
เกี่ยวกับsetLightOn(bool).
ฟินบาร์

10
ความคิดเห็นที่ล่าช้า แต่ @Jay Bazuzi: หากวิธีการของคุณเรียกว่า turnLightOn และคุณส่งผ่านเท็จคุณอาจไม่เรียกเมธอดนี้เลยโดยการส่งผ่านเท็จบอกว่าอย่าเปิดไฟ ถ้าไฟสว่างอยู่แล้วไม่ได้หมายความว่าปิดหมายความว่าอย่าเปิด ... หากคุณมีวิธี 'turnLight' an enum ด้วย 'On' และ 'Off' ให้เหมาะสม turnLight ( เปิด), ไฟเลี้ยว (ปิด) ฉันเห็นด้วยกับ skaffman tho ฉันอยากจะใช้วิธีการที่ชัดเจนสองวิธีที่แตกต่างกัน turnLightOn () และ turnLightOff () (BTW: สิ่งนี้มีอธิบายไว้ในหนังสือ Uncle Bobs "Clean Code")
Phill


32

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

lock.setIsLocked(True);

หรือ

enum LockState { Locked, Unlocked };
lock.setLockState(Locked);

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


2
ในตัวอย่างของคุณฉันอยากมีสองวิธี lock.lock () lock.release () และ lock.IsSet แต่ทั้งหมดขึ้นอยู่กับสิ่งที่เหมาะสมที่สุดกับรหัสที่ใช้
Robert Paulson

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

ฉันเห็นด้วยอย่างยิ่ง :) ฉันแค่แสดงความคิดเห็นเกี่ยวกับการเสนอรหัสเทียมที่เฉพาะเจาะจง ฉันเห็นด้วยกับคำตอบของคุณ
Robert Paulson

14

สำหรับฉันแล้วการใช้บูลีนหรือการแจงนับไม่ใช่แนวทางที่ดี Robert C.Martin อธิบายสิ่งนี้อย่างชัดเจนในClean Code Tip # 12: ขจัดข้อโต้แย้งแบบบูลีน :

อาร์กิวเมนต์บูลีนดังประกาศว่าฟังก์ชันทำมากกว่าหนึ่งสิ่ง พวกเขาสับสนและควรถูกกำจัด

หากเมธอดทำมากกว่าหนึ่งอย่างคุณควรเขียนสองวิธีที่แตกต่างกันเช่นในกรณีของคุณ: file.append(data)และfile.overwrite(data) .

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


7
นั่นไม่ได้หมายความว่าฟังก์ชันที่ยอมรับสตริงความยาว ASCII N ทำสิ่งต่างๆ 128 ^ N ได้หรือไม่?
ย้อนกลับ

@delty นี่คือความคิดเห็นที่รุนแรง? ถ้าใช่คุณเขียนโค้ด if กับค่าที่เป็นไปได้ทั้งหมดของ String บ่อยๆหรือไม่? มีการเปรียบเทียบที่เป็นไปได้กับกรณีของอาร์กิวเมนต์บูลีนหรือไม่?
Pascal Thivent

ฉันเชื่อว่ามันยอมรับได้เมื่อคุณตั้งค่าบูลีนภายในออบเจ็กต์ setVisible(boolean visible) { mVisible = visible; }ตัวอย่างที่สมบูรณ์แบบที่จะเป็น คุณจะแนะนำทางเลือกใดได้บ้าง
Brad

2
@Brad show () {mVisible = true} hide () {mVisible = false}
Oswaldo Acauan

@Oswaldo ในขณะที่ยังคงถูกต้องฉันไม่คิดว่ามันสมเหตุสมผลที่จะมีสองวิธีที่แตกต่างกันในการกำหนดบูลีนให้กับค่าที่แตกต่างกัน คุณไม่มี setIntToOne (), setIntToTwo () setIntToThree () ใช่ไหม มีความคลุมเครือเล็กน้อยเมื่อคุณสามารถมีค่าที่เป็นไปได้เพียงสองค่า แต่เพื่อความสะอาดให้ใช้บูลีนในกรณีนั้น
Brad

13

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


13

จำคำถามที่ Adlai Stevenson ถามถึงเอกอัครราชทูต Zorin ที่ UN ในช่วงวิกฤตขีปนาวุธคิวบาได้หรือไม่?

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

หากแฟล็กที่คุณมีในวิธีการของคุณมีลักษณะที่คุณสามารถตรึงมันลงในการตัดสินใจแบบไบนารีได้และการตัดสินใจนั้นจะไม่กลายเป็นการตัดสินใจแบบสามทางหรือแบบ n-way ให้ไปหาบูลีน ข้อบ่งชี้: แฟล็กของคุณเรียกว่าisXXX isXXX

อย่าทำให้มัน boolean ในกรณีของบางสิ่งบางอย่างที่เป็นสวิตช์โหมด มีโหมดมากกว่าที่คุณคิดไว้เสมอเมื่อเขียนวิธีการตั้งแต่แรก

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


13

มีสองเหตุผลที่ฉันพบว่าสิ่งนี้เป็นสิ่งที่ไม่ดี:

  1. เพราะบางคนจะเขียนวิธีการเช่น:

    ProcessBatch(true, false, false, true, false, false, true);
    

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

  2. เนื่องจากการควบคุมการไหลของโปรแกรมด้วยสาขาใช่ / ไม่ใช่แบบง่ายอาจหมายความว่าคุณมีฟังก์ชันที่แตกต่างกันอย่างสิ้นเชิงสองฟังก์ชันที่รวมเข้าด้วยกันในลักษณะเดียวกัน ตัวอย่างเช่น:

    public void Write(bool toOptical);
    

    จริงๆนี่ควรเป็นสองวิธี

    public void WriteOptical();
    public void WriteMagnetic();
    

    เนื่องจากรหัสในสิ่งเหล่านี้อาจแตกต่างกันอย่างสิ้นเชิง พวกเขาอาจต้องจัดการและตรวจสอบข้อผิดพลาดที่แตกต่างกันทุกประเภทหรืออาจจะต้องจัดรูปแบบข้อมูลขาออกให้แตกต่างกัน คุณไม่สามารถบอกได้ว่าเพียงแค่ใช้Write()หรือWrite(Enum.Optical)แม้ว่าคุณจะมีวิธีใดวิธีหนึ่งเพียงแค่เรียกวิธีการภายใน WriteOptical / Mag ถ้าคุณต้องการ)

ฉันเดาว่ามันขึ้นอยู่กับ ฉันจะไม่ทำข้อตกลงเรื่องนี้มากเกินไปยกเว้น # 1


คะแนนดีมาก! พารามิเตอร์บูลีนสองตัวในวิธีเดียวดูแย่มากแน่นอน (เว้นแต่คุณจะโชคดีที่ได้ตั้งชื่อพารามิเตอร์)
Yarik

คำตอบนี้อาจได้รับประโยชน์จากการจัดรูปแบบใหม่! ;-)
Yarik

7

Enums ดีกว่า แต่ฉันจะไม่เรียกพารามิเตอร์บูลีนว่า "ยอมรับไม่ได้" บางครั้งมันก็ง่ายกว่าที่จะโยนบูลีนเล็ก ๆ ตัวหนึ่งเข้าไปแล้วเดินหน้าต่อไป (คิดวิธีส่วนตัวเป็นต้น)


เพียงแค่สร้างวิธีการอธิบายให้ชัดเจนเพื่อให้ชัดเจนว่าจริงหรือใช่หมายถึงอะไร
ไซม่อน

6

บูลีนอาจใช้ได้ในภาษาที่ตั้งชื่อพารามิเตอร์เช่น Python และ Objective-C เนื่องจากชื่อสามารถอธิบายสิ่งที่พารามิเตอร์ทำ:

file.writeData(data, overwrite=true)

หรือ:

[file writeData:data overwrite:YES]

1
IMHO, writeData () เป็นตัวอย่างที่ไม่ดีของการใช้พารามิเตอร์บูลีนไม่ว่าพารามิเตอร์ที่ตั้งชื่อจะได้รับการสนับสนุนหรือไม่ก็ตาม ไม่ว่าคุณจะตั้งชื่อพารามิเตอร์อย่างไรความหมายของ False value ก็ไม่ชัดเจน!
Yarik

4

ฉันจะไม่ยอมรับว่ามันเป็นกฎที่ดีกฎเห็นได้ชัดว่า Enum สร้างรหัสที่ชัดเจนหรือละเอียดกว่าในบางกรณี แต่ตามกฎแล้วดูเหมือนว่าจะเข้าถึงได้

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

dim append as boolean = true
file.writeData( data, append );

หรือฉันชอบทั่วไปมากกว่า

dim shouldAppend as boolean = true
file.writeData( data, shouldAppend );

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


1
แม้ว่าฉันยอมรับว่าพารามิเตอร์บูลีนเป็นที่ยอมรับในหลาย ๆ กรณีในกรณีของตัวอย่าง writeData () นี้พารามิเตอร์บูลีนเช่น shouldAppend ไม่เหมาะสมอย่างยิ่ง เหตุผลง่ายๆคือยังไม่ชัดเจนในทันทีว่าความหมายของ False คืออะไร
Yarik

4

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

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

คุณสมบัติ (โดยเฉพาะอย่างยิ่งกับตัวเริ่มต้นอ็อบเจ็กต์ C # 3) หรืออาร์กิวเมนต์คำหลัก (a la ruby ​​หรือ python) เป็นวิธีที่ดีกว่ามากในการไปที่ที่คุณใช้อาร์กิวเมนต์บูลีน

ตัวอย่าง C #:

var worker = new BackgroundWorker { WorkerReportsProgress = true };

ตัวอย่าง Ruby

validates_presence_of :name, :allow_nil => true

ตัวอย่าง Python

connect_to_database( persistent=true )

สิ่งเดียวที่ฉันคิดได้ว่าอาร์กิวเมนต์เมธอดบูลีนเป็นสิ่งที่ถูกต้องที่ควรทำคือใน java ซึ่งคุณไม่มีคุณสมบัติหรืออาร์กิวเมนต์คีย์เวิร์ด นี่เป็นหนึ่งในเหตุผลที่ฉันเกลียด java :-(


4

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

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

ดูที่. Net framework บูลีนถูกใช้เป็นพารามิเตอร์ในหลาย ๆ วิธี NET API ไม่สมบูรณ์แบบ แต่ฉันไม่คิดว่าการใช้บูลีนเป็นพารามิเตอร์จะเป็นปัญหาใหญ่ คำแนะนำเครื่องมือจะให้ชื่อของพารามิเตอร์แก่คุณเสมอและคุณสามารถสร้างคำแนะนำประเภทนี้ได้เช่นกัน - กรอกความคิดเห็น XML ของคุณเกี่ยวกับพารามิเตอร์วิธีการซึ่งจะปรากฏในคำแนะนำเครื่องมือ

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

ตัวอย่างเช่นถ้าคลาสของคุณมีคุณสมบัติเช่น

public bool IsFoo
public bool IsBar

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

enum FooBarType { IsFoo, IsBar, IsNeither };

4

กฎบางประการที่เพื่อนร่วมงานของคุณอาจยึดถือปฏิบัติได้ดีกว่า ได้แก่ :

  • อย่าดันทุรังกับการออกแบบของคุณ
  • เลือกสิ่งที่เหมาะกับผู้ใช้รหัสของคุณมากที่สุด
  • อย่าพยายามทุบหมุดรูปดาวลงในทุกหลุมเพียงเพราะคุณชอบรูปร่างในเดือนนี้!

3

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

ข้อดีอีกอย่างของ Enum คืออ่านง่ายกว่า


2

หากวิธีการถามคำถามเช่น:

KeepWritingData (DataAvailable());

ที่ไหน

bool DataAvailable()
{
    return true; //data is ALWAYS available!
}

void KeepWritingData (bool keepGoing)
{
   if (keepGoing)
   {
       ...
   }
}

อาร์กิวเมนต์วิธีบูลีนดูเหมือนจะมีความหมายที่สมบูรณ์แบบ


วันหนึ่งคุณจะต้องเพิ่ม "เขียนต่อไปถ้าคุณมีพื้นที่ว่าง" จากนั้นคุณจะเปลี่ยนจากบูลเป็นอีนัม
Ilya Ryzhenkov

จากนั้นคุณจะมีการเปลี่ยนแปลงอย่างสิ้นเชิงหรือล้าสมัยเกินพิกัดหรืออาจเป็นเช่น KeppWritingDataEx :)
Ilya Ryzhenkov

1
@ อิลยาไม่งั้นอาจจะไม่! การสร้างสถานการณ์ที่เป็นไปได้โดยที่ไม่มีอยู่ในขณะนี้จะไม่เป็นการลบล้างโซลูชัน
Jesse C. Slicer

1
ใช่ของเจสซี่ การวางแผนสำหรับการเปลี่ยนแปลงแบบนั้นเป็นเรื่องไร้สาระ ทำในสิ่งที่สมเหตุสมผล ในกรณีนี้บูลีนสามารถใช้งานง่ายและชัดเจน c2.com/xp/DoTheSimplestThingThatCouldPossablyWork.html
Derek Park

@Derek ในกรณีนี้ไม่จำเป็นต้องใช้บูลีนด้วยซ้ำเพราะ DataAvailable ส่งคืนจริงเสมอ :)
Ilya Ryzhenkov

2

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

CommentService.SetApprovalStatus(commentId, false);

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


2

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


1
แล้วการจ่ายล่วงหน้าเป็นตัวเลือกที่สามที่เป็นไปได้ล่ะ? ;-))
Yarik

2

Enums สามารถทำให้โค้ดอ่านง่ายขึ้น ยังมีบางสิ่งที่ต้องระวัง (ใน. net เป็นอย่างน้อย)

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

หาก enum ของคุณเป็นรหัสส่วนตัวของคุณ (ไม่เคยเปิดเผยต่อสาธารณะ) คุณสามารถหยุดอ่านได้ที่นี่

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

ฉันสามารถเขียนได้อย่างถูกกฎหมาย

WriteMode illegalButWorks = (WriteMode)1000000;
file.Write( data, illegalButWorks );

เพื่อต่อสู้กับสิ่งนี้รหัสใด ๆ ที่ใช้ enum ที่คุณไม่สามารถมั่นใจได้ (เช่น Public API) จำเป็นต้องตรวจสอบว่า enum ถูกต้องหรือไม่ คุณทำได้ผ่านทาง

if (!Enum.IsDefined(typeof(WriteMode), userValue))
    throw new ArgumentException("userValue");

ข้อแม้เดียวEnum.IsDefinedคือใช้การสะท้อนและช้ากว่า นอกจากนี้ยังประสบปัญหาการกำหนดเวอร์ชัน หากคุณต้องการตรวจสอบค่า enum บ่อยๆคุณควรทำสิ่งต่อไปนี้:

public static bool CheckWriteModeEnumValue(WriteMode writeMode)
{
  switch( writeMode )
  {
    case WriteMode.Append:
    case WriteMode.OverWrite:
      break;
    default:
      Debug.Assert(false, "The WriteMode '" + writeMode + "' is not valid.");
      return false;
  }
  return true;
}

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

มีความสนุกสนานมากยิ่งขึ้นที่คุณสามารถทำได้กับ[Flags]enums และรหัสการตรวจสอบความถูกต้องนั้นแตกต่างกันเล็กน้อย

ฉันจะสังเกตด้วยว่าสำหรับการพกพาคุณควรใช้ call ToString()on the enum และใช้Enum.Parse()เมื่ออ่านกลับเข้ามาทั้งสองอย่างToString()และEnum.Parse()สามารถจัดการ[Flags] enum ได้เช่นกันดังนั้นจึงไม่มีเหตุผลที่จะไม่ใช้มัน โปรดทราบว่านี่เป็นข้อผิดพลาดอีกประการหนึ่งเพราะตอนนี้คุณไม่สามารถเปลี่ยนชื่อ enum ได้โดยไม่ต้องทำลายรหัส

ดังนั้นบางครั้งคุณต้องชั่งน้ำหนักทั้งหมดข้างต้นเมื่อคุณถามตัวเองว่าฉันจะหนีไปได้ไหม?


1

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


0

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

ที่กล่าวว่าคุณยังสามารถใช้ False และ True (บูลีน 0 และ 1) จากนั้นหากคุณต้องการค่าเพิ่มเติมในภายหลังให้ขยายฟังก์ชันออกเพื่อรองรับค่าที่ผู้ใช้กำหนดเอง (เช่น 2 และ 3) และค่า 0/1 เก่าของคุณ จะพอร์ตไปอย่างสวยงามดังนั้นโค้ดของคุณไม่ควรพัง


0

บางครั้งการจำลองพฤติกรรมที่แตกต่างกันด้วยการโอเวอร์โหลดก็ง่ายกว่า เพื่อดำเนินการต่อจากตัวอย่างของคุณจะเป็น:

file.appendData( data );  
file.overwriteData( data );

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

ในบางกรณี Enums สามารถทำให้โค้ดอ่านง่ายขึ้นแม้ว่าการตรวจสอบค่า enum ที่แน่นอนในบางภาษา (เช่น C #) อาจเป็นเรื่องยาก

บ่อยครั้งที่พารามิเตอร์บูลีนถูกต่อท้ายรายการพารามิเตอร์เป็นโอเวอร์โหลดใหม่ ตัวอย่างหนึ่งใน. NET คือ:

Enum.Parse(str);  
Enum.Parse(str, true); // ignore case

การโอเวอร์โหลดหลังมีให้ใช้งานในเฟรมเวิร์ก. NET เวอร์ชันใหม่กว่ารุ่นแรก

หากคุณรู้ว่าจะมีเพียงสองทางเลือกเท่านั้นบูลีนอาจจะใช้ได้ Enums สามารถขยายได้ในลักษณะที่จะไม่ทำลายรหัสเก่าแม้ว่าไลบรารีเก่าอาจไม่รองรับค่า enum ใหม่ดังนั้นจึงไม่สามารถมองข้ามการกำหนดเวอร์ชันได้ทั้งหมด


แก้ไข

ใน C # เวอร์ชันใหม่กว่าสามารถใช้อาร์กิวเมนต์ที่ตั้งชื่อได้ซึ่ง IMO สามารถทำให้รหัสการโทรชัดเจนขึ้นในลักษณะเดียวกับที่ enums สามารถทำได้ โดยใช้ตัวอย่างเดียวกับด้านบน:

Enum.Parse(str, ignoreCase: true);

0

โดยที่ฉันยอมรับว่า Enums เป็นวิธีที่ดีในวิธีการที่คุณมี 2 ตัวเลือก (และมีเพียงสองตัวเลือกที่คุณสามารถอ่านได้โดยไม่ต้องใช้ enum)

เช่น

public void writeData(Stream data, boolean is_overwrite)

รัก Enums แต่บูลีนก็มีประโยชน์เช่นกัน


0

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

ความคิดเห็นแบบอินไลน์ช่วยแก้ปัญหาที่ไม่คาดคิดได้เป็นอย่างboolดี ตัวอย่างเดิมเป็นเรื่องเลวร้ายโดยเฉพาะอย่างยิ่ง: ลองนึกภาพพยายามตั้งชื่อตัวแปรในการลดฟังก์ชัน! มันจะเป็นอย่างนั้น

void writeData( DataObject data, bool use_append_mode );

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

file.writeData( data, true );

กับ

file.writeData( data, true /* use_append_mode */);

-1

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


-1

การใช้ enums แทนบูลีนในตัวอย่างของคุณจะช่วยให้การเรียกเมธอดนั้นอ่านง่ายขึ้น อย่างไรก็ตามนี่เป็นการใช้แทนรายการความปรารถนาที่ฉันชอบใน C # ซึ่งตั้งชื่ออาร์กิวเมนต์ในการเรียกใช้วิธี ไวยากรณ์นี้:

var v = CallMethod(pData = data, pFileMode = WriteMode, pIsDirty = true);

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

C # 3.0 อนุญาตให้มีอาร์กิวเมนต์ที่มีชื่อในตัวสร้าง ฉันไม่รู้ว่าทำไมพวกเขาถึงไม่สามารถทำได้ด้วยวิธีการเช่นกัน


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

-1

ค่าบูลีนtrue/ falseเท่านั้น จึงไม่ชัดเจนว่าสื่อถึงอะไร Enumสามารถมีชื่อที่มีความหมายเช่นOVERWRITE, APPEND, enums ฯลฯ ดังนั้นจะดีกว่า

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