คุณควรใช้รูปแบบซิงเกิลตันแทนคลาสแบบคงที่เมื่อใด [ปิด]


85

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


คลาส java ไม่สามารถสร้างแบบคงที่ได้เว้นแต่จะเป็นคลาสภายใน
Harshana

1
หากสถานะของวัตถุมีความสำคัญให้ใช้ซิงเกิลตันมิฉะนั้นให้ใช้คลาสคงที่
Levent Divilioglu

คำตอบ:


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

2
ดูที่ลิงค์นี้ด้วย: codeofdoom.com/wordpress/2008/04/20/…
Amit

4
"Singletons can be lazy loaded" - ใน C # ตัวสร้างแบบคงที่จะถูกเรียกในครั้งแรกที่มีการอ้างอิงสมาชิกแบบคงที่เท่านั้น ใน PHP คุณสามารถใช้การโหลดอัตโนมัติเพื่อให้เรียกใช้งานได้ในครั้งแรกที่ใช้คลาสเท่านั้น
mpen

การเริ่มต้นคงที่ของ Java นั้นขี้เกียจเช่นกัน ไม่มีคะแนนสำหรับ Singletons ที่นั่น
MikeFHay

13

แล้ว "หลีกเลี่ยงทั้งสอง" ล่ะ? Singletons และคลาสแบบคงที่:

  • อาจแนะนำรัฐทั่วโลก
  • จับคู่กับคลาสอื่น ๆ อย่างแน่นหนา
  • ซ่อนการอ้างอิง
  • สามารถทำให้ชั้นเรียนการทดสอบหน่วยแยกเป็นเรื่องยาก

ให้ดูที่Dependency InjectionและInversion of Control Container แทน libraries แทน ไลบรารี IoC หลายแห่งจะจัดการอายุการใช้งานให้คุณ

(เช่นเคยมีข้อยกเว้นเช่นคลาสคณิตศาสตร์คงที่และวิธีการขยาย C #)


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

@cdleary - ตกลง; อย่างไรก็ตามการใช้งานแบบคลาสสิกมักจะออกจาก Singleton.getInstance () ตลอดทั้ง codebase "Singletons" เพื่อจัดการทรัพยากรแคช ฯลฯ สามารถนำไปใช้กับกรอบงาน POCOs / POJO และ IoC Container ที่รองรับการจัดการตลอดอายุการใช้งาน (ลงทะเบียนประเภทเป็นอายุการใช้งานคอนเทนเนอร์และทุกความละเอียดจะได้รับอินสแตนซ์เดียวกัน)
TrueWill

9
หากคุณต้องการฝังศพ Singletons ของคุณอย่างถูกต้องให้ไปที่singletonfuneralservice.com
TrueWill

1
Hide Dependenciessideinfo เล็ก ๆ น้อย ๆ ในประเด็นนี้: คุณอาจดำเนินการผกผันของรูปแบบการควบคุมสำหรับตัวอย่างโดยใช้Ninject สิ่งนี้ช่วยให้คุณใช้อินสแตนซ์ประเภทเดียวเท่านั้นโดยผูกเข้ากับ a SingletonScopeซึ่งหมายความว่ามันจะฉีดอินสแตนซ์เดียวกัน แต่คลาสจะไม่เป็นซิงเกิลตัน
LuckyLikey

8

ฉันจะเถียงความแตกต่างเพียงอย่างเดียวคือไวยากรณ์: MySingleton.Current.Whatever () เทียบกับ MySingleton สิ่งที่ () ในที่สุดรัฐดังที่เดวิดกล่าวถึงก็คือ "คงที่" ไม่ว่าในกรณีใด ๆ


แก้ไข: กองพลฝังศพมาจาก digg ... อย่างไรก็ตามฉันนึกถึงกรณีที่ต้องใช้ซิงเกิลตัน คลาสแบบคงที่ไม่สามารถสืบทอดจากคลาสพื้นฐานหรือใช้อินเทอร์เฟซได้ (อย่างน้อยใน. Net พวกเขาทำไม่ได้) ดังนั้นหากคุณต้องการฟังก์ชันนี้คุณต้องใช้ซิงเกิลตัน


7

หนึ่งในการอภิปรายที่ฉันชอบเกี่ยวกับปัญหานี้อยู่ที่นี่ (ไซต์เดิมไม่ทำงานตอนนี้เชื่อมโยงกับInternet Archive Wayback Machine )

เพื่อสรุปข้อดีความยืดหยุ่นของ Singleton:

  • Singleton สามารถเปลี่ยนเป็นโรงงานได้อย่างง่ายดาย
  • Singleton สามารถแก้ไขได้อย่างง่ายดายเพื่อส่งคืนคลาสย่อยที่แตกต่างกัน
  • ซึ่งอาจส่งผลให้แอปพลิเคชันที่ดูแลรักษาได้ดีขึ้น

5

คลาสแบบคงที่ที่มีตัวแปรคงที่เป็นจำนวนมากเป็นเรื่องยาก

/**
 * Grotty static semaphore
 **/
 public static class Ugly {

   private static int count;

   public synchronized static void increment(){
        count++;
   }

   public synchronized static void decrement(){
        count--;
        if( count<0 ) {
            count=0;
        }
   }

   public synchronized static boolean isClear(){
         return count==0;    

    }
   }

ซิงเกิลตันที่มีอินสแตนซ์จริงจะดีกว่า

/**
 * Grotty static semaphore
 **/
 public static class LessUgly {
   private static LessUgly instance;

   private int count;

   private LessUgly(){
   }

   public static synchronized getInstance(){
     if( instance==null){
        instance = new LessUgly();
     }
     return instance;
   }
   public synchronized void increment(){
        count++;
   }

   public synchronized void decrement(){
        count--;
        if( count<0 ) {
            count=0;
        }
   }

   public synchronized boolean isClear(){
         return count==0;    

    }
   }

สถานะอยู่ในอินสแตนซ์เท่านั้น

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

public static class LessUgly {
       private static Hashtable<String,LessUgly> session;
       private static FIFO<LessUgly> freePool = new FIFO<LessUgly>();
       private static final POOL_SIZE=5;
       private int count;

       private LessUgly(){
       }

       public static synchronized getInstance(){
         if( session==null){
            session = new Hashtable<String,LessUgly>(POOL_SIZE);
            for( int i=0; i < POOL_SIZE; i++){
               LessUgly instance = new LessUgly();  
               freePool.add( instance)
            }
         }
         LessUgly instance = session.get( Session.getSessionID());
         if( instance == null){
            instance = freePool.read();
         }
         if( instance==null){
             // TODO search sessions for expired ones. Return spares to the freePool. 
             //FIXME took too long to write example in blog editor.
         }
         return instance;
       }     

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

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

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


4

ลองนึกถึงซิงเกิลตันเหมือนบริการ เป็นวัตถุที่มีชุดฟังก์ชันเฉพาะ เช่น

ObjectFactory.getInstance().makeObject();

โรงงานวัตถุเป็นวัตถุที่ให้บริการเฉพาะ

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

StringUtils.reverseString("Hello");
StringUtils.concat("Hello", "World");

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


4

คลาสแบบคงที่ถูกสร้างอินสแตนซ์ที่รันไทม์ ซึ่งอาจใช้เวลานาน Singletons สามารถสร้างอินสแตนซ์ได้เมื่อจำเป็นเท่านั้น


13
Singletons ถูกสร้างอินสแตนซ์ที่รันไทม์ด้วย ...
เรียกซ้ำ

1
@recursive: สามารถควบคุมการสร้างอินสแตนซ์ Singleton ได้
Nikit Batale

3
บางทีคำที่ดีกว่าอาจเป็นการเริ่มต้น (แทนที่จะเป็นอินสแตนซ์)
Marlon

3

ไม่ควรใช้ Singletons ในลักษณะเดียวกับคลาสแบบคงที่ ในสาระสำคัญ

MyStaticClass.GetInstance().DoSomething();

โดยพื้นฐานแล้วจะเหมือนกับ

MyStaticClass.DoSomething();

สิ่งที่คุณควรจะทำจริงคือการรักษาเดี่ยวเป็นเพียงวัตถุอื่น หากบริการต้องการอินสแตนซ์ประเภทซิงเกิลตันให้ส่งอินสแตนซ์นั้นในตัวสร้าง:

var svc = new MyComplexServce(MyStaticClass.GetInstance());

บริการไม่ควรทราบว่าวัตถุนั้นเป็นซิงเกิลตันและควรปฏิบัติต่อวัตถุนั้นเป็นเพียงวัตถุ

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


2

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

MySingleton().getInstance().doSomething();

เทียบกับ

MySingleton.doSomething();

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


2
Singletons เป็นวัตถุ -> สามารถส่งผ่านเป็นอาร์กิวเมนต์ได้
Christian Klauser

2

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


1

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

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


4
แล้วคลาสผู้จัดการที่ควบคุมทรัพยากรฮาร์ดแวร์ล่ะ สิ่งที่เกี่ยวกับคลาสไฟล์บันทึก?
RJFalconer

0

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

โค้ดการล้างข้อมูลจะดูเป็นธรรมชาติมากขึ้นเมื่อคุณมีซิงเกิลตันซึ่งต่างจากคลาสแบบคงที่ที่มีฟิลด์สถานะคงที่

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


0

ทั้งสองจะค่อนข้างคล้ายกัน แต่จำไว้ว่าซิงเกิลจริงต้องตัวเองถูก instantiated (รับครั้งเดียว) และแล้วก็ทำหน้าที่ คลาสฐานข้อมูล PHP ที่ส่งคืนอินสแตนซ์ของmysqliไม่ใช่ Singleton จริงๆ (อย่างที่บางคนเรียกว่า) เนื่องจากส่งคืนอินสแตนซ์ของคลาสอื่นไม่ใช่อินสแตนซ์ของคลาสที่มีอินสแตนซ์เป็นสมาชิกแบบคงที่

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


0

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


0

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

rp


0

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

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

Foo foo = Foo.getInstance();
doSomeWork(foo); // doSomeWork wont even know Foo is a singleton

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


0

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


0

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


0

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


0

Singleton เป็นเหมือนบริการดังที่กล่าวไปแล้ว Pro คือความยืดหยุ่น คงที่คุณต้องมีชิ้นส่วนที่คงที่เพื่อที่จะใช้ Singleton

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

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

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


0

อ้างถึง สิ่งนี้

สรุป:

ก. กฎง่ายๆอย่างหนึ่งที่คุณสามารถปฏิบัติตามได้คือถ้าไม่จำเป็นต้องรักษาสถานะคุณสามารถใช้คลาส Static ได้มิฉะนั้นคุณควรใช้ Singleton

ข. ใช้ Singleton คือถ้าเป็นวัตถุที่ "หนัก" เป็นพิเศษ หากวัตถุของคุณมีขนาดใหญ่และใช้หน่วยความจำในปริมาณที่เหมาะสมให้เรียกจำนวนมาก n / w (พูลการเชื่อมต่อ) .. ฯลฯ เพื่อให้แน่ใจว่าจะไม่ถูกสร้างอินสแตนซ์หลายครั้ง คลาส Singleton จะช่วยป้องกันไม่ให้เกิดกรณีเช่นนี้ขึ้น


-1

เมื่อคลาสเดียวต้องการสถานะ Singletons คงสถานะโกลบอลคลาสแบบคงที่ไม่ได้

ตัวอย่างเช่นการสร้างผู้ช่วยในระดับรีจิสทรี: หากคุณมีกลุ่มที่เปลี่ยนแปลงได้ (ผู้ใช้ปัจจุบัน HKey เทียบกับเครื่องท้องถิ่น HKEY) คุณสามารถไป:

RegistryEditor editor = RegistryEditor.GetInstance();
editor.Hive = LocalMachine

ตอนนี้การเรียกร้องใด ๆ เพิ่มเติมไปยัง singleton นั้นจะดำเนินการในกลุ่ม Local Machine มิฉะนั้นใช้ระดับคงที่คุณจะต้องระบุว่าภายในเครื่อง everytiem ReadSubkeyFromLocalMachineรังหรือมีวิธีการเช่น


1
ฉันจะเถียงกับการใช้ Singleton ด้วยวิธีนี้ ... มันอันตรายมากที่จะทำให้สถานะอืดอาดเพราะผู้โทรคนต่อไปอาจลืมตั้งค่ากลุ่มและเขียนลงในรีจิสทรีในรังที่ไม่ถูกต้อง ... :-( นี่คือ อาจเป็นตัวอย่างของการใช้ผิดประเภทที่ทำให้คนคิดว่า Singletons ไม่ดี
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.