การสร้างใหม่: มันไม่ใช่แค่คำแฟนซีสำหรับล้างรหัสของคุณใช่ไหม [ปิด]


21

ก่อนที่หนังสือของมาร์ตินฟาวเลอร์ "Refactoring: การปรับปรุงการออกแบบของรหัสที่มีอยู่" ออกมาเราเคยเรียกการเปลี่ยนแปลงครั้งใหญ่กับรหัส IMO เทคนิคการเปลี่ยนโฉมใหม่เป็นสามัญสำนึก / สิ่งที่เห็นได้ชัดที่เราทำตลอดไป

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


เมื่อคุณพูดว่า "ก่อนที่หนังสือจะออกมา" ฉันคิดว่าคุณหมายถึงหนังสือของ Martin Folwer ว่าถูกต้องหรือไม่?
AlexC

-1: ประโยชน์ของคำถามนี้คืออะไร
Jim G.

ใช่หนังสือของฟาวเลอร์
Chuck Stephanski

คำตอบ:


43

Refactoring เก่ากว่าเนินเขาดังนั้นไม่เลยไม่มีอะไรใหม่

และการทำให้ใหม่ไม่ได้ทำความสะอาด เป็นไปได้ แต่ไม่ จำกัด เพียงการทำความสะอาด

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

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

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

สิ่งนี้กล่าวว่าไม่ว่าการเปลี่ยนแปลงใดที่เกิดขึ้นกับโค้ดหนึ่งควรรันการทดสอบของเขาเสมอ ... ในกรณี


1
Newtopian: นั่นเป็นจุดสำคัญ หากคุณไม่ได้เรียกใช้การทดสอบของคุณคุณมีความคิดว่าคุณสุ่ม hacked อะไรหรือจริงrefactored (และแน่นอนคุณต้องมีชุดทดสอบที่เพียงพอ!)
Frank Shearar

9

มันเป็นเพียงการจัดรหัสให้เรียบร้อย โดยพื้นฐานแล้วโปรแกรมเมอร์ (โดยเฉพาะมาร์ตินฟาวเลอร์) สังเกตว่าพวกเขามีแนวโน้มที่จะทำงานแบบเดียวกันทุกครั้งที่พวกเขาจัดระเบียบโค้ด พวกเขากำหนดและระบุวิธีการจัดระเบียบและปัญหาเกี่ยวกับรหัสและ presto! การสร้างใหม่เกิดขึ้น

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

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


วิลเลียม Opdyke 1992: Refactoring กรอบเชิงวัตถุ นักล่า & เบ็ค & เพื่อนนิยมการสร้างใหม่ Jon Brant และ Don Roberts ใช้เครื่องมืออัตโนมัติครั้งแรกก่อนปี 1999 ดังนั้น "ศัพท์แสงชุดใหม่" จึงไม่แม่นยำมาก
Frank Shearar

หากคุณยอมรับว่าการเขียนโปรแกรมคอมพิวเตอร์เกิดขึ้นตั้งแต่ Ada Lovelace ในช่วงกลางปี ​​1800 ใช่แล้ว - มันเป็นชุดคำศัพท์ใหม่ที่ค่อนข้างใหม่
Ant

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

อันที่จริงฉันพูดถึงว่ามันเป็นศัพท์แสงใหม่ ... เทียบกับแคลคูลัสแลมบ์ดา
Frank Shearar

@ ชัคคุณอ่านหนังสือ Refactoring แล้วเหรอ? ถ้าเป็นเช่นนั้นฉันจะแปลกใจมากถ้าคุณไม่ได้เรียนรู้สิ่งใหม่จากมัน
Marcie

7

เราทำสามสิ่งที่แตกต่างกันใน บริษัท ของเราด้วยเวลาที่จัดสรรสำหรับทั้งสาม:

  • การปรับโครงสร้างใหม่:ประกอบด้วยการเปลี่ยนแปลงโครงสร้างรหัสดังนั้นการรักษาพฤติกรรม

ตัวอย่าง: แบ่งวิธี 100 บรรทัดที่น่าเกลียดและอ่านไม่ได้ซึ่งทำสี่สิ่งออกเป็นสี่วิธีที่สามารถนำกลับมาใช้ใหม่ได้ 25 บรรทัดในแต่ละวิธี

  • การล้างข้อมูล:ประกอบด้วยการทำการแก้ไขเล็กน้อยเพื่อให้โค้ดอ่านได้ง่ายขึ้นโดยไม่แก้ไขพฤติกรรมหรือโครงสร้างของมัน

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

  • การบังคับใช้กฎ StyleCop / FxCop:ประกอบด้วยการตรวจสอบว่ารหัสตรงกับชุดเริ่มต้นของกฎ StyleCop หรือ FxCop หรือไม่หากไม่ให้แก้ไขเพื่อให้ตรงกับกฎเหล่านั้น

ตัวอย่าง: การเพิ่มCulture.Invariantในstring.Format(หรือวัฒนธรรมอื่นที่มีความเหมาะสมมากขึ้น)

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


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

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

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

3

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

มันเป็นขั้นตอนมากมาย - เล็กและใหญ่ - หวังว่าจะส่งผลให้โปรแกรมดีขึ้น


3

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

"ล้างข้อมูล" อาจหมายถึงอะไรก็ได้ตั้งแต่ "ฟอร์แมตใหม่" ไปจนถึง "เขียนใหม่ชิ้นใหญ่ ๆ "

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

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


2

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


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

@ user21007: รหัสมีความซับซ้อนมากกว่าสคีมาฐานข้อมูล แต่สามารถเปลี่ยนแปลงและปรับใช้ได้ง่ายกว่ามาก
kevin cline

1

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

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

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


0

Refactoring เป็นโค้ด "cleanup" แต่ยังทำการปรับโครงสร้างโค้ด ในการปรับโครงสร้างทีมของฉันมักจะเป็นหลัง เมื่อเรามีกรณี "refactor" เราจัดสรรเวลาในการปรับโครงสร้างโค้ดของเราเช่นเพื่อให้สอดคล้องกับสถาปัตยกรรมใหม่หรือโมเดลข้อมูลหรือเพื่อให้มีประสิทธิภาพมากขึ้น

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


1
การเปลี่ยนชื่อเป็นเทคนิคการรีแฟคเตอร์มาตรฐาน!
Chuck Stephanski

0

ฉันว่าไม่

อาจมีการล้างข้อมูลในกระบวนการ refactoring แต่ไม่ใช่สาระสำคัญ

การล้างข้อมูลมาพร้อมกับสมมติฐานว่ารหัสก่อนหน้าไม่สะอาด ในความเป็นจริงนักพัฒนา refactor รหัสของพวกเขาแม้รหัสเดิมสะอาดอยู่แล้ว

DRYเป็นไดรฟ์หลักที่อยู่เบื้องหลังการปรับโครงสร้างใหม่

เมื่อเพิ่มรหัสใหม่ลงในฐานรหัสที่มีอยู่การปรับโครงสร้างจะทำตามธรรมชาติเนื่องจากหลักการของ DRY

แค่ 0.02 ของฉัน


0

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


0

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


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

0

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


-1

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


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

ฉันไม่ได้บอกว่ามันเป็นสิ่งที่ดี / ปลอดภัย / เป็นที่ต้องการในการเปลี่ยนแปลงครั้งใหญ่โดยไม่มีวินัยในการทดสอบ ฉันกำลังบอกว่าการเปลี่ยนโครงสร้างเป็นวิธีการเกี่ยวข้องกับการทดสอบวินัยในขณะที่ "ทำความสะอาดสิ่งต่าง ๆ " ไม่ใช่วิธีการเลย
Willie Wheeler

ดังนั้นถ้าฉันเข้าใจอย่างถูกต้องหากมันไม่มีชื่อแฟนซีที่เหมาะสมเราก็ควรไป !! : -O ล้อเล่นฉันเห็นสิ่งที่คุณกำลังพยายามที่จะพูดความจริงที่ว่าสิ่งที่ทำความสะอาดขึ้นอาจแทบจะไม่ได้เรียกว่าวิธีการ แต่แล้วอีกครั้งไม่ได้ refactoring เป็นเพียงคำแฟนซีที่ระบุว่าคุณกำลังเปลี่ยนรหัสโดยไม่เปลี่ยนพฤติกรรมของมัน มันไม่ได้มีความเกี่ยวข้องกับการทดสอบ แต่อย่างใด การปฏิบัติตามการเข้ารหัสที่ดีจะระบุว่าหากรหัสเปลี่ยนไปควรทดสอบว่าไม่ว่าคุณจะเรียกการกระทำที่สร้างการเปลี่ยนแปลงอย่างไร
Newtopian

1
แน่นอนว่าฉันสามารถซื้อได้ วินัยในการทดสอบเป็นมากกว่าเกี่ยวกับวิธีการดำเนินการ refactoring แต่ไม่ได้อยู่ในคำจำกัดความ (เช่นฉันสามารถสร้างรหัสใหม่ได้โดยไม่ต้องทำการทดสอบใด ๆ เลย) ฉันไม่รู้ว่าฉันสามารถยอมรับได้หรือไม่ว่าการปรับโครงสร้างนั้นไม่ใช่วิธีการ - มีหนังสือทั้งเล่มที่เขียนด้วยรูปแบบทีละขั้นตอนเป็นต้น
Willie Wheeler

-1

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


-2

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

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