การใช้พารามิเตอร์บูลีนผิดพลาดหรือไม่


39

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

รุ่นเดิม

public void setState(boolean flag){
    if(flag){
        a();
    }else{
        b();
    }
    c();
}

เวอร์ชั่นใหม่:

public void setStateTrue(){
    a();
    c();
}

public void setStateFalse(){
    b();
    c();
}

แต่วิธีการเกี่ยวกับกรณีที่พารามิเตอร์บูลีนจะใช้ในการกำหนดค่าแทนพฤติกรรม? เช่น:

public void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

ฉันกำลังพยายามกำจัดการตั้งค่าสถานะ isHintOn และสร้าง 2 ฟังก์ชันที่แยกกัน:

public void setHintOn(){
    this.layer1.visible=true;
    this.layer2.visible=false;
    this.layer3.visible=true;
}

public void setHintOff(){
    this.layer1.visible=false;
    this.layer2.visible=true;
    this.layer3.visible=false;
}

แต่ดูเหมือนว่าเวอร์ชั่นที่แก้ไขจะสามารถบำรุงรักษาได้น้อยลงเนื่องจาก:

  1. มันมีรหัสมากกว่าเวอร์ชั่นดั้งเดิม

  2. ไม่สามารถแสดงได้อย่างชัดเจนว่าการมองเห็นของ layer2 นั้นตรงกันข้ามกับตัวเลือกคำใบ้

  3. เมื่อเพิ่มเลเยอร์ใหม่ (เช่น: layer4) ฉันต้องเพิ่ม

    this.layer4.visible=false;
    

    และ

    this.layer4.visible=true;  
    

    เป็น setHintOn () และ setHintOff () แยกกัน

ดังนั้นคำถามของฉันคือถ้าใช้พารามิเตอร์บูลีนเพื่อกำหนดค่าเท่านั้น แต่ไม่ใช่พฤติกรรม (เช่น: ไม่มี if-else บนพารามิเตอร์นั้น) จะแนะนำให้กำจัดพารามิเตอร์บูลีนนั้นหรือไม่


26
มันไม่เคยผิดถ้ารหัสที่เป็นผลลัพธ์นั้นอ่านได้ง่ายขึ้นและสามารถบำรุงรักษาได้ ;-) ฉันขอแนะนำให้ใช้วิธีการเดียวแทนที่จะเป็นสองวิธีแยกกัน
helb

32
คุณนำเสนอข้อโต้แย้งที่น่าสนใจว่าการใช้วิธีการเดียวที่กำหนดบูลีนเหล่านี้จะส่งผลให้การบำรุงรักษาชั้นเรียนง่ายขึ้นและเข้าใจการใช้งานของมัน ดีมาก; นั่นคือการพิจารณาที่ถูกต้องตามกฎหมาย แต่อินเทอร์เฟซสาธารณะของชั้นไม่จำเป็นต้องเปลี่ยนรูปเพื่อรองรับพวกเขา หากวิธีการแยกจากกันจะทำให้อินเตอร์เฟซที่สาธารณะง่ายต่อการเข้าใจและการทำงานที่มีการกำหนดของคุณsetHint(boolean isHintOn)เป็นส่วนตัววิธีการและเพิ่มสาธารณะsetHintOnและsetHintOffวิธีการที่ตามลำดับเรียกและsetHint(true) setHint(false)
Mark Amery

9
ฉันไม่พอใจมากกับชื่อวิธีการเหล่านั้น: พวกเขาไม่ได้ให้ประโยชน์อะไรsetHint(true|false)เลย มันฝรั่งมันฝรั่ง บางสิ่งบางอย่างที่ใช้งานน้อยเหมือนและsetHint unsetHint
Konrad Rudolph


4
@kevincline หากเงื่อนไขเป็นชื่อเดียวคุณเขียนisที่จุดเริ่มต้น isValidเป็นต้นเหตุใดจึงต้องเปลี่ยนคำสองคำนี้ นอกจากนี้ "ดูเป็นธรรมชาติ" อยู่ในสายตาของคนดู หากคุณต้องการออกเสียงมันเป็นประโยคภาษาอังกฤษสำหรับฉันแล้วมันจะเป็นเรื่องธรรมดากว่าที่จะมี "ถ้าคำใบ้ใช้" กับ "the" tucked in
Mr Lister

คำตอบ:


95

การออกแบบ API ควรให้ความสำคัญกับสิ่งที่สามารถใช้ได้มากที่สุดสำหรับลูกค้าของ API ที่จากด้านเรียก

ตัวอย่างเช่นหาก API ใหม่นี้กำหนดให้ผู้โทรต้องเขียนรหัสประจำเช่นนี้

if(flag)
    foo.setStateTrue();
else
    foo.setStateFalse();

ดังนั้นควรชัดเจนว่าการหลีกเลี่ยงพารามิเตอร์นั้นแย่กว่าการมี API ซึ่งอนุญาตให้ผู้เรียกเขียน

 foo.setState(flag);

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

อย่างไรก็ตามด้านการใช้งานไม่ควรกำหนดว่า API สาธารณะจะมีลักษณะอย่างไร หากฟังก์ชั่นเช่นเดียวsetHintกับพารามิเตอร์ต้องการรหัสน้อยในการใช้งาน แต่ API ในแง่setHintOn/ setHintOffดูง่ายต่อการใช้สำหรับลูกค้าหนึ่งสามารถใช้มันด้วยวิธีนี้:

private void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

public void setHintOn(){
   setHint(true);
}

public void setHintOff(){
   setHint(false);
}

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

วิธีนี้จะทำงานในลักษณะตรงกันข้าม: หากsetStateวิธีการจากด้านบนจำเป็นต้องสลับระหว่างโค้ดสองส่วนที่แตกต่างกันชิ้นส่วนของรหัสนั้นสามารถเปลี่ยนเป็นวิธีส่วนตัวสองวิธีที่แตกต่างกัน ดังนั้น IMHO จึงไม่เหมาะสมที่จะค้นหาเกณฑ์สำหรับการตัดสินใจระหว่าง "หนึ่งพารามิเตอร์ / วิธีหนึ่ง" และ "ศูนย์พารามิเตอร์ / สองวิธี" โดยดูที่ internals ดูอย่างไรในแบบที่คุณต้องการเห็น API ในบทบาทของผู้บริโภค

หากมีข้อสงสัยลองใช้ "การทดสอบขับเคลื่อนการพัฒนา" (TDD) ที่จะบังคับให้คุณคิดเกี่ยวกับ API สาธารณะและวิธีการใช้งาน


DocBrown คุณจะบอกว่าเส้นแบ่งที่เหมาะสมคือการตั้งค่าของแต่ละรัฐมีผลข้างเคียงที่ซับซ้อนและอาจเป็นไปได้หรือไม่? ตัวอย่างเช่นหากคุณเพียงแค่สลับธงที่ทำในสิ่งที่มันพูดบนกระป๋องและไม่มีเครื่องรัฐพื้นฐานคุณจะกำหนดพารามิเตอร์รัฐที่แตกต่างกัน ตัวอย่างเช่นในขณะที่คุณจะไม่ตั้งค่าพารามิเตอร์วิธีการเช่นSetLoanFacility(bool enabled)เนื่องจากมีการให้สินเชื่อมันอาจไม่ง่ายที่จะนำมันออกไปอีกครั้งและตัวเลือกทั้งสองอาจเกี่ยวข้องกับตรรกะที่แตกต่างกันโดยสิ้นเชิง - และคุณต้องการย้ายไปสร้างแยกต่างหาก / ลบวิธีการ
Steve

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

@Steve: ดูการแก้ไขของฉัน
Doc Brown

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

1
@Steve ดังนั้นหากผู้ใช้ต้องการยืนยันการตั้งค่าที่เฉพาะเจาะจงนั้นToggle()ไม่ใช่ฟังก์ชั่นที่ถูกต้องที่จะให้ นั่นคือจุดทั้งหมด; หากผู้โทรสนใจเพียงแค่ "เปลี่ยน" และไม่ใช่ "สิ่งที่มันจะกลายเป็น" ก็Toggle()เป็นตัวเลือกที่หลีกเลี่ยงการตรวจสอบและการตัดสินใจเพิ่มเติม ฉันจะไม่เรียกว่ากรณีทั่วไปและฉันจะไม่แนะนำให้ใช้งานได้โดยไม่มีเหตุผลที่ดี แต่ถ้าผู้ใช้ต้องการสลับแล้วฉันจะให้สลับพวกเขา
Kamil Drakari

40

Martin Fowler เสนอราคา Kent Beck ในการแนะนำsetOn() setOff()วิธีการแยกต่างๆ แต่ยังบอกด้วยว่าสิ่งนี้ไม่ควรถูกพิจารณาว่าเป็นการละเมิดสิทธิ์:

หากคุณดึงข้อมูล [sic] จากแหล่งบูลีนเช่นตัวควบคุม UI หรือแหล่งข้อมูลฉันควรมีsetSwitch(aValue)มากกว่า

if (aValue)
  setOn();
else
  setOff();

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

ข้อเสนอแนะอีกประการหนึ่งคือการใช้ค่าหรือชนิดแฟล็กที่แจกแจงเพื่อให้trueและfalseดีกว่าชื่อเฉพาะบริบท ในตัวอย่างของคุณshowHintและhideHintอาจจะดีกว่า


16
จากประสบการณ์ของฉัน setSwitch (ค่า) มักจะส่งผลให้รหัสโดยรวมน้อยกว่า setOn / setOff อย่างแม่นยำเพราะรหัส if / else ที่ยกมาในคำตอบของคุณ ฉันมักจะสาปแช่งผู้พัฒนาที่ให้ API กับ setOn / setOff มากกว่า setSwitch (ค่า)
17 จาก 26

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

@ 17of26 เป็นตัวอย่างที่เป็นรูปธรรม (เพื่อแสดงให้เห็นว่า "มันขึ้นอยู่กับ" มากกว่าที่จะเป็นตัวอย่างที่ดีกว่า) ใน AppKit ของ Apple มี-[NSView setNeedsDisplay:]วิธีการที่คุณผ่านYESถ้ามุมมองควรวาดใหม่และNOถ้ามันไม่ควร คุณแทบไม่จำเป็นต้องบอกว่าไม่ได้ดังนั้น UIKit จึง-[UIView setNeedsDisplay]ไม่มีพารามิเตอร์ มันไม่มี-setDoesNotNeedDisplayวิธีการที่สอดคล้องกัน
Graham Lee

2
@ GrahamLee ฉันคิดว่าข้อโต้แย้งของ Fowler นั้นค่อนข้างบอบบางและวางอยู่บนการตัดสิน ฉันจะไม่ใช้bool isPremiumธงในตัวอย่างของเขา แต่ฉันจะใช้ enum ( BookingType bookingType) เพื่อกำหนดพารามิเตอร์วิธีเดียวกันยกเว้นตรรกะสำหรับการจองแต่ละครั้งค่อนข้างแตกต่างกัน "ตรรกะที่พันกัน" ที่ฟาวเลอร์อ้างถึงมักเป็นที่ต้องการถ้าใครอยากเห็นความแตกต่างระหว่างสองโหมด และหากพวกเขาแตกต่างกันอย่างสิ้นเชิงฉันจะเปิดเผยวิธีการกำหนดพารามิเตอร์ภายนอกและใช้วิธีการแยกต่างหากภายใน
Steve

3

ฉันคิดว่าคุณกำลังผสมสองสิ่งในโพสต์ของคุณ API และการใช้งาน ในทั้งสองกรณีฉันไม่คิดว่ามีกฎที่แข็งแกร่งที่คุณสามารถใช้ตลอดเวลา แต่คุณควรพิจารณาสองสิ่งนี้อย่างอิสระ (เท่าที่จะทำได้)

เริ่มต้นด้วย API ทั้งคู่:

public void setHint(boolean isHintOn)

และ:

public void setHintOn()
public void setHintOff()

เป็นทางเลือกที่ถูกต้องขึ้นอยู่กับสิ่งที่วัตถุของคุณควรนำเสนอและลูกค้าของคุณจะใช้ API อย่างไร ดังที่ Doc ชี้ให้เห็นหากผู้ใช้ของคุณมีตัวแปรบูลีน (จากการควบคุม UI, การกระทำของผู้ใช้, ภายนอก, API และอื่น ๆ ) ตัวเลือกแรกจะเหมาะสมกว่ามิฉะนั้นคุณจะบังคับใช้คำสั่งพิเศษหากรหัสของลูกค้า . อย่างไรก็ตามหากตัวอย่างเช่นคุณกำลังเปลี่ยนคำใบ้เป็นจริงเมื่อเริ่มต้นกระบวนการและเป็นเท็จในตอนท้ายตัวเลือกแรกจะให้สิ่งนี้กับคุณ:

setHint(true)
// Do your process
…
setHint(false)

ในขณะที่ตัวเลือกที่สองให้สิ่งนี้แก่คุณ:

setHintOn()
// Do your process
…
setHintOff()

IMO ใดที่อ่านได้มากขึ้นดังนั้นฉันจะเลือกตัวเลือกที่สองในกรณีนี้ เห็นได้ชัดว่าไม่มีอะไรหยุดคุณจากการเสนอทั้งสองตัวเลือก (หรือมากกว่านั้นคุณสามารถใช้ enum ได้ตามที่ Graham กล่าวถ้าทำเช่นนั้นเหมาะสมกว่า)

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

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

private void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

public void setHintOn(){
   setHint(true);
}

public void setHintOff(){
   setHint(false);
}

ในทางกลับกันสมมติว่าเราเลือกsetHint(boolean isHintOn)API ของเรา แต่ลองย้อนกลับตัวอย่างของคุณเนื่องจากเหตุผลใดก็ตามที่การตั้งค่าคำใบ้เป็นแตกต่างอย่างสิ้นเชิงกับการตั้งค่าเป็นปิด ในกรณีนี้เราสามารถใช้มันได้ดังนี้

public void setHint(boolean isHintOn){
    if(isHintOn){
        // Set it On
    } else {
        // Set it Off
    }    
}

หรือแม้กระทั่ง:

public void setHint(boolean isHintOn){    
    if(isHintOn){
        setHintOn()
    } else {
        setHintOff()
   }    
}

private void setHintOn(){
   // Set it On
}

private void setHintOff(){
   // Set it Off 
}

ประเด็นก็คือในทั้งสองกรณีเราเลือก API สาธารณะของเราก่อนจากนั้นปรับการใช้งานของเราให้เหมาะสมกับ API ที่เลือก (และข้อ จำกัด ที่เรามี) ไม่ใช่วิธีอื่น

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


3
ในฐานะที่เป็นหมายเหตุด้านข้างถ้ามันเป็นสิ่งที่คล้ายกับหลอกหลอก (หนึ่งคำสั่งที่จุดเริ่มต้นหนึ่งตอนท้าย) คุณควรใช้beginและendหรือคำพ้องความหมายเพียงเพื่อให้ชัดเจนชัดเจนว่าพวกเขาทำอะไรและเพื่อบ่งบอกว่า การเริ่มต้นจะต้องมีจุดสิ้นสุดและในทางกลับกัน
Nic Hartley

ฉันเห็นด้วยกับ Nic และจะเพิ่ม: หากคุณต้องการให้แน่ใจว่าจุดเริ่มต้นและจุดสิ้นสุดนั้นเชื่อมโยงกันเสมอคุณควรให้สำนวนเฉพาะภาษาสำหรับสิ่งนั้น: RAII / ขอบเขตของหน่วยเฝ้าระวังใน C ++ usingบล็อกใน C #, withผู้จัดการบริบทคำสั่งใน Python เนื้อความของเป็นแลมบ์ดาหรือวัตถุที่เรียกได้ (เช่นไวยากรณ์ของบล็อก Ruby) ฯลฯ
Daniel Pryden

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

2

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

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

bool isBackgroundVisible() {
    return isHintVisible;
}    

bool isContentVisible() {
    return !isHintVisible;
}

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

นี่ยังทำให้คุณมีคำถามว่าจะมีsetHintVisibility(bool)วิธีการหรือไม่ โดยส่วนตัวแล้วผมขอแนะนำให้แทนที่ด้วย a showHint()และhideHint()method - ทั้งคู่จะง่ายมากและคุณไม่ต้องเปลี่ยนมันเมื่อคุณเพิ่มเลเยอร์ อย่างไรก็ตามมันไม่ถูกต้อง / ผิดชัดเจน

ตอนนี้ถ้าการเรียกใช้ฟังก์ชันควรเปลี่ยนการมองเห็นของเลเยอร์เหล่านั้นจริง ๆ แล้วคุณมีพฤติกรรม ในกรณีนั้นฉันขอแนะนำวิธีการต่าง ๆ อย่างแน่นอน


ดังนั้น tl; dr คือ: คุณแนะนำให้แบ่งออกเป็นสองฟังก์ชั่นเพราะคุณไม่ต้องเปลี่ยนมันเมื่อคุณเพิ่มเลเยอร์? หากมีการเพิ่มเลเยอร์ 4 เราอาจต้องพูดว่า "this.layer4.visibility = isHintOn" ด้วยดังนั้นฉันไม่แน่ใจว่าฉันเห็นด้วย หากมีสิ่งใดนั่นเป็นการกระทำที่ผิดเพี้ยนเมื่อตอนนี้เมื่อเพิ่มเลเยอร์เราต้องแก้ไขสองฟังก์ชั่นไม่ใช่แค่หนึ่งเลเยอร์
Erdrik Ironrose

ไม่ฉัน (อ่อนแอ) แนะนำให้ใช้เพื่อความชัดเจน ( showHintvs setHintVisibility) ฉันพูดถึงเลเยอร์ใหม่เพียงเพราะ OP กังวลเกี่ยวกับมัน isLayer4Visibleนอกจากนี้เราก็ต้องเพิ่มวิธีการใหม่ที่หนึ่ง: showHintและhideHintเพียงตั้งค่าisHintVisibleแอตทริบิวต์เป็นจริง / เท็จและไม่เปลี่ยนแปลง
doubleYou

1
@doubleYou คุณบอกว่ารหัสอีกต่อไปไม่ได้บำรุงรักษาน้อย ฉันจะบอกว่าความยาวของรหัสเป็นหนึ่งในตัวแปรหลักในการบำรุงรักษาและความชัดเจนมากเกินไปโดยความซับซ้อนของโครงสร้างเท่านั้น รหัสใด ๆ ที่กลายเป็นโครงสร้างที่ซับซ้อนและยาวขึ้นควรจะได้รับเป็นอย่างอื่นปัญหาอย่างง่ายจะได้รับการรักษาที่ซับซ้อนกว่าในรหัสมากกว่าที่พวกเขาสมควรได้รับและ codebase จะได้รับบรรทัดที่ไม่จำเป็น
Steve

@ Steve ฉันเห็นด้วยอย่างยิ่งว่าคุณสามารถควบคุมสิ่งต่างๆได้มากขึ้นเช่นทำให้โค้ดยาวขึ้นโดยไม่ทำให้ชัดเจนยิ่งขึ้น - คุณสามารถทำให้โค้ดสั้นลงโดยมีความชัดเจนดังนั้นจึงไม่มีความสัมพันธ์ 1: 1 ที่นี่
doubleYou

@ Steve คิดว่า“รหัสกอล์ฟ” - การรหัสดังกล่าวและเขียนไว้ในบรรทัดที่มากขึ้นไม่มักจะทำให้มันชัดเจน „ Code Golf“ นั้นสุดขีด แต่ยังมีโปรแกรมเมอร์จำนวนมากที่คิดว่าการยัดเยียดทุกอย่างให้กลายเป็นคำพูดที่ฉลาดคือ“ สง่างาม” และอาจจะเร็วกว่าเพราะคอมไพเลอร์ไม่ได้ปรับให้เหมาะสมดีพอ
BlackJack

1

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

ตัวอย่างแรกเป็นปัญหาแม้ว่าการตั้งชื่อจะระบุวิธีการตั้งค่า แต่การใช้งานดูเหมือนจะแตกต่างกัน ดังนั้นคุณจึงมี antipattern ที่เปลี่ยนพฤติกรรมและวิธีการตั้งชื่อที่ทำให้เข้าใจผิด แต่ถ้าวิธีการที่จริงเป็นตัวตั้งค่าปกติ (โดยไม่ต้องเปลี่ยนพฤติกรรม) setState(boolean)แล้วมีปัญหากับไม่มี การมีสองวิธีsetStateTrue()และsetStateFalse()เป็นสิ่งที่ไม่จำเป็นต้องทำให้ยุ่งยากโดยไม่จำเป็น


1

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

ตัวอย่างเช่นใน Java คุณสามารถทำได้:

public enum HintState {
    SHOW_HINT(true, false, true),
    HIDE_HINT(false, true, false);

    private HintState(boolean layer1Visible, boolean layer2Visible, boolean layer3Visible) {
         // constructor body and accessors omitted for clarity
    }
}

จากนั้นรหัสผู้โทรของคุณจะเป็นดังนี้:

setHint(HintState.SHOW_HINT);

และรหัสการติดตั้งของคุณจะเป็นดังนี้:

public void setHint(HintState hint) {
    this.layer1Visible = hint.isLayer1Visible();
    this.layer2Visible = hint.isLayer2Visible();
    this.layer3Visible = hint.isLayer3Visible();
}

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


0

ดังนั้นคำถามของฉันคือถ้าใช้พารามิเตอร์บูลีนเพื่อกำหนดค่าเท่านั้น แต่ไม่ใช่พฤติกรรม (เช่น: ไม่มี if-else บนพารามิเตอร์นั้น) จะแนะนำให้กำจัดพารามิเตอร์บูลีนนั้นหรือไม่

เมื่อฉันมีข้อสงสัยเกี่ยวกับสิ่งต่าง ๆ ฉันชอบภาพที่กองร่องรอยจะมีลักษณะอย่างไร

เป็นเวลาหลายปีที่ผมทำงานในโครงการ PHP ที่ใช้ฟังก์ชั่นเช่นเดียวกับหมาและทะเยอทะยาน หากคุณผ่านค่า nullมันจะคืนค่ามิฉะนั้นตั้งค่า มันก็น่ากลัวจะทำงานร่วมกับ

นี่คือตัวอย่างการติดตามสแต็ก:

function visible() : line 440
function parent() : line 398
function mode() : line 384
function run() : line 5

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

ตอนนี้รูปภาพทำงานกับการติดตามสแต็กสำหรับฟังก์ชันที่มีลักษณะ A หรือ B ตามค่าบูลีน

function bar() : line 92
function setVisible() : line 120
function foo() : line 492
function setVisible() : line 120
function run() : line 5

สับสนถ้าคุณถามฉัน setVisibleบรรทัดเดียวกันให้เส้นทางการติดตามที่แตกต่างกันสองเส้นทาง

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

นี่คือเคล็ดลับ:

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

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


1
สิ่งที่ทำให้ฉันสับสนเกี่ยวกับการsetVisibleติดตามสแต็กคือการโทรsetVisible(true)ปรากฏขึ้นเพื่อให้โทรไปยังsetVisible(false)(หรือวิธีอื่น ๆ ขึ้นอยู่กับว่าคุณได้รับการติดตามนั้นเกิดขึ้น)
David K

0

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

หากคุณไม่ทำอะไรมากไปกว่าใช้สิ่งEnumที่แสดงถึงสถานะที่คุณได้ปรับปรุงความเข้าใจของรหัสของคุณ

ตัวอย่างนี้ใช้NodeคลาสจากJavaFX:

public enum Visiblity
{
    SHOW, HIDE

    public boolean toggleVisibility(@Nonnull final Node node) {
        node.setVisible(!node.isVisible());
    }
}

ดีกว่าเสมอเหมือนพบได้ในหลาย ๆJavaFXวัตถุ:

public void setVisiblity(final boolean flag);

แต่ฉันคิดว่า.setVisible()และ.setHidden()เป็นทางออกที่ดีที่สุดสำหรับสถานการณ์ที่ธงเป็นbooleanเพราะมันชัดเจนที่สุดและ verbose น้อยที่สุด

ในกรณีของบางสิ่งที่มีตัวเลือกหลายตัวสิ่งนี้สำคัญยิ่งกว่าที่จะทำเช่นนี้ EnumSetมีอยู่เพียงเพราะเหตุนี้

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


0

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

หากการใช้งานทั้งหมดจะผ่านค่าคงที่ (เช่นจริงเท็จ) จากนั้นจะระบุว่าเกินพิกัด

หากการใช้งานทั้งหมดจะผ่านค่าตัวแปรดังนั้นที่ระบุสำหรับวิธีการที่มีวิธีการพารามิเตอร์

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


เกิดอะไรขึ้นถ้าคุณออกแบบระบบแบบบูรณาการและคุณอยู่ในท้ายที่สุดทั้งผู้ผลิตและรหัส "ลูกค้าเสีย" ของรหัส? คนที่ยืนอยู่ในรองเท้าของลูกค้าที่บริโภคควรกำหนดวิธีการของพวกเขาสำหรับวิธีการหนึ่งมากกว่าอีกวิธีหนึ่งอย่างไร
Steve

@ Steve ในฐานะที่เป็นลูกค้าที่บริโภคคุณรู้หรือไม่ว่าคุณกำลังผ่านค่าคงที่หรือตัวแปร หากค่าคงที่ผ่านควรใช้เกินพิกัดโดยไม่มีพารามิเตอร์
Erik Eidt

แต่ฉันสนใจที่จะพูดอย่างชัดเจนว่าทำไมถึงเป็นเช่นนั้น ทำไมไม่จ้าง enums สำหรับค่าคงที่จำนวน จำกัด เนื่องจากเป็นไวยากรณ์ที่มีน้ำหนักเบาในภาษาส่วนใหญ่ที่ออกแบบมาอย่างแม่นยำเพื่อจุดประสงค์ประเภทนี้
Steve

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

0

มีสองข้อพิจารณาในการออกแบบ:

  • API: ส่วนต่อประสานที่คุณนำเสนอให้กับผู้ใช้
  • การใช้งาน: ความชัดเจนการบำรุงรักษา ฯลฯ ...

พวกเขาไม่ควรแชท

มันสมบูรณ์แบบดีกับ:

  • มีหลายวิธีในการมอบสิทธิ์ API ให้กับการใช้งานเพียงครั้งเดียว
  • มีวิธีการเดียวในการจัดส่ง API ไปยังการใช้งานหลายอย่างขึ้นอยู่กับเงื่อนไข

ดังนั้นข้อโต้แย้งใด ๆ ที่พยายามรักษาสมดุลต้นทุน / ผลประโยชน์ของการออกแบบ API โดยต้นทุน / ผลประโยชน์ของการออกแบบการใช้งานนั้นน่าสงสัยและควรตรวจสอบอย่างรอบคอบ


ในด้าน API

ในฐานะโปรแกรมเมอร์ฉันมักจะชอบ API ที่ตั้งโปรแกรมได้ รหัสมีความชัดเจนมากขึ้นเมื่อฉันสามารถส่งต่อค่าได้มากกว่าเมื่อฉันต้องการif/ switchคำสั่งเกี่ยวกับค่าที่จะตัดสินใจว่าฟังก์ชั่นที่จะโทร

หลังอาจมีความจำเป็นหากแต่ละฟังก์ชั่นต้องการข้อโต้แย้งที่แตกต่างกัน

ในกรณีของคุณดังนั้นวิธีการเดียวsetState(type value)ดูเหมือนจะดีกว่า

แต่มีอะไรที่เลวร้ายยิ่งกว่านิรนามtrue, false, 2ฯลฯ ... ค่ามายากลเหล่านั้นไม่มีความหมายของตัวเอง หลีกเลี่ยงความหลงใหลดั้งเดิมและโอบกอดการพิมพ์ที่แข็งแกร่ง

ดังนั้นจาก POV API setState(State state)ผมต้องการ:


ในด้านการดำเนินงาน

ฉันขอแนะนำให้ทำสิ่งที่ง่ายกว่า

หากวิธีการนี้เป็นวิธีที่ง่ายก็จะเป็นการรวมตัวที่ดีที่สุด หากการไหลของการควบคุมมีความซับซ้อนควรแยกเป็นหลาย ๆ วิธีโดยแต่ละกรณีจะเกี่ยวข้องกับกรณีย่อยหรือขั้นตอนของท่อ


สุดท้ายพิจารณาการจัดกลุ่ม

ในตัวอย่างของคุณ (พร้อมช่องว่างที่เพิ่มเพื่อให้สามารถอ่านได้):

this.layer1.visible = isHintOn;
this.layer2.visible = ! isHintOn;
this.layer3.visible = isHintOn;

ทำไมlayer2แนวโน้มเช่นนี้ มันเป็นคุณสมบัติหรือเป็นข้อบกพร่อง?

เป็นไปได้หรือไม่ที่จะมี 2 รายการ[layer1, layer3]และ[layer2]มีชื่อที่ชัดเจนซึ่งระบุสาเหตุที่พวกเขาถูกจัดกลุ่มเข้าด้วยกันแล้วทำซ้ำมากกว่ารายการเหล่านั้น

ตัวอย่างเช่น:

for (auto layer : this.mainLayers) { // layer2
    layer.visible = ! isHintOn;
}
for (auto layer : this.hintLayers) { // layer1 and layer3
    layer.visible = isHintOn;
}

รหัสพูดด้วยตัวเองมันชัดเจนว่าทำไมมีสองกลุ่มและพฤติกรรมของพวกเขาแตกต่างกัน


0

แยกออกจากคำถามsetOn() + setOff()vs set(flag)ฉันจะพิจารณาอย่างรอบคอบว่าประเภทบูลีนดีที่สุดที่นี่หรือไม่ คุณแน่ใจหรือไม่ว่าจะไม่มีทางเลือกที่สาม?

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

setHint(false)

VS

setHint(Visibility::HIDE)

ด้วย enum จะขยายได้ง่ายขึ้นเมื่อมีคนตัดสินใจว่าต้องการตัวเลือก 'ถ้าจำเป็น':

enum class Visibility {
  SHOW,
  HIDE,
  IF_NEEDED // New
}

VS

setHint(false)
setHint(true)
setHintAutomaticMode(true) // New

0

ตามที่ ... ฉันรู้ถึงความสำคัญของการหลีกเลี่ยงการใช้พารามิเตอร์บูลีนเพื่อตรวจสอบพฤติกรรม

ฉันขอแนะนำให้ประเมินความรู้นี้อีกครั้ง

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

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

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


0

การกำหนดคำถาม

คำถามชื่อของคุณคือ "ผิด [หรือ]]?" - แต่คุณหมายถึงอะไรกับ "ผิด"?

ตามคอมไพเลอร์ C # หรือ Java ก็ไม่ผิด ฉันมั่นใจว่าคุณทราบดีและไม่ใช่สิ่งที่คุณขอ ฉันเกรงว่านอกจากนั้นเรามีเพียงความคิดเห็นที่แตกต่างกันของnโปรแกรมเมอร์ n+1คำตอบนี้นำเสนอสิ่งที่Clean Codeหนังสือพูดถึงเกี่ยวกับเรื่องนี้

ตอบ

รหัสที่สะอาดทำให้ตัวพิมพ์ใหญ่แข็งแรงต่ออาร์กิวเมนต์ของฟังก์ชันโดยทั่วไป:

การโต้แย้งยาก พวกเขาใช้พลังความคิดมากมาย [... ] ผู้อ่านของเราจะต้องตีความมันทุกครั้งที่พวกเขาเห็นมัน

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

กรณีที่เจาะจงของการโต้แย้งค่าสถานะจะได้รับการแก้ไขในภายหลังโดยตรง:

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

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


ข้อมูลเพิ่มเติม:

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


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

if (isAfterSunset) light.TurnOn();
else light.TurnOff();

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


ฉันไม่รู้ว่าคุณทำการทดสอบหรือไม่ในกรณีนี้ให้ลองพิจารณาสิ่งนี้ด้วย:

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


คุณได้ฝัง lede ที่นี่จริง ๆ : "... ประโยคแรกของคุณพูดถึง" ความสำคัญของการหลีกเลี่ยง [sic] โดยใช้พารามิเตอร์บูลีนเพื่อกำหนดพฤติกรรม "และนั่นคือพื้นฐานสำหรับคำถามทั้งหมดฉันไม่เห็น เหตุผลที่ทำให้สิ่งที่ไม่ดีทำง่ายสำหรับผู้ใช้ API " นี่คือจุดที่น่าสนใจ แต่คุณค่อนข้างบ่อนทำลายการโต้แย้งของคุณในย่อหน้าสุดท้ายของคุณ
สัญลักษณ์แทน

ฝัง lede หรือไม่ หลักของคำตอบนี้คือ "ใช้อาร์กิวเมนต์น้อยที่สุดเท่าที่จะทำได้" ซึ่งอธิบายไว้ในครึ่งแรกของคำตอบนี้ ทุกอย่างหลังจากนั้นเป็นเพียงข้อมูลเพิ่มเติม: การโต้แย้งอาร์กิวเมนต์ที่ขัดแย้งกัน (โดยผู้ใช้รายอื่นไม่ใช่ OP) และสิ่งที่ไม่สามารถใช้ได้กับทุกคน
R. Schmitz

ย่อหน้าสุดท้ายกำลังพยายามทำให้ชัดเจนว่าคำถามหัวเรื่องไม่ได้ถูกนิยามไว้อย่างดีพอที่จะตอบได้ OP ถามว่ามัน "ผิด" แต่ไม่พูดตามใครหรืออะไร ตามคอมไพเลอร์? ดูเหมือนว่ารหัสที่ถูกต้องจึงไม่ผิด ตามหนังสือสะอาดรหัส? มันใช้อาร์กิวเมนต์ธงดังนั้นใช่มันคือ "ผิด" อย่างไรก็ตามฉันเขียน "แนะนำ" แทนเพราะในทางปฏิบัติ> ดันทุรัง คุณคิดว่าฉันต้องทำให้ชัดเจนยิ่งขึ้นหรือไม่?
R. Schmitz

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

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