จะทราบได้อย่างไรว่าต้องทำอะไรในการออกแบบเชิงวัตถุ


12

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

ฉันเรียนรู้ที่จะเขียนโปรแกรมในปี 2009 เมื่อฉันเรียน PHP ต่อมาในปี 2012 ฉันย้ายไปที่ C # และ. NET อย่างไรก็ตามการเขียนโค้ดไม่ใช่ปัญหาการจดอัลกอริธึมไม่ใช่ปัญหาของฉัน ปัญหาที่เกิดขึ้นจริงของฉันคือการรู้ว่าสิ่งที่จะต้องมีการเข้ารหัสเพื่อให้บรรลุความต้องการและการที่มันจะต้องมีการเข้ารหัส

หลักสูตรส่วนใหญ่ที่มีอยู่บนเว็บจัดการวิธีการเขียนโค้ดในภาษาบางภาษาวิธีใช้ API บางชุด ฯลฯ นั่นไม่ใช่ประเด็นของฉันที่นี่

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

แต่เมื่อพูดถึงการฝึกฝนฉันรู้สึกว่าฉันเป็นหายนะ เมื่อไม่นานมานี้ฉันจำเป็นต้องพัฒนาระบบการเงินที่ถูกพัฒนาโดยบุคคลอื่นต่อไป มันเป็น "ระบบเก่า" ที่พัฒนาด้วย C # และ WinForms นี่เป็นครั้งแรกที่ฉันเลือกโครงการที่มีความซับซ้อนของโดเมนจริงด้วยกฎเกณฑ์ทางธุรกิจมากมายและอื่น ๆ

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

เกือบทุกครั้งที่มีความคิดค่อนข้างมากฉันจะจบลงด้วยความคิดบางอย่าง แต่ฉันไม่เคยรู้วิธีที่จะตัดสินว่าความคิดของฉันถูกต้องหรือไม่

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

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

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


1. คุณเข้าใจแนวคิดพื้นฐานทั้งหมดของ C # อยู่แล้วรวมถึงสิ่งต่าง ๆ เช่นความแตกต่างระหว่างตัวดำเนินการ overloading กับตัวดำเนินการ overriding คลาสนามธรรมคืออะไรและแตกต่างจากส่วนต่อประสาน encapsulation polymorphism ฯลฯ หรือไม่? การรู้สิ่งเหล่านี้ก่อนเป็นสิ่งสำคัญที่จะต้องเข้าใจอย่างถ่องแท้ OO ใน C # ดูc-sharpcorner.com/technologies/oop-ood
Robert Harvey

2
2. แอปพลิเคชันที่เก่ากว่าที่เขียนใน Winforms มีแนวโน้มที่จะกลายเป็นโคลนก้อนใหญ่เว้นแต่ว่ามันได้รับการออกแบบอย่างเหมาะสม การแยกความกังวลกลายเป็นเรื่องสำคัญยิ่ง ดูwinformsmvp.codeplex.com
Robert Harvey

1
ไม่มีกระบวนการจริงๆ การออกแบบส่วนใหญ่เกี่ยวกับการรู้วิธีจัดระเบียบซึ่งมาพร้อมกับประสบการณ์ หลักการโซลิดเป็นการเริ่มต้นที่ดี แต่ยังไม่เพียงพอและผู้คนมักจะหลงทางในโซลิดและลืมว่าทำไมหลักการมีอยู่
Robert Harvey

1
เริ่มต้นเล็กน้อย ข้อกำหนดคือปัญหา ปัญหาหนึ่งอาจมีขนาดใหญ่เท่ากับ "เราต้องการพัฒนาไซต์ Stackexchange ถัดไป" หรือเพียงเล็กน้อย "เราต้องการให้ Stackexchange ถัดไปของเรามีการเข้าสู่ระบบ" เปลี่ยนปัญหาใหญ่ให้เล็ก แต่ใหญ่ โดยรวมแล้วให้โอกาสตัวเองในการทำสิ่งที่ "ผิด" เป็นอันดับแรกและปรับปรุงตลอดเวลา
Laiv

1
ฉันต้องการที่จะไปพร้อม ๆ กันและ upvote VTC นี้ ...
svidgen

คำตอบ:


21

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

ก่อนอื่นหยุดคิดถึงการออกแบบเชิงวัตถุให้ถูกต้อง นั่นเหมือนกับการคิดว่าภาษาอังกฤษนั้นถูกต้อง

กระบวนทัศน์เชิงวัตถุไม่ถูกต้อง มันมีข้อดีและข้อเสียบางอย่าง มันเป็นอุดมคติ มันไม่ใช่ของเราคนเดียว มันดีกว่าไม่มีอะไร แต่มันไม่ใช่ทุกอย่างแน่นอน

ฉันได้รับการเข้ารหัสมานานหลายทศวรรษแล้ว ฉันได้ศึกษาสิ่งนี้มานานตราบเท่าที่ความคิดมันมีอยู่ ฉันยังคงเรียนรู้ความหมายของมัน ผู้เชี่ยวชาญยังคงเรียนรู้ว่ามันหมายถึงอะไร เขตทั้งหมดของเรามีอายุน้อยกว่า 100 ปี

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

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

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

อ่าน. คำถาม. ทดสอบ. ทำซ้ำ

เมื่อคุณไปถึงจุดที่คุณทำมาแล้ว 80% ของชีวิตคุณจะสับสนเหมือนฉัน

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

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


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

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

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

1

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

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

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

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

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

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

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

หากรหัสของคุณครอบคลุมโดยการทดสอบอัตโนมัติคุณสามารถผ่อนคลายเกี่ยวกับการออกแบบและโครงสร้างโดยรู้ว่าคุณครอบคลุมการทดสอบเหล่านั้น คุณสามารถ refactor อย่างจริงจังในภายหลังหรือแม้แต่ทิ้งมันและเริ่มต้นใหม่อีกครั้ง

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

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


1
เราไม่เคยมีเวลาที่จะเขียนการทดสอบอัตโนมัติ แต่เรามักจะมีเวลาทำการทดสอบด้วยตนเองซ้ำแล้วซ้ำอีก ...
Nick Keighley

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

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

1
@ BenCottrell - ฉันเห็นด้วยอย่างยิ่ง รหัสจะต้องมีการทบทวนอีกครั้ง อย่างไรก็ตามเนื่องจากการทำเช่นนี้ทำให้ผู้คนชี้ไปที่ใช้เวลาในการทำ "การออกแบบล่วงหน้า" และเขียน "รหัสที่สะอาด" เป็นความล้มเหลวบางประเภท พวกเขาใช้มนต์ "ในเวลา" และ "ในงบประมาณ" มนต์เพื่อปรับรหัส / การออกแบบที่ไม่ดี คุณสามารถใช้ "แนวทางปฏิบัติ" ทั้งหมดที่คุณต้องการ แต่จะไม่ซื้อ "รหัสที่ง่ายและราคาถูกในการเปลี่ยนแปลง" โดยไม่ต้องมีการออกแบบที่ดีและค่อนข้าง "รหัสสะอาด" เพื่อเริ่มต้นด้วย การออกแบบที่ดีและ "รหัสที่สะอาด" จะมีผลพลอยได้จากการบรรลุเป้าหมายตามเวลาและตามงบประมาณจริง
Dunk

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