1. รูปแบบการออกแบบคืออะไร
ในด้านวิศวกรรมซอฟต์แวร์รูปแบบการออกแบบเป็นวิธีแก้ปัญหาที่พบได้ทั่วไป (เกิดซ้ำ) ปัญหาในการออกแบบซอฟต์แวร์ คำนี้ได้รับการแนะนำให้รู้จักกับวิทยาศาสตร์คอมพิวเตอร์จากสาขาการออกแบบสถาปัตยกรรมในปี 1990 โดย Erich Gamma และอื่น ๆ
แก๊งค์ 4 คนที่มีชื่อเสียง: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (GOF)
รูปแบบการออกแบบ: พื้นฐานของซอฟต์แวร์เชิงวัตถุที่นำกลับมาใช้ใหม่ได้
2. โหมดซิงเกิลตัน
คลาสของวัตถุซิงเกิลจะต้องรับประกันว่าจะมีเพียงหนึ่งอินสแตนซ์ที่มีอยู่ หลายครั้งที่ระบบทั้งหมดจำเป็นต้องมีวัตถุระดับโลกหนึ่งชิ้นเท่านั้นซึ่งเอื้อต่อการประสานงานของเราเกี่ยวกับพฤติกรรมโดยรวมของระบบ
ตัวอย่างเช่น: การกำหนดค่าข้อมูลทั่วโลก
การใช้งานที่ง่ายที่สุดของโหมด Singleton:
Public Class Singleton {Private Singleton () {System.out.println ("Singleton Is Create"); } อินสแตนซ์แบบสแตติกส่วนตัว = ใหม่ซิงเกิล (); สาธารณะสแตติก Singleton GetInstance () {อินสแตนซ์กลับ; - เอกลักษณ์จะถูกกำหนดโดยตัวสร้างส่วนตัวและคงที่
ข้อเสีย: เมื่อมีการสร้างอินสแตนซ์นั้นยากที่จะควบคุม
แม้ว่าเราจะรู้ว่าเมื่อมีการโหลดคลาสซิงเกิลเป็นครั้งแรก แต่อินสแตนซ์จะถูกสร้างขึ้น
แต่ถ้ามีคุณสมบัติอื่น ๆ ในชั้นเรียนนี้
ระดับสาธารณะ Singleton {สถานะ int คงที่สาธารณะ = 1; Private Singleton () {System.out.println ("Singleton Is Create"); } อินสแตนซ์แบบสแตติกส่วนตัว = ใหม่ซิงเกิล (); สาธารณะสแตติก Singleton GetInstance () {อินสแตนซ์กลับ; - เมื่อใช้
System.out.println (Singleton.status);
ตัวอย่างนี้ผลิตขึ้น บางทีคุณอาจไม่ต้องการให้อินสแตนซ์นี้สร้างขึ้นในเวลานี้
หากระบบให้ความสนใจเป็นพิเศษกับปัญหานี้วิธีการใช้งานของซิงเกิลตันนี้ไม่ค่อยดีนัก
ทางออกสำหรับโหมดซิงเกิลที่สอง:
Public Class Singleton {Private Singleton () {System.out.println ("Singleton Is Create"); } อินสแตนซ์แบบสแตติกส่วนตัว = null; Singleton GetInstance () {ถ้า (อินสแตนซ์ == null) อินสแตนซ์ = new Singleton (); อินสแตนซ์กลับ; - ปล่อยให้อินสแตนซ์ถูกสร้างขึ้นเฉพาะเมื่อมีการเรียกวิธีการ getInstance () และความปลอดภัยของเธรดจะได้รับการรับรองผ่านการซิงโครไนซ์
การควบคุมนี้เมื่อมีการสร้างอินสแตนซ์
วิธีการนี้เป็นเรื่องปกติของการโหลดขี้เกียจ
แต่ปัญหาหนึ่งคือประสิทธิภาพจะมีผลกระทบในสถานการณ์ที่เกิดขึ้นพร้อมกันสูง แม้ว่ามันจะถูกส่งคืนด้วยการตัดสินเพียงครั้งเดียว แต่ก็จะมีผลกระทบในกรณีของการทำงานร่วมกันสูง แต่ก็จะมีผลกระทบมากหรือน้อยเพราะคุณต้องได้รับการล็อคแบบซิงโครไนซ์
เพื่อให้มีประสิทธิภาพมีวิธีที่สาม:
Staticsingletleton ระดับสาธารณะ {Private Staticsingleton () {System.out.println ("Staticsingleton ถูกสร้างขึ้น"); } ชั้นเรียนแบบคงที่ส่วนตัว singletonholder {statics staticsingletleton ส่วนตัว = ใหม่ staticsingletleton (); } Statics Staticsingleton Public GetInstance () {return singletonholder.instance; - เนื่องจากเมื่อมีการโหลดคลาสคลาสภายในจะไม่ถูกโหลด สิ่งนี้ทำให้มั่นใจได้ว่าอินสแตนซ์จะถูกสร้างขึ้นเฉพาะเมื่อเรียกว่า getInstance () เวลาในการสร้างอินสแตนซ์จะถูกควบคุมและการโหลดล่าช้าจะทำได้
และการซิงโครไนซ์จะถูกลบออกเพื่อให้ประสิทธิภาพดีขึ้นและใช้สแตติกเพื่อให้แน่ใจว่าเป็นเอกลักษณ์
3. โหมดไม่แปรเปลี่ยน
หลังจากสถานะภายในของชั้นเรียนถูกสร้างขึ้นมันจะไม่เปลี่ยนแปลงในช่วงชีวิตทั้งหมด
โหมด Unchange ไม่จำเป็นต้องมีการซิงโครไนซ์
สร้างคลาสที่ไม่เปลี่ยนแปลง:
ผลิตภัณฑ์ระดับสุดท้ายของสาธารณะ {// ตรวจสอบให้แน่ใจว่าไม่มีสตริงย่อยส่วนตัวย่อยหมายเลข // แอตทริบิวต์ส่วนตัวจะไม่ได้รับจากวัตถุอื่น ๆ ชื่อสตริงสุดท้ายส่วนตัว; // รับประกันขั้นสุดท้ายว่าแอตทริบิวต์จะไม่ได้รับการกำหนดสองครั้งสองครั้งส่วนตัว ผลิตภัณฑ์สาธารณะ (หมายเลขสตริง, ชื่อสตริง, ราคาสองเท่า) {// เมื่อสร้างวัตถุข้อมูลจะต้องระบุ super (); // เพราะหลังจากการสร้างมันไม่สามารถแก้ไขสิ่งนี้ได้ no = no; this.name = ชื่อ; this.price = ราคา; } สตริงสาธารณะ getNo () {return no; } สตริงสาธารณะ getName () {ชื่อคืน; } สาธารณะ double getPrice () {ราคาคืน; - กรณีของรูปแบบที่ไม่เปลี่ยนแปลงใน Java รวมถึง:
java.lang.string
java.lang.boolean
java.lang.byte
java.lang.character
java.lang.double
java.lang.float
java.lang.integer
java.lang.long
java.lang.stort
4. โหมดอนาคต
แนวคิดหลักคือการโทรแบบอะซิงโครนัส
ไม่ใช่ asynchronous:
อะซิงโครนัส:
call_return แรกจะถูกส่งคืนเนื่องจากงานยังไม่เสร็จสมบูรณ์
แต่ผลตอบแทนนี้คล้ายกับคำสั่งซื้อในการช็อปปิ้งและคุณสามารถรับผลลัพธ์ตามคำสั่งนี้ในอนาคต
ดังนั้นรูปแบบในอนาคตนี้หมายความว่า "อนาคต" สามารถรับได้ซึ่งหมายความว่าคำสั่งหรือสัญญาคือ "สัญญา" และจะให้ผลลัพธ์ในอนาคต
การใช้งานโหมดในอนาคตอย่างง่าย:
สิ่งที่ผู้โทรได้รับคือข้อมูลซึ่งอาจเป็น futuredata ในตอนแรกเพราะ realdata มีการสร้างช้า ในบางช่วงเวลาในอนาคต Realdata สามารถรับได้ผ่าน Futuredata
การใช้รหัส:
ข้อมูลส่วนต่อประสานสาธารณะ {สตริงสาธารณะ getResult (); } คลาสสาธารณะ FutureData ใช้ข้อมูล {Protected Realdata realdata = null; // FutureData เป็นบูลีนที่ได้รับการป้องกันแบบ realdata wrapper isread = false; โมฆะที่ซิงโครไนซ์สาธารณะ setrealdata (realdata realdata) {ถ้า (พร้อม) {return; } this.realdata = realdata; พร้อม = จริง; แจ้งเตือน (); // realdata ได้รับการฉีดแจ้งเตือน getResult ()} สตริงที่ซิงโครไนซ์สาธารณะ getResult () // จะรอการก่อสร้าง realdata ให้เสร็จสมบูรณ์ {ในขณะที่ (! // รอตลอดเวลาที่จะรู้ว่า realdata ถูกฉีด} catch (interruptedException e) {}} return realdata.result; // ดำเนินการโดย Realdata}} คลาสสาธารณะ Realdata ใช้ข้อมูล {ผลลัพธ์สตริงสุดท้ายที่ได้รับการป้องกัน Public Realdata (Para String) {// การสร้าง realdata อาจช้ามากและต้องการให้ผู้ใช้ต้องรอเป็นเวลานาน ที่นี่เราใช้ Sleep เพื่อจำลอง StringBuffer SB = new StringBuffer (); สำหรับ (int i = 0; i <10; i ++) {sb.append (para); ลอง {// ใช้การนอนหลับที่นี่แทนที่จะใช้เธรดการทำงานที่ช้ามาก (100); } catch (interruptedException e) {}} result = sb.toString (); } สตริงสาธารณะ getResult () {ผลตอบแทน; }} ไคลเอนต์คลาสสาธารณะ {คำขอข้อมูลสาธารณะ (String String สุดท้าย) {Final FutureData Future = New FutureData (); เธรดใหม่ () {โมฆะสาธารณะ Run () {// realdata นั้นช้ามากในการสร้าง // realdata ในเธรดแยกต่างหาก realdata = new realdata (querystr); Future.setrealdata (realdata); } } }.เริ่ม(); กลับมาอนาคต // futuredata จะถูกส่งคืนทันที}} โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ไคลเอนต์ไคลเอนต์ = ไคลเอนต์ใหม่ (); // สิ่งนี้จะถูกส่งคืนทันทีเพราะสิ่งที่คุณได้รับคือ futuredata แทนข้อมูลข้อมูล realdata = client.request ("ชื่อ"); System.out.println ("ขอเสร็จสมบูรณ์"); ลอง {// ที่นี่คุณสามารถใช้การนอนหลับแทนการประมวลผล logics ทางธุรกิจอื่น ๆ // ในกระบวนการประมวลผลตรรกะทางธุรกิจเหล่านี้ realdata ถูกสร้างขึ้นทำให้ใช้ประโยชน์จาก Time.sleep (2000) อย่างเต็มที่ } catch (interruptedException e) {} // ใช้ข้อมูลจริง System.out.println ("data =" + data.getResult ()); -นอกจากนี้ยังมีการสนับสนุนโหมดในอนาคตมากมายใน JDK:
ถัดไปใช้คลาสและวิธีการที่ JDK จัดหาให้ใช้ในการใช้งานรหัสตอนนี้:
นำเข้า java.util.concurrent.callable; คลาสสาธารณะ realdata ใช้ callable <string> {private string para; สาธารณะ realdata (พาราสตริง) {this.para = para; } @Override การเรียกสตริงสาธารณะ () พ่นข้อยกเว้น {StringBuffer sb = new StringBuffer (); สำหรับ (int i = 0; i <10; i ++) {sb.append (para); ลอง {thread.sleep (100); } catch (interruptedException e) {}} ส่งคืน sb.toString (); - นำเข้า java.util.concurrent.executionexception; นำเข้า java.util.concurrent.executorservice; นำเข้า java.util.concurrent.executors; นำเข้า java.util.concurrent.futureTask; FutureTask FutureTask <String> Future = New FutureTask <String> (ใหม่ realdata ("A")); ExecutorService Executor = Executors.NewFixedThreadPool (1); // ดำเนินการ FutureTask เทียบเท่ากับ client.request ("a") ในตัวอย่างข้างต้นส่งคำขอ // เปิดใช้งานเธรดที่นี่เพื่อทำการโทร realdata () และดำเนินการ executor.submit (อนาคต); System.out.println ("ขอเสร็จสมบูรณ์"); ลอง {// การดำเนินการข้อมูลเพิ่มเติมยังสามารถทำได้ที่นี่และสามารถใช้การนอนหลับแทนการประมวลผลเธรดตรรกะทางธุรกิจอื่น ๆ SLEEP (2000); } catch (interruptedException e) {} // เทียบเท่ากับ data.getResult () รับค่าส่งคืนของเมธอดการโทร () // ถ้าวิธีการโทร () ไม่ถูกดำเนินการในเวลานี้มันจะยังคงรอ system.out.println ("data =" + future.get (); - สิ่งที่คุณต้องทราบที่นี่คือ FutureTask เป็นคลาสที่มีฟังก์ชั่นในอนาคตและฟังก์ชั่น Runnable ดังนั้นจึงสามารถวิ่งได้อีกครั้งและในที่สุดก็รับมัน
แน่นอนถ้าข้อมูลจริงยังไม่พร้อมเมื่อโทร Future.get () มันจะยังคงทำให้เกิดสถานการณ์การปิดกั้นจนกว่าข้อมูลจะพร้อม
แน่นอนว่ามีวิธีที่ง่ายกว่า:
นำเข้า java.util.concurrent.executionexception; นำเข้า java.util.concurrent.executorservice; นำเข้า java.util.concurrent.executors; นำเข้า java.util.concurrent.future; = Executors.NewFixedThreadPool (1); // ดำเนินการ FutureTask เทียบเท่ากับ client.request ("a") ในตัวอย่างข้างต้นส่งคำขอ // เปิดเธรดที่นี่เพื่อทำการเรียก realdata () และดำเนินการในอนาคต <String> future = executor.submit (ใหม่ realdata ("a"); System.out.println ("ขอเสร็จสมบูรณ์"); ลอง {// การดำเนินการข้อมูลเพิ่มเติมยังสามารถทำได้ที่นี่ใช้การนอนหลับแทนเธรดการประมวลผลตรรกะทางธุรกิจอื่น ๆ SLEEP (2000); } catch (interruptedException e) {} // เทียบเท่ากับ data.getResult () รับค่าส่งคืนของเมธอดการโทร () // หากวิธีการโทร () ไม่ดำเนินการในเวลานี้ System.out.println จะยังคงรอ system.out.println ("data =" + future.get. );); - เนื่องจาก Callable มีค่าส่งคืนคุณสามารถส่งคืนวัตถุในอนาคตได้โดยตรง
5. ผู้ผลิตและผู้บริโภค
โมเดลผู้ผลิต-ผู้บริโภคเป็นแบบจำลองการออกแบบแบบมัลติเธรดแบบคลาสสิก มันเป็นทางออกที่ดีสำหรับการทำงานร่วมกันระหว่างหลายเธรด ในรุ่นผู้ผลิต-ผู้บริโภคมักจะมีเธรดสองประเภทคือเธรดผู้ผลิตหลายตัวและเธรดผู้บริโภคหลายตัว เธรดผู้ผลิตมีหน้าที่รับผิดชอบในการส่งคำขอผู้ใช้ในขณะที่เธรดผู้บริโภคมีหน้าที่รับผิดชอบในการจัดการงานที่ส่งโดยผู้ผลิตโดยเฉพาะ ผู้ผลิตและผู้บริโภคสื่อสารผ่านบัฟเฟอร์หน่วยความจำที่ใช้ร่วมกัน
ฉันเคยเขียนบทความในอดีตเพื่อใช้วิธีการต่าง ๆ ในการใช้ Java เพื่อนำผู้ผลิตและผู้บริโภคมาใช้ดังนั้นฉันจะไม่อธิบายที่นี่