รหัสแฟนซีสั้นที่แสดงความคิดเห็นกับรหัสที่เข้าใจง่ายอีกต่อไปที่ไม่ใส่โค้ดซึ่งเป็นที่ต้องการ?


18

บางครั้งอัลกอริทึมสามารถเขียนได้สองวิธี:

  • วิธีที่สั้นและแฟนซี หรือ
  • วิธีที่ยาวและง่ายต่อการเข้าใจ

ยกตัวอย่างเช่นที่นี่เป็นอีกต่อไปวิธีที่ง่ายของการคัดลอกสตริงsourceไปdestใน C:

*dest = *source;
while (*source != '\0') {
    source++;
    dest++;
    *dest = *source;
} (true);

และนี่เป็นวิธีที่สั้นและแฟนซี

// Copy string source to dest
while (*dest++ = *source++);

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

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

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

EDIT2:นี่เป็นตัวอย่างใหม่ที่ฉันได้จากSO :

ความคิดเห็นเวอร์ชั่นแฟนซี:

//direct formula for xoring all numbers from 1 to N
int Sum = (N & (N % 2 ? 0 : ~0) | ( ((N & 2)>>1) ^ (N & 1) ) );

รุ่นที่ไม่มีความคิดเห็นยาว:

int Sum = 0;
for (int i = 1; i < N; ++i)
{
   Sum ^= i; //or Sum = Sum ^ i;
}

2
@ gablin: "goodly" เป็นคำ แต่มันเป็นคำคุณศัพท์ที่มีความหมายค่อนข้างแตกต่างจาก "good" และไม่ได้ใช้กันอย่างแพร่หลายอีกต่อไป (อย่างน้อยในสหรัฐอเมริกา) รูปแบบคำวิเศษณ์ของ "good" คือ "well" เช่นเดียวกับใน "รหัสที่มีความคิดเห็นดี"
John M Gant

@John: อ่าใช่แน่นอน ฉันได้อัปเดตคำถามตาม ขอบคุณ
gablin

2
ทางลัด 'แฟนซี' ในทางใด? นั่นคือรหัสชิ้นส่วนของข้อความ C ที่ใครก็ตามที่อ้างว่ารู้ C ควรสามารถอ่านได้โดยไม่มีปัญหาใด ๆ ความอดทนไม่ได้หมายถึง 'แฟนซี'
JBRWilkinson

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

@gablin: การแก้ไขของคุณมีผลต่อคำตอบปัจจุบัน
Maniero

คำตอบ:


28

โดยทั่วไปฉันต้องการแยกรหัสแฟนซีออกเป็นวิธีการของตัวเอง ..

แทนที่จะแสดงความคิดเห็นรหัสแฟนซีชื่อวิธีการควรเป็นทุกอย่างที่จำเป็นเพื่อทำให้สิ่งต่าง ๆ ชัดเจน

char *copy_string(char *s, const char *t) {    
    while (*s++ = *t++); 
    return s;
}

3
จุดที่ดี การแยกเอาโค้ดที่มีในตัวเองดีกว่าความคิดเห็น คอมไพเลอร์รู้วิธีการอินไลน์หากจำเป็น
dbkk

ใช่แล้วนั่นเป็นวิธีที่ดีในการแสดงความคิดเห็นที่ล้าสมัย แต่ถึงอย่างไรก็ตามคุณจะชอบรหัสแฟนซีมากกว่ารหัสที่ยาวกว่าเสมอไหม?
gablin

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

ฟังก์ชั่นของคุณควรตั้งชื่อว่า 'strcpy' หรือเปล่า?
JBRWilkinson

มีข้อผิดพลาดในส่วนของรหัสนั้น อะไรที่ส่งคืน? สิ่งที่ควรจะคืน? เพียงแค่ทำให้เป็นโมฆะและทำได้ด้วย;)
Christian Mann

10

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

ฉันมีตัวอย่างชีวิตจริง ในผลิตภัณฑ์ของเราเรามีข้อมูลโค้ดต่อไปนี้:

if (++someCounter < MAX_VALUE) {
    // Do something that has to be done only MAX_VALUE times
}

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

if (someCounter < MAX_VALUE) {
    ++someCounter;
    // Do whatever it is we came here for
}

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

ดังนั้น - ฉันทั้งหมดเพื่อความเรียบง่ายและชัดเจน

แก้ไข:โอ้และเกี่ยวกับความคิดเห็น - มันไม่สำคัญ หลายคนจะไม่อ่านความคิดเห็น ( http://www.javaspecialists.co.za/archive/Issue039.html ) และผู้ที่จะไม่เข้าใจรหัสของคุณโดยไม่มีความคิดเห็นจะไม่เข้าใจดีพอสำหรับพวกเขา สามารถรักษาได้ เป้าหมายคือเพื่อช่วยให้ผู้คนเห็นว่ารหัสบางอย่าง "ถูกต้อง" ความคิดเห็นไม่สามารถช่วยได้


1
"ทำลายระบบการผลิตของลูกค้าของเรา" - นั่นแย่มาก: -S

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

@quant_dev: ตรงกันข้าม "การดีบักนั้นยากกว่าการเขียนรหัสสองครั้งแรกดังนั้นหากคุณเขียนรหัสอย่างชาญฉลาดที่สุดเท่าที่จะทำได้คุณจะได้คำจำกัดความไม่ฉลาดพอที่จะทำการแก้ไข" - Brian W. Kernighan
Michael Borgwardt

@MichaelBorgwardt ฉันจะไม่ใช้ Kictighan เผด็จการสุ่มสี่สุ่มห้ากับทุกสถานการณ์ที่เป็นไปได้ ตัวอย่าง OPs เป็นฟังก์ชันที่เขียนว่า "อย่างชาญฉลาดที่สุดเท่าที่จะเป็นไปได้" (หรือใกล้เคียงกับมัน) แต่ก็ควรง่ายที่จะทำการดีบักหากจำเป็นต้องทำการดีบัก ในทางกลับกันผู้ที่ใช้สมองส่วนบนที่ซับซ้อนเล็กน้อยอาจฉลาดมาก แต่จะยากที่จะทำการแก้ไข (นอกจากนี้: Kernighan ถือว่าทักษะการเขียนโปรแกรม = ทักษะการดีบักมันไม่จำเป็นต้องเป็นอย่างนั้นฉันได้ทำการดีบั๊กโค้ดเรียบร้อยแล้วฉันจะไม่สามารถเขียนได้)
quant_dev

6

โดยทั่วไปฉันต้องการเวอร์ชั่นที่ยาวกว่า เหตุผลหลักสองประการที่ควรคำนึงถึง:

  • ผู้คนจำนวนมากมีแนวโน้มที่จะเข้าใจด้วยความพยายามน้อยลง (สมมติว่าไม่ใช่ตัวอย่าง "มาตรฐาน" เช่นการคัดลอกสตริงนี้)
  • เป็นไปได้ที่จะวางเบรกพอยต์ในแต่ละงบและขั้นตอนด้วยดีบักเกอร์

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

void copy_string(char *s, char *t)
{
    *s = *t;
    while (*t != '\0') {
        t++;
        s++;
        *s = *t;
    }
}

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


1
นั่นไม่ใช่สิ่งที่อินไลน์มีไว้เพื่ออะไร?
Antsan

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

4

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

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

ความคิดเห็นไม่ควรระบุสิ่งที่ชัดเจนจากรหัสซึ่งทำให้ผู้อ่านอ่านในสิ่งเดียวกันสองครั้ง สำหรับฉันตัวอย่างแรกนั้นต้องการการชี้แจง เหตุใดนักพัฒนาจึงไม่ใช้การdo...whileวนซ้ำเพื่อลบการทำซ้ำที่*s = *t;ได้รับมอบหมาย ฉันต้องแยกวิเคราะห์รหัสเพิ่มเติมเพื่อทราบว่ากำลังใช้งานการคัดลอกสตริง ความคิดเห็นจะเป็นประโยชน์กับรหัสที่ยาวกว่านี้มากกว่ารหัสที่สั้นกว่า

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


ผู้พัฒนา (ฉัน) ไม่ได้ใช้do ... whileเพียงเพราะฉันไม่ได้คิดถึงเมื่อฉันเขียนคำถาม ขอบคุณสำหรับการชี้ให้เห็นว่า ^^ ตัวอย่างที่สองอาจชัดเจนสำหรับคุณ แต่แน่นอนว่าไม่ใช่สำหรับฉัน
gablin

4

ปัญหาคือไม่มีคำจำกัดความที่ชัดเจนshort fancy codeเนื่องจากขึ้นอยู่กับระดับของโปรแกรมเมอร์มาก ในขณะที่บางคนไม่มีปัญหาในการเข้าใจการwhile(*s++ = *t++);แสดงออก

โดยส่วนตัวฉันพบว่าwhile(*s++ = *t++);อ่านได้อย่างสมบูรณ์แบบและยิ่งอ่านยากอีกต่อไป คนอื่นอาจไม่เห็นด้วย

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


มันทำให้ฉันเศร้าที่ชุดรูปแบบของคำตอบเหล่านี้มากมายคือ "นักพัฒนาอาจไม่รู้จักวิธีมาตรฐานในการเขียน strcpy () ใน C"
AShelly

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

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

2

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

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

while (*dest++ = *source++)
    ;

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

while (*dest++ = *source++)
    ; /* no body */

บางคนชอบใช้เครื่องมือจัดฟันแทน:

while (*dest++ = *source++)
    {}

ไม่ว่าการจัดรูปแบบที่คุณต้องการจะเกิดข้อผิดพลาดมากพอกับสิ่งต่าง ๆ เช่น:

if (x>y);
     x = z;

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


ใช่ในการหวนกลับของตัวอย่างนี้รุ่น 'แฟนซี' น่าจะอ่านได้มากกว่ารุ่นที่ยาวกว่า หากฉันนึกถึงตัวอย่างที่ดีกว่านี้ฉันก็อยากจะทำ แต่ตอนนี้ฉันไม่สามารถทำได้ แต่ฉันไม่ได้ "ออกนอกเส้นทางของฉัน" ในการทำให้มันนานขึ้น - นั่นคือวิธีที่ฉันจะเขียนสำเนาสตริงอย่างน้อยในตอนแรก มันอาจไม่ใช่วิธีที่ดีที่สุด แต่ไม่ได้ทำเกินความจำเป็นโดยเจตนา
gablin

2
Keep it simple, stupid!

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

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

ฉันมักจะอ้างถึงZen of Pythonในสถานการณ์เหล่านี้ โดยเฉพาะอย่างยิ่ง:

Explicit is better than implicit.
Simple is better than complex.

1

อย่าเขียนรหัสแฟนซีในซอฟต์แวร์การผลิต ฉันรู้ว่ารู้สึกดีที่สามารถเขียนได้จริงและสั้นกว่า การเขียนรหัสแฟนซีเพิ่มค่า "WTF / นาที" อย่างมีนัยสำคัญกล่าวคือลดคุณภาพ

ข้อความแสดงแทน


1

แน่นอนมันทั้งหมดขึ้นอยู่กับสถานการณ์ แต่โดยทั่วไปแล้วฉันคิดว่านี่เป็นกฎง่ายๆ:

การแก้จุดบกพร่องนั้นยากกว่าการเขียนรหัสสองเท่า ดังนั้นหากคุณเขียนรหัสอย่างชาญฉลาดที่สุดเท่าที่จะเป็นไปได้คุณจะไม่ฉลาดพอที่จะทำการดีบัก

~ Brian Kernighan


อีกต่อไป แต่มีความหมายสง่างามยิ่งกว่าตัวย่อKISS ที่มีขนาดกะทัดรัดขึ้น~ หวังว่าประชดประชันจะไม่สูญหายไป p
violet313

0

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


0

ฉันเริ่มไม่เชื่อความคิดเห็น บ่อยครั้งที่ความคิดเห็นไม่ได้รับการอัพเดตเมื่อรหัสได้รับการอัปเดตและล้าสมัยอย่างมากหรือแสดงกฎจากลูกค้า / การจัดการที่ไม่เกี่ยวข้องกันอีกต่อไป

มีบางประเด็นที่ความคิดเห็นไม่ตรงกับรหัสที่พวกเขากำลังอธิบายอีกต่อไป

ถ้าฉันต้องเลือกระหว่างสั้น / แฟนซีและยาว / ง่ายกว่าที่จะเข้าใจแล้วฉันมักจะเลือกหลังเว้นแต่มีเหตุผลที่ดีจริงๆ


0

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

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