คำถามติดแท็ก maintainability

ด้านคุณภาพของระบบแสดงให้เห็นถึงความง่ายในการบำรุงรักษาซอฟต์แวร์

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

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

20
การแทนที่รหัสที่ได้รับการปรับปรุงด้วยรหัสที่อ่านได้นั้นเป็นเรื่องปกติหรือไม่
บางครั้งคุณพบสถานการณ์ที่คุณต้องขยาย / ปรับปรุงบางรหัสที่มีอยู่ คุณจะเห็นว่ารหัสเก่านั้นผอมมาก แต่มันก็ยากที่จะขยายและใช้เวลาในการอ่าน มันเป็นความคิดที่ดีที่จะแทนที่ด้วยรหัสที่ทันสมัย? ก่อนหน้านี้ฉันชอบวิธีการแบบลีน แต่ตอนนี้ฉันคิดว่ามันจะดีกว่าถ้าเสียสละจำนวนมากเพื่อเพิ่มประสิทธิภาพให้กับ abstractions ที่สูงขึ้นอินเตอร์เฟสที่ดีขึ้นและโค้ดที่อ่านได้และสามารถขยายได้มากขึ้น คอมไพเลอร์ดูเหมือนว่าจะเริ่มดีขึ้นเช่นกันดังนั้นสิ่งต่าง ๆ เช่นstruct abc = {}กลายเป็นเงียบ ๆmemset, shared_ptrs ค่อนข้างผลิตรหัสเดียวกันเป็นตัวชี้ดิบ twiddling, แม่แบบทำงานได้ดีสุดเพราะพวกเขาผลิตรหัสซุปเปอร์ลีนและอื่น ๆ แต่ถึงกระนั้นบางครั้งคุณก็เห็นอาร์เรย์ตามสแต็กและฟังก์ชั่น C เก่าที่มีตรรกะคลุมเครือและมักจะไม่ได้อยู่ในเส้นทางที่สำคัญ เป็นความคิดที่ดีที่จะเปลี่ยนรหัสดังกล่าวถ้าคุณต้องสัมผัสชิ้นส่วนเล็ก ๆ ด้วยวิธีใด?

17
แนวทางการเข้ารหัส: วิธีการไม่ควรมีคำสั่งมากกว่า 7 รายการ?
ฉันกำลังดูแนวทางการเข้ารหัสของ AvSolสำหรับ C # และฉันเห็นด้วยกับเกือบทุกอย่าง แต่ฉันอยากรู้จริง ๆ ว่าคนอื่นคิดอย่างไรกับกฎข้อหนึ่ง AV1500 วิธีการไม่ควรเกิน 7 ข้อความวิธีที่ต้องใช้มากกว่า 7 ข้อความกำลังทำมากเกินไปหรือมีความรับผิดชอบมากเกินไป นอกจากนี้ยังต้องใช้ความคิดของมนุษย์ในการวิเคราะห์งบที่แน่นอนเพื่อให้เข้าใจว่ารหัสกำลังทำอะไรอยู่ ทำลายมันลงในวิธีการเล็ก ๆ และเน้นด้วยชื่ออธิบายตนเอง คุณส่วนใหญ่ปฏิบัติตามกฎนี้หรือไม่? แม้ว่าจะมีน้อยที่จะถูกบันทึกจากการสร้างวิธีการใหม่ (รหัสของคุณยังคงแห้ง ) นอกเหนือจากการอ่านที่เพิ่มขึ้นอย่างมาก? และหมายเลขของคุณยังต่ำอยู่ที่ 7 หรือไม่ ฉันจะมีแนวโน้มมากขึ้นต่อ 10 ฉันไม่ได้บอกว่าฉันละเมิดกฎนี้ทั่วทุกที่ - ในทางตรงกันข้ามวิธีการของฉันเล็ก 95% และเน้น แต่บอกว่าคุณไม่ควรละเมิดกฎนี้จริงๆพัดฉันออกไป ฉันแค่อยากจะรู้ว่าสิ่งที่ทุกคนคิดว่าไม่เคยละเมิดกฎนี้ (มัน '1' ในมาตรฐานการเข้ารหัส - ความหมายไม่เคยทำเช่นนี้) แต่ฉันคิดว่าคุณมีปัญหาในการค้นหา codebase ที่ไม่ได้

10
ทำความสะอาดโค้ดที่อ่านได้และโค้ดที่อ่านยากอย่างรวดเร็ว เมื่อไหร่ที่จะข้ามเส้น?
เมื่อฉันเขียนรหัสฉันพยายามทำให้รหัสของฉันสะอาดและอ่านง่ายที่สุดเท่าที่จะทำได้ ทุกครั้งที่มีเวลาที่คุณต้องข้ามเส้นและไปจากรหัสที่สะอาดดีเป็นรหัสที่น่าเกลียดเล็กน้อยเพื่อให้เร็วขึ้น เมื่อไรที่จะข้ามเส้นนั้น?

9
ความสามารถในการอ่านและการบำรุงรักษากรณีพิเศษของการเขียนการเรียกฟังก์ชันที่ซ้อนกัน
รูปแบบการเข้ารหัสของฉันสำหรับการเรียกใช้ฟังก์ชันที่ซ้อนกันมีดังต่อไปนี้: var result_h1 = H1(b1); var result_h2 = H2(b2); var result_g1 = G1(result_h1, result_h2); var result_g2 = G2(c1); var a = F(result_g1, result_g2); ฉันเพิ่งเปลี่ยนเป็นแผนกที่ใช้รูปแบบการเข้ารหัสต่อไปนี้เป็นอย่างมาก: var a = F(G1(H1(b1), H2(b2)), G2(c1)); ผลของวิธีการเข้ารหัสของฉันคือในกรณีที่ฟังก์ชั่นการขัดข้อง Visual Studio สามารถเปิดการถ่ายโอนข้อมูลที่สอดคล้องกันและระบุบรรทัดที่ปัญหาเกิดขึ้น (ฉันกังวลเป็นพิเศษเกี่ยวกับการละเมิดการเข้าถึง) ฉันกลัวว่าในกรณีที่เกิดความผิดพลาดเนื่องจากปัญหาเดียวกันที่ตั้งโปรแกรมไว้ในวิธีแรกฉันจะไม่สามารถรู้ได้ว่าฟังก์ชันใดที่ทำให้เกิดความผิดพลาด ในทางกลับกันยิ่งการประมวลผลที่คุณใส่ในบรรทัดมากขึ้นเท่าไรคุณก็ยิ่งได้รับตรรกะมากขึ้นในหน้าเดียวซึ่งจะช่วยเพิ่มความสามารถในการอ่าน ความกลัวของฉันถูกต้องหรือฉันขาดอะไรไปและโดยทั่วไปแล้วสิ่งใดที่เป็นที่นิยมในสภาพแวดล้อมเชิงพาณิชย์ อ่านหรือบำรุงรักษา? ฉันไม่รู้ว่ามันเกี่ยวข้องหรือไม่ แต่เรากำลังทำงานใน C ++ (STL) / C #

10
การแยกแอพพลิเคชั่นแบบเสาหินออกเป็นส่วนเล็ก ๆ หลาย ๆ อันช่วยป้องกันข้อผิดพลาดหรือไม่? [ปิด]
วิธีการถามนี้ก็คือ; ทำไมโปรแกรมมีแนวโน้มที่จะเป็นเสาหิน? ฉันกำลังคิดถึงบางสิ่งเช่นแพ็คเกจอนิเมชั่นเช่น Maya ซึ่งผู้คนใช้สำหรับเวิร์กโฟลว์ต่างๆ หากความสามารถในการเคลื่อนไหวและการสร้างแบบจำลองถูกแบ่งออกเป็นแอปพลิเคชันแยกต่างหากของตนเองและพัฒนาแยกต่างหากเมื่อไฟล์ถูกส่งผ่านระหว่างกันจะไม่สามารถดูแลรักษาได้ง่ายขึ้นหรือไม่

10
ตัวแปรสถานะเป็นความชั่วร้ายหรือไม่? [ปิด]
ตัวแปรสถานะเป็นสิ่งชั่วร้ายหรือไม่? ตัวแปรต่อไปนี้มีความผิดศีลธรรมอย่างลึกซึ้งและเป็นสิ่งที่ชั่วร้ายหรือไม่ที่จะใช้มัน "ตัวแปรบูลีนหรือเลขจำนวนเต็มที่คุณกำหนดค่าในบางสถานที่จากนั้นลงมาด้านล่างคุณตรวจสอบแล้วใน orther ที่จะทำอะไรหรือไม่เช่นเช่นใช้newItem = trueแล้วบางบรรทัดด้านล่างif (newItem ) then" ฉันจำได้ว่าทำสองโครงการที่ฉันละเลยการใช้ค่าสถานะทั้งหมดและจบลงด้วยสถาปัตยกรรม / รหัสที่ดีขึ้น อย่างไรก็ตามมันเป็นวิธีปฏิบัติทั่วไปในโครงการอื่น ๆ ที่ฉันทำงานด้วยและเมื่อโค้ดเติบโตและมีการเพิ่มแฟล็ก IMHO code-spaghetti ก็จะโตขึ้นเช่นกัน คุณจะบอกว่ามีกรณีใดบ้างที่การใช้ค่าสถานะเป็นวิธีปฏิบัติที่ดีหรือจำเป็นหรือไม่หรือคุณยอมรับว่าการใช้ค่าสถานะในรหัสคือ ... ค่าสถานะสีแดงและควรหลีกเลี่ยง / refactored ฉันฉันเพิ่งได้รับโดยการทำฟังก์ชั่น / วิธีการที่ตรวจสอบสถานะในเวลาจริงแทน

10
กำจัดหมายเลขเวทย์มนตร์: เมื่อไหร่เวลาที่จะพูดว่า "ไม่"
เราทุกคนทราบดีว่าเลขอาถรรพ์ (ค่าฮาร์ดโค้ด) สามารถสร้างความหายนะในโปรแกรมของคุณโดยเฉพาะเมื่อถึงเวลาที่จะแก้ไขส่วนของรหัสที่ไม่มีความคิดเห็น แต่คุณจะวาดเส้นที่ไหน ตัวอย่างเช่นหากคุณมีฟังก์ชันที่คำนวณจำนวนวินาทีระหว่างสองวันคุณจะเปลี่ยนหรือไม่ seconds = num_days * 24 * 60 * 60 กับ seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE ณ จุดใดที่คุณตัดสินใจว่ามันชัดเจนอย่างชัดเจนว่าค่าฮาร์ดโค้ดมีความหมายอย่างไรและทิ้งไว้คนเดียว?

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

4
การใช้งานสายอักขระ / หมายเลข [ปิด]
นี่เป็นหัวข้อที่ถกเถียงกันอยู่และฉันคิดว่ามีความคิดเห็นมากเท่ากับที่มีโปรแกรมเมอร์ แต่เพื่อประโยชน์ของมันฉันต้องการทราบว่าการปฏิบัติทั่วไปในธุรกิจคืออะไร (หรือในที่ทำงานของคุณ) ในที่ทำงานของฉันเรามีแนวทางการเข้ารหัสที่เข้มงวด ส่วนหนึ่งของนั้นจะทุ่มเทให้กับสายอักขระ / หมายเลข มันระบุ (สำหรับ C #): อย่าใช้ค่าตัวอักษรตัวเลขหรือสตริงในรหัสของคุณนอกเหนือจากการกำหนดค่าคงที่เชิงสัญลักษณ์ ใช้รูปแบบต่อไปนี้เพื่อกำหนดค่าคงที่: public class Whatever { public static readonly Color PapayaWhip = new Color(0xFFEFD5); public const int MaxNumberOfWheels = 18; } มีข้อยกเว้นคือค่า 0, 1 และ null สามารถใช้ได้อย่างปลอดภัยเกือบตลอดเวลา บ่อยครั้งที่ค่า 2 และ -1 ก็โอเคเช่นกัน สตริงที่มีไว้สำหรับการบันทึกหรือการติดตามได้รับการยกเว้นจากกฎนี้ อนุญาตให้ใช้ตัวอักษรเมื่อความหมายชัดเจนจากบริบทและไม่ขึ้นอยู่กับการเปลี่ยนแปลงในอนาคต mean = (a + b) …

10
เทมเพลต“ metaprogramming” ใน Java เป็นความคิดที่ดีหรือไม่
มีไฟล์ต้นฉบับในโครงการที่ค่อนข้างใหญ่ที่มีฟังก์ชั่นหลายอย่างที่มีความอ่อนไหวต่อประสิทธิภาพอย่างมาก (เรียกว่าล้านครั้งต่อวินาที) ในความเป็นจริงผู้ดูแลก่อนหน้านี้ตัดสินใจที่จะเขียน 12 สำเนาของฟังก์ชั่นที่แตกต่างกันเล็กน้อยมากเพื่อประหยัดเวลาที่จะใช้ในการตรวจสอบเงื่อนไขในฟังก์ชั่นเดียว น่าเสียดายที่นี่หมายถึงรหัสนี้เป็น PITA ที่จะรักษาไว้ ฉันต้องการลบรหัสที่ซ้ำกันทั้งหมดและเขียนเพียงหนึ่งเทมเพลต อย่างไรก็ตามภาษา, Java, ไม่รองรับเทมเพลตและฉันไม่แน่ใจว่ายาชื่อสามัญเหมาะสำหรับสิ่งนี้ แผนปัจจุบันของฉันคือการเขียนแทนไฟล์ที่สร้างฟังก์ชั่น 12 ชุด (ตัวขยายเทมเพลตแบบใช้ครั้งเดียวเท่านั้นจริง) แน่นอนฉันจะให้คำอธิบายมากมายว่าทำไมไฟล์จะต้องถูกสร้างขึ้นโดยทางโปรแกรม ความกังวลของฉันคือสิ่งนี้จะนำไปสู่ความสับสนของผู้ดูแลในอนาคตและอาจแนะนำข้อผิดพลาดที่น่ารังเกียจหากพวกเขาลืมสร้างไฟล์ใหม่หลังจากแก้ไขมันหรือ (ยิ่งแย่กว่า) หากพวกเขาแก้ไขไฟล์ที่สร้างโดยทางโปรแกรม โชคไม่ดีที่การเขียนใหม่ทั้งหมดใน C ++ ฉันไม่เห็นวิธีการแก้ไข ประโยชน์ของวิธีนี้มีมากกว่าข้อเสียหรือไม่ ฉันควรจะแทน: เพิ่มประสิทธิภาพการทำงานและใช้ฟังก์ชั่นเดียวที่บำรุงรักษาได้ เพิ่มคำอธิบายว่าทำไมฟังก์ชันจึงต้องทำซ้ำ 12 ครั้งและรับภาระการบำรุงรักษาอย่างสง่างาม พยายามใช้ข้อมูลทั่วไปเป็นเทมเพลต (อาจใช้ไม่ได้ผล) ตะโกนใส่ผู้ดูแลเก่าเพื่อสร้างรหัสขึ้นอยู่กับประสิทธิภาพในฟังก์ชั่นเดียว วิธีอื่นในการรักษาประสิทธิภาพและการบำรุงรักษา? ป.ล. เนื่องจากการออกแบบของโครงการไม่ดีการทำโปรไฟล์ฟังก์ชั่นค่อนข้างยุ่งยาก ... อย่างไรก็ตามผู้ดูแลคนก่อนทำให้ฉันมั่นใจว่าประสิทธิภาพในการทำงานนั้นไม่เป็นที่ยอมรับ ฉันคิดว่าเขามีความหมายมากกว่า 5% แม้ว่าจะเป็นการคาดเดาที่สมบูรณ์ในส่วนของฉัน บางทีฉันควรทำอย่างละเอียดหน่อย สำเนา 12 ชุดทำหน้าที่คล้ายกันมาก แต่มีความแตกต่างเล็กน้อย ความแตกต่างอยู่ในสถานที่ต่าง ๆ ตลอดทั้งฟังก์ชั่นดังนั้นน่าเสียดายที่มีข้อความมากมายที่มีเงื่อนไข การทำงานมีประสิทธิภาพ …

11
การบำรุงรักษาอย่างชาญฉลาดมีอีกอย่างหนึ่งที่ไม่ต้องใช้เครื่องมือจัดฟันที่ปลอดภัยหรือไม่?
คือelse whileโดยไม่แทรกแซงการพิจารณาการจัดฟัน "ปลอดภัย" การบำรุงรักษาที่ชาญฉลาด? กำลังเขียนif-elseโค้ดโดยไม่มีเครื่องหมายวงเล็บด้านล่าง ... if (blah) foo(); else bar(); ... มีความเสี่ยงเนื่องจากการขาดเครื่องหมายปีกกาทำให้ง่ายต่อการเปลี่ยนความหมายของรหัสโดยไม่ตั้งใจ อย่างไรก็ตามด้านล่างมีความเสี่ยงหรือไม่ if (blah) { ... } else while (!bloop()) { bar(); } หรือelse whileโดยไม่ต้องจัดฟันจัดฟันถือว่า "ปลอดภัย"?

4
ปลั๊กอินควรใช้อะไร: hooks เหตุการณ์หรืออย่างอื่น?
พิจารณาแอพที่อนุญาตให้ปลั๊กอินตอบสนองต่อโฟลว์ของโปรแกรม ฉันรู้ 2 วิธีในการบรรลุเป้าหมายนี้: hooksและevents 1. ตะขอ ใช้การเรียกฟังก์ชั่นที่ว่างเปล่าภายในผังโปรแกรมหลัก ฟังก์ชั่นเหล่านี้สามารถแทนที่ได้ด้วยปลั๊กอิน ตัวอย่างเช่น Drupal CMS ใช้ฮุคที่พร้อมใช้งานกับโมดูลและธีม นี่คือตัวอย่างของวิธีการใช้เบ็ดในฟังก์ชั่นfile_copy function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS_RENAME) { // ... [File copying routine] // Inform modules that the file has been copied. module_invoke_all('file_copy', $file, $source); return $file; // ... } โมดูลสามารถใช้modulename_file_copy($file, $source)ฟังก์ชั่นซึ่งจะถูกเรียกโดยในmodule_invoke_all file_copyหลังจากฟังก์ชั่นนี้เสร็จสิ้นการfile_copyดำเนินการจะดำเนินการต่อ 2. …

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

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