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

รูปแบบการสร้างสรรค์
เริ่มจากจุดเริ่มต้นของวงจรชีวิตของวัตถุ - ด้วยการสร้างวัตถุ เทมเพลตทั่วไปช่วยสร้างออบเจ็กต์ได้สะดวกยิ่งขึ้นและให้ความยืดหยุ่นในกระบวนการนี้ สิ่งที่มีชื่อเสียงที่สุดคือ " ผู้สร้าง " รูปแบบนี้ช่วยให้คุณสร้างวัตถุที่ซับซ้อนได้ทีละขั้นตอน ใน Java ตัวอย่างที่มีชื่อเสียงที่สุดคือStringBuilder
:
class Main {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder();
builder.append("Hello");
builder.append(',');
builder.append("World!");
System.out.println(builder.toString());
}
}
อีกแนวทางหนึ่งที่รู้จักกันดีในการสร้างออบเจ็กต์คือการย้ายการสร้างไปยังวิธีที่แยกจากกัน วิธีการนี้จะกลายเป็นโรงงานออบเจ็กต์ นั่นเป็นเหตุผลว่าทำไมรูปแบบนี้จึงเรียกว่า " Factory Method" java.util.Calendar
ตัวอย่างเช่น ใน Java เอฟเฟก ต์สามารถเห็นได้ในคลาส คลาสนั้นCalendar
เป็นนามธรรม และเพื่อสร้างมันขึ้นมาจะใช้เมธอดgetInstance
:
import java.util.*;
class Main {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getTime());
System.out.println(calendar.getClass().getCanonicalName());
}
}
สาเหตุนี้มักเกิดขึ้นเนื่องจากตรรกะเบื้องหลังการสร้างออบเจ็กต์อาจมีความซับซ้อน ตัวอย่างเช่น ในกรณีข้างต้น เราเข้าถึงคลาสฐานCalendar
และคลาสก็ถูกสร้างGregorianCalendar
ขึ้น หากเราดูที่ Constructor เราจะเห็นว่ามีการสร้างการใช้งานที่แตกต่างกันขึ้นอยู่กับCalendar
เงื่อนไข แต่บางครั้งวิธีการจากโรงงานวิธีเดียวก็ไม่เพียงพอ บางครั้งคุณจำเป็นต้องสร้างวัตถุต่างๆ เพื่อให้เข้ากันได้ เทมเพลตอื่นจะช่วยเราในเรื่องนี้ - " โรงงานนามธรรม " จากนั้นเราก็ต้องสร้างโรงงานต่างๆ ขึ้นมาในที่เดียว ในขณะเดียวกันข้อดีก็คือรายละเอียดการดำเนินการไม่สำคัญสำหรับเรานั่นคือ ไม่สำคัญว่าเราจะได้โรงงานแห่งใดโดยเฉพาะ สิ่งสำคัญคือการสร้างการใช้งานที่ถูกต้อง ตัวอย่างสุดยอด:

java.lang.Cloneable
:
class Main {
public static void main(String[] args) {
class CloneObject implements Cloneable {
@Override
protected Object clone() throws CloneNotSupportedException {
return new CloneObject();
}
}
CloneObject obj = new CloneObject();
try {
CloneObject pattern = (CloneObject) obj.clone();
} catch (CloneNotSupportedException e) {
//Do something
}
}
}
อย่างที่คุณเห็น ผู้โทรไม่รู้ว่าวิธีclone
การ นั่นคือการสร้างออบเจ็กต์ตามต้นแบบถือเป็นความรับผิดชอบของออบเจ็กต์นั้นเอง สิ่งนี้มีประโยชน์เนื่องจากไม่ได้ผูกผู้ใช้กับการใช้งานออบเจ็กต์เทมเพลต อันสุดท้ายในรายการนี้คือรูปแบบ “ซิงเกิลตัน” วัตถุประสงค์นั้นเรียบง่าย - เพื่อจัดเตรียมออบเจ็กต์เดียวสำหรับแอปพลิเคชันทั้งหมด รูปแบบนี้น่าสนใจเนื่องจากมักจะแสดงปัญหาแบบมัลติเธรด หากต้องการดูเชิงลึกยิ่งขึ้น โปรดดูบทความเหล่านี้:

รูปแบบโครงสร้าง
เมื่อสร้างวัตถุขึ้นมาก็ชัดเจนขึ้น ตอนนี้เป็นเวลาที่จะดูรูปแบบโครงสร้าง เป้าหมายของพวกเขาคือการสร้างลำดับชั้นของชั้นเรียนที่ง่ายต่อการสนับสนุนและความสัมพันธ์ของพวกเขา รูปแบบแรกและเป็นที่รู้จักคือ “ รอง ” (ผู้รับมอบฉันทะ) พร็อกซีมีอินเทอร์เฟซเดียวกันกับออบเจ็กต์จริง ดังนั้นจึงไม่มีความแตกต่างสำหรับไคลเอ็นต์ในการทำงานผ่านพร็อกซีหรือโดยตรง ตัวอย่างที่ง่ายที่สุดคือjava.lang.reflect.Proxy :import java.util.*;
import java.lang.reflect.*;
class Main {
public static void main(String[] arguments) {
final Map<String, String> original = new HashMap<>();
InvocationHandler proxy = (obj, method, args) -> {
System.out.println("Invoked: " + method.getName());
return method.invoke(original, args);
};
Map<String, String> proxyInstance = (Map) Proxy.newProxyInstance(
original.getClass().getClassLoader(),
original.getClass().getInterfaces(),
proxy);
proxyInstance.put("key", "value");
System.out.println(proxyInstance.get("key"));
}
}
อย่างที่คุณเห็นในตัวอย่างที่เรามี ต้นฉบับ - นี่คืออันHashMap
ที่ใช้อินเทอร์เฟซMap
. ต่อไปเราจะสร้างพรอกซีที่จะแทนที่อันเดิมHashMap
สำหรับส่วนของไคลเอนต์ ซึ่งเรียกใช้เมธอดput
และget
โดยเพิ่มตรรกะของเราเองระหว่างการโทร ดังที่เราเห็น การโต้ตอบในรูปแบบเกิดขึ้นผ่านอินเทอร์เฟซ แต่บางครั้งตัวสำรองยังไม่เพียงพอ จากนั้นจึง สามารถใช้รูปแบบ" มัณฑนากร " ได้ มัณฑนากรเรียกอีกอย่างว่ากระดาษห่อหรือกระดาษห่อ พร็อกซีและมัณฑนากรคล้ายกันมาก แต่ถ้าคุณดูตัวอย่าง คุณจะเห็นความแตกต่าง:
import java.util.*;
class Main {
public static void main(String[] arguments) {
List<String> list = new ArrayList<>();
List<String> decorated = Collections.checkedList(list, String.class);
decorated.add("2");
list.add("3");
System.out.println(decorated);
}
}
ต่างจากพรอกซี มัณฑนากรจะล้อมรอบบางสิ่งที่ส่งผ่านเป็นอินพุต พร็อกซีสามารถยอมรับสิ่งที่จำเป็นต้องมีพร็อกซีและจัดการอายุการใช้งานของอ็อบเจ็กต์พร็อกซีได้ (เช่น สร้างอ็อบเจ็กต์พร็อกซี) มีอีกรูปแบบหนึ่งที่น่าสนใจ - “ อะแดปเตอร์ ” มันคล้ายกับมัณฑนากร - มัณฑนากรใช้วัตถุหนึ่งชิ้นเป็นอินพุตและส่งกลับเสื้อคลุมเหนือวัตถุนี้ ความแตกต่างก็คือเป้าหมายไม่ใช่การเปลี่ยนฟังก์ชันการทำงาน แต่เป็นการปรับอินเทอร์เฟซหนึ่งไปยังอีกอินเทอร์เฟซหนึ่ง Java มีตัวอย่างที่ชัดเจนมากเกี่ยวกับสิ่งนี้:
import java.util.*;
class Main {
public static void main(String[] arguments) {
String[] array = {"One", "Two", "Three"};
List<String> strings = Arrays.asList(array);
strings.set(0, "1");
System.out.println(Arrays.toString(array));
}
}
ที่อินพุตเรามีอาร์เรย์ ต่อไป เราจะสร้างอะแดปเตอร์ที่นำอาร์เรย์มาสู่อินเทอร์เฟList
ซ เมื่อทำงานกับมัน เรากำลังทำงานกับอาร์เรย์จริงๆ ดังนั้นการเพิ่มองค์ประกอบจะไม่ทำงานเพราะ... อาร์เรย์เดิมไม่สามารถเปลี่ยนแปลงได้ และในกรณีนี้เราจะได้รับUnsupportedOperationException
. แนวทางที่น่าสนใจต่อไปในการพัฒนาโครงสร้างคลาสคือรูป แบบ คอมโพสิต สิ่งที่น่าสนใจคือชุดองค์ประกอบบางชุดที่ใช้อินเทอร์เฟซเดียวถูกจัดเรียงในลำดับชั้นที่เหมือนต้นไม้ โดยการเรียกเมธอดบนอิลิเมนต์พาเรนต์ เราจึงได้รับการเรียกเมธอดนี้ในอิลิเมนต์ลูกที่จำเป็นทั้งหมด ตัวอย่างที่สำคัญของรูปแบบนี้คือ UI (ไม่ว่าจะเป็น java.awt หรือ JSF):
import java.awt.*;
class Main {
public static void main(String[] arguments) {
Container container = new Container();
Component component = new java.awt.Component(){};
System.out.println(component.getComponentOrientation().isLeftToRight());
container.add(component);
container.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
System.out.println(component.getComponentOrientation().isLeftToRight());
}
}
ดังที่เราเห็น เราได้เพิ่มส่วนประกอบลงในคอนเทนเนอร์แล้ว จากนั้นเราขอให้คอนเทนเนอร์ใช้การวางแนวใหม่ของส่วนประกอบต่างๆ และคอนเทนเนอร์เมื่อรู้ว่าส่วนประกอบใดประกอบด้วยอะไรบ้าง จึงมอบหมายการดำเนินการของคำสั่งนี้ให้กับส่วนประกอบย่อยทั้งหมด อีกรูปแบบที่น่าสนใจคือลาย “ สะพาน ” มันถูกเรียกสิ่งนี้เพราะมันอธิบายการเชื่อมต่อหรือสะพานเชื่อมระหว่างลำดับชั้นของคลาสที่แตกต่างกันสองลำดับ หนึ่งในลำดับชั้นเหล่านี้ถือเป็นนามธรรมและอีกลำดับหนึ่งถือเป็นการนำไปปฏิบัติ สิ่งนี้ถูกเน้นเนื่องจากสิ่งที่เป็นนามธรรมนั้นไม่ได้ดำเนินการใดๆ แต่มอบหมายการดำเนินการนี้ให้กับการใช้งาน รูปแบบนี้มักใช้เมื่อมีคลาส "ควบคุม" และคลาส "แพลตฟอร์ม" หลายประเภท (เช่น Windows, Linux เป็นต้น) ด้วยแนวทางนี้ หนึ่งในลำดับชั้นเหล่านี้ (นามธรรม) จะได้รับการอ้างอิงถึงออบเจ็กต์ของลำดับชั้นอื่น (การนำไปใช้) และจะมอบหมายงานหลักให้กับพวกเขา เนื่องจากการใช้งานทั้งหมดจะเป็นไปตามอินเทอร์เฟซทั่วไป จึงสามารถสับเปลี่ยนกันได้ภายในนามธรรม ใน Java ตัวอย่างที่ชัดเจนคือjava.awt
:



รูปแบบพฤติกรรม
ดังนั้นเราจึงพบว่าสามารถสร้างออบเจ็กต์ได้อย่างไรและสามารถจัดระเบียบการเชื่อมต่อระหว่างคลาสได้อย่างไร สิ่งที่น่าสนใจที่สุดที่เหลืออยู่คือการให้ความยืดหยุ่นในการเปลี่ยนแปลงพฤติกรรมของวัตถุ และรูปแบบพฤติกรรมจะช่วยเราในเรื่องนี้ รูปแบบหนึ่งที่ถูกกล่าวถึงบ่อยที่สุดคือรูปแบบ " กลยุทธ์ " นี่คือจุดเริ่มต้นของการศึกษารูปแบบในหนังสือ “ Head First. Design Patterns ” การใช้รูปแบบ "กลยุทธ์" ทำให้เราสามารถจัดเก็บภายในออบเจ็กต์ว่าเราจะดำเนินการอย่างไร เช่น วัตถุภายในเก็บกลยุทธ์ที่สามารถเปลี่ยนแปลงได้ระหว่างการเรียกใช้โค้ด นี่เป็นรูปแบบที่เรามักใช้เมื่อใช้ตัวเปรียบเทียบ:import java.util.*;
class Main {
public static void main(String[] args) {
List<String> data = Arrays.asList("Moscow", "Paris", "NYC");
Comparator<String> comparator = Comparator.comparingInt(String::length);
Set dataSet = new TreeSet(comparator);
dataSet.addAll(data);
System.out.println("Dataset : " + dataSet);
}
}
ก่อนเรา - TreeSet
. มีพฤติกรรมTreeSet
การรักษาลำดับขององค์ประกอบเช่น เรียงลำดับพวกมัน (เนื่องจากเป็น SortedSet) ลักษณะการทำงานนี้มีกลยุทธ์เริ่มต้น ซึ่งเราเห็นใน JavaDoc: การเรียงลำดับใน "การเรียงลำดับตามธรรมชาติ" (สำหรับสตริง นี่คือการเรียงลำดับพจนานุกรม) สิ่งนี้จะเกิดขึ้นหากคุณใช้ตัวสร้างแบบไม่มีพารามิเตอร์ แต่ถ้าเราอยากเปลี่ยนกลยุทธ์เราก็ผ่านComparator
ได้ ในตัวอย่างนี้ เราสามารถสร้างชุดของเราเป็นnew TreeSet(comparator)
จากนั้นลำดับการจัดเก็บองค์ประกอบ (กลยุทธ์การจัดเก็บ) จะเปลี่ยนไปเป็นลำดับที่ระบุในตัวเปรียบเทียบ ที่น่าสนใจคือมีรูปแบบเดียวกันเกือบเรียกว่า " รัฐ " รูปแบบ "สถานะ" บอกว่าถ้าเรามีพฤติกรรมบางอย่างในวัตถุหลักที่ขึ้นอยู่กับสถานะของวัตถุนี้ เราก็สามารถอธิบายสถานะนั้นว่าเป็นวัตถุและเปลี่ยนวัตถุสถานะได้ และมอบหมายการโทรจากวัตถุหลักไปยังรัฐ อีกรูปแบบหนึ่งที่เรารู้จักจากการศึกษาพื้นฐานของภาษา Java คือรูปแบบ “ Command ” รูปแบบการออกแบบนี้แสดงให้เห็นว่าคำสั่งที่แตกต่างกันสามารถแสดงเป็นคลาสที่แตกต่างกันได้ รูปแบบนี้คล้ายกับรูปแบบกลยุทธ์มาก แต่ในรูปแบบกลยุทธ์ เรากำลังกำหนดวิธีดำเนินการเฉพาะเจาะจงใหม่ (เช่น การเรียงลำดับในTreeSet
) ในรูปแบบ "คำสั่ง" เราจะกำหนดการกระทำที่จะดำเนินการใหม่ คำสั่งรูปแบบอยู่กับเราทุกวันเมื่อเราใช้เธรด:
import java.util.*;
class Main {
public static void main(String[] args) {
Runnable command = () -> {
System.out.println("Command action");
};
Thread th = new Thread(command);
th.start();
}
}
อย่างที่คุณเห็น command กำหนดการกระทำหรือคำสั่งที่จะดำเนินการในเธรดใหม่ นอกจากนี้ยังควรพิจารณารูปแบบ " สายโซ่แห่ง ความรับผิดชอบ " ด้วย รูปแบบนี้ก็ง่ายมากเช่นกัน รูปแบบนี้บอกว่าหากบางสิ่งจำเป็นต้องได้รับการประมวลผล คุณสามารถรวบรวมตัวจัดการแบบลูกโซ่ได้ ตัวอย่างเช่น รูปแบบนี้มักใช้ในเว็บเซิร์ฟเวอร์ ที่อินพุต เซิร์ฟเวอร์ได้รับคำขอบางอย่างจากผู้ใช้ คำขอนี้จะต้องผ่านห่วงโซ่การประมวลผล สายของตัวจัดการนี้ประกอบด้วยตัวกรอง (เช่น ไม่ยอมรับคำขอจากบัญชีดำของที่อยู่ IP) ตัวจัดการการตรวจสอบสิทธิ์ (อนุญาตเฉพาะผู้ใช้ที่ได้รับอนุญาตเท่านั้น) ตัวจัดการส่วนหัวของคำขอ ตัวจัดการแคช ฯลฯ แต่มีตัวอย่างที่ง่ายกว่าและเข้าใจง่ายกว่าใน Java java.util.logging
:
import java.util.logging.*;
class Main {
public static void main(String[] args) {
Logger logger = Logger.getLogger(Main.class.getName());
ConsoleHandler consoleHandler = new ConsoleHandler(){
@Override
public void publish(LogRecord record) {
System.out.println("LogRecord обработан");
}
};
logger.addHandler(consoleHandler);
logger.info("test");
}
}
อย่างที่คุณเห็น ตัวจัดการจะถูกเพิ่มเข้าไปในรายการตัวจัดการตัวบันทึก เมื่อคนบันทึกได้รับข้อความสำหรับการประมวลผล แต่ละข้อความดังกล่าวจะผ่านสายโซ่ของตัวจัดการ (จากlogger.getHandlers
) สำหรับคนตัดไม้นั้น อีกรูปแบบหนึ่งที่เราเห็นทุกวันคือ “ Iterator ” สาระสำคัญของมันคือการแยกคอลเลกชันของวัตถุ (เช่น คลาสที่แสดงโครงสร้างข้อมูล ตัวอย่างเช่นList
) และการสำรวจเส้นทางของคอลเลกชันนี้
import java.util.*;
class Main {
public static void main(String[] args) {
List<String> data = Arrays.asList("Moscow", "Paris", "NYC");
Iterator<String> iterator = data.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
อย่างที่คุณเห็น ตัววนซ้ำไม่ได้เป็นส่วนหนึ่งของคอลเลกชัน แต่แสดงโดยคลาสที่แยกจากกันที่สำรวจคอลเลกชัน ผู้ใช้ตัววนซ้ำอาจไม่รู้ด้วยซ้ำว่าคอลเลกชันใดกำลังวนซ้ำอยู่ เช่น เขาไปเยี่ยมชมคอลเลกชันอะไร? ควรพิจารณารูปแบบ " ผู้เยี่ยมชม " รูปแบบผู้เยี่ยมชมจะคล้ายกับรูปแบบตัววนซ้ำมาก รูปแบบนี้ช่วยให้คุณข้ามโครงสร้างของวัตถุและดำเนินการกับวัตถุเหล่านี้ได้ พวกเขาแตกต่างกันค่อนข้างในแนวคิด ตัววนซ้ำจะสำรวจคอลเลกชันเพื่อให้ไคลเอนต์ที่ใช้ตัววนซ้ำไม่สนใจว่าคอลเลกชันนั้นอยู่ภายในอะไร เฉพาะองค์ประกอบในลำดับเท่านั้นที่สำคัญ ผู้เยี่ยมชมหมายความว่ามีลำดับชั้นหรือโครงสร้างของวัตถุที่เราเยี่ยมชม ตัวอย่างเช่น เราสามารถใช้การประมวลผลไดเร็กทอรีแยกกันและการประมวลผลไฟล์แยกกัน Java มีการนำรูปแบบนี้ไปใช้นอกกรอบในรูปแบบjava.nio.file.FileVisitor
:
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.*;
class Main {
public static void main(String[] args) {
SimpleFileVisitor visitor = new SimpleFileVisitor() {
@Override
public FileVisitResult visitFile(Object file, BasicFileAttributes attrs) throws IOException {
System.out.println("File:" + file.toString());
return FileVisitResult.CONTINUE;
}
};
Path pathSource = Paths.get(System.getProperty("java.io.tmpdir"));
try {
Files.walkFileTree(pathSource, visitor);
} catch (AccessDeniedException e) {
// skip
} catch (IOException e) {
// Do something
}
}
}
บางครั้งจำเป็นต้องมีวัตถุบางอย่างเพื่อตอบสนองต่อการเปลี่ยนแปลงในวัตถุอื่น จากนั้นรูปแบบ "ผู้สังเกตการณ์" จะช่วยเรา วิธีที่สะดวกที่สุดคือการจัดหากลไกการสมัครสมาชิกที่อนุญาตให้บางอ็อบเจ็กต์ตรวจสอบและตอบสนองต่อเหตุการณ์ที่เกิดขึ้นในอ็อบเจ็กต์อื่น รูปแบบนี้มักใช้กับผู้ฟังและผู้สังเกตการณ์ต่างๆ ที่ตอบสนองต่อเหตุการณ์ต่างๆ เป็นตัวอย่างง่ายๆ เราสามารถจำการนำรูปแบบนี้ไปใช้ตั้งแต่เวอร์ชันแรกของ JDK:
import java.util.*;
class Main {
public static void main(String[] args) {
Observer observer = (obj, arg) -> {
System.out.println("Arg: " + arg);
};
Observable target = new Observable(){
@Override
public void notifyObservers(Object arg) {
setChanged();
super.notifyObservers(arg);
}
};
target.addObserver(observer);
target.notifyObservers("Hello, World!");
}
}
มีรูปแบบพฤติกรรมที่มีประโยชน์อีกรูปแบบหนึ่ง - “ ผู้ไกล่เกลี่ย ” มันมีประโยชน์เพราะในระบบที่ซับซ้อน จะช่วยลบการเชื่อมต่อระหว่างอ็อบเจ็กต์ต่างๆ และมอบหมายการโต้ตอบทั้งหมดระหว่างอ็อบเจ็กต์ให้กับบางอ็อบเจ็กต์ ซึ่งเป็นตัวกลาง หนึ่งในการใช้งานที่โดดเด่นที่สุดของรูปแบบนี้คือ Spring MVC ซึ่งใช้รูปแบบนี้ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับสิ่งนี้ได้ที่นี่: " Spring: Mediator Pattern " คุณมักจะเห็นสิ่งเดียวกันนี้ในตัวอย่างjava.util.Timer
:
import java.util.*;
class Main {
public static void main(String[] args) {
Timer mediator = new Timer("Mediator");
TimerTask command = new TimerTask() {
@Override
public void run() {
System.out.println("Command pattern");
mediator.cancel();
}
};
mediator.schedule(command, 1000);
}
}
ตัวอย่างดูเหมือนรูปแบบคำสั่งมากกว่า และแก่นแท้ของรูปแบบ "ผู้ไกล่เกลี่ย" ถูกซ่อนอยู่ในการนำTimer
"a" ไปใช้ ภายในตัวจับเวลาจะมีคิวงานTaskQueue
และมีTimerThread
เธรด เราในฐานะลูกค้าของคลาสนี้ ไม่ได้โต้ตอบกับพวกเขา แต่โต้ตอบกับTimer
อ็อบเจ็กต์ ซึ่งตอบสนองต่อการเรียกเมธอดของเรา เข้าถึงเมธอดของอ็อบเจ็กต์อื่น ๆ ที่มันเป็นตัวกลาง ภายนอกอาจดูคล้ายกับ "Facade" มาก แต่ความแตกต่างก็คือเมื่อใช้ Facade ส่วนประกอบจะไม่รู้ว่า Facade มีอยู่จริงและพูดคุยกัน และเมื่อใช้ "ตัวกลาง" องค์ประกอบจะรู้และใช้ตัวกลางแต่ไม่ได้ติดต่อกันโดยตรง ควรพิจารณารูปแบบ “ Template Method ” รูปแบบชัดเจนจากชื่อ บรรทัดล่างคือโค้ดถูกเขียนในลักษณะที่ผู้ใช้โค้ด (นักพัฒนา) ได้รับเทมเพลตอัลกอริทึมบางส่วนซึ่งขั้นตอนต่างๆ ได้รับอนุญาตให้กำหนดใหม่ได้ สิ่งนี้ช่วยให้ผู้ใช้โค้ดไม่ต้องเขียนอัลกอริธึมทั้งหมด แต่ให้คิดเฉพาะวิธีการดำเนินการขั้นตอนหนึ่งของอัลกอริธึมนี้อย่างถูกต้องเท่านั้น ตัวอย่างเช่น Java มีคลาสนามธรรมAbstractList
ที่กำหนดพฤติกรรมของตัววนซ้ำList
ด้วย อย่างไรก็ตาม ตัว วนซ้ำเองก็ใช้เมธอดลีฟ เช่น: get
, set
, ลักษณะการทำงานของวิธีการเหล่า นี้remove
ถูกกำหนดโดยนักพัฒนาของลูกหลาน AbstractList
ดังนั้นตัววนซ้ำในAbstractList
- จึงเป็นเทมเพลตสำหรับอัลกอริทึมสำหรับการวนซ้ำบนชีต และผู้พัฒนาการใช้งานเฉพาะAbstractList
เปลี่ยนพฤติกรรมของการวนซ้ำนี้โดยการกำหนดพฤติกรรมของขั้นตอนเฉพาะ รูปแบบสุดท้ายที่เราวิเคราะห์คือรูปแบบ “ Snapshot ” (Momento) สาระสำคัญของมันคือการรักษาสถานะบางอย่างของวัตถุด้วยความสามารถในการฟื้นฟูสถานะนี้ ตัวอย่างที่เป็นที่รู้จักมากที่สุดจาก JDK คือการทำให้เป็นอนุกรมของอ็อบเจ็กต์ เช่น java.io.Serializable
. ลองดูตัวอย่าง:
import java.io.*;
import java.util.*;
class Main {
public static void main(String[] args) throws IOException {
ArrayList<String> list = new ArrayList<>();
list.add("test");
// Save State
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try (ObjectOutputStream out = new ObjectOutputStream(stream)) {
out.writeObject(list);
}
// Load state
byte[] bytes = stream.toByteArray();
InputStream inputStream = new ByteArrayInputStream(bytes);
try (ObjectInputStream in = new ObjectInputStream(inputStream)) {
List<String> listNew = (List<String>) in.readObject();
System.out.println(listNew.get(0));
} catch (ClassNotFoundException e) {
// Do something. Can't find class fpr saved state
}
}
}

บทสรุป
ดังที่เราเห็นจากการรีวิวพบว่ามีลวดลายที่หลากหลายมาก แต่ละคนแก้ปัญหาของตัวเอง และการรู้รูปแบบเหล่านี้สามารถช่วยให้คุณเข้าใจวิธีเขียนระบบของคุณได้อย่างทันท่วงที เพื่อให้มีความยืดหยุ่น บำรุงรักษาได้ และทนทานต่อการเปลี่ยนแปลง และสุดท้ายคือลิงก์บางส่วนสำหรับการเจาะลึก:- แหล่งข้อมูลที่น่าทึ่งที่สุดสำหรับรูปแบบ: refactoring.guru
- เพลย์ลิสต์วิดีโอ " รูปแบบการออกแบบในการเขียนโปรแกรมเชิงวัตถุ "
- แบบฝึกหัดการออกแบบรูปแบบ
- การทดสอบรูปแบบ
- หลักสูตรรูปแบบบน Udemy
- หลักสูตรรูปแบบบน Coursera
GO TO FULL VERSION