![รูปแบบการออกแบบอะแด็ปเตอร์ช่วยแก้ปัญหาอะไรได้บ้าง - 1](https://cdn.codegym.cc/images/article/2a00c183-20f3-4a73-abc5-f1f896e18a52/800.jpeg)
เพิ่มเติมเกี่ยวกับปัญหา
ก่อนอื่น เราจะจำลองพฤติกรรมของระบบเก่า สมมติว่ามันสร้างข้อแก้ตัวสำหรับการไปทำงานหรือไปโรงเรียนสาย ในการทำเช่น นี้มีExcuse
อินเทอร์เฟซที่มีgenerateExcuse()
และเมธอด likeExcuse()
dislikeExcuse()
public interface Excuse {
String generateExcuse();
void likeExcuse(String excuse);
void dislikeExcuse(String excuse);
}
คลาสWorkExcuse
ใช้อินเทอร์เฟซนี้:
public class WorkExcuse implements Excuse {
private String[] excuses = {"in an incredible confluence of circumstances, I ran out of hot water and had to wait until sunlight, focused using a magnifying glass, heated a mug of water so that I could wash.",
"the artificial intelligence in my alarm clock failed me, waking me up an hour earlier than normal. Because it is winter, I thought it was still nighttime and I fell back asleep. Everything after that is a bit hazy.",
"my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia."};
private String [] apologies = {"This will not happen again, of course. I'm very sorry.", "I apologize for my unprofessional behavior.", "There is no excuse for my actions. I am not worthy of this position."};
@Override
public String generateExcuse() { // Randomly select an excuse from the array
String result = "I was late today because " + excuses[(int) Math.round(Math.random() + 1)] + "\\n" +
apologies[(int) Math.round(Math.random() + 1)];
return result;
}
@Override
public void likeExcuse(String excuse) {
// Duplicate the element in the array so that its chances of being chosen are higher
}
@Override
public void dislikeExcuse(String excuse) {
// Remove the item from the array
}
}
ลองทดสอบตัวอย่างของเรา:
Excuse excuse = new WorkExcuse();
System.out.println(excuse.generateExcuse());
เอาท์พุต:
"I was late today because my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia.
I apologize for my unprofessional behavior.
ลองจินตนาการว่าคุณได้เปิดบริการสร้างข้อแก้ตัว รวบรวมสถิติ และสังเกตเห็นว่าผู้ใช้ส่วนใหญ่ของคุณเป็นนักศึกษามหาวิทยาลัย เพื่อให้บริการกลุ่มนี้ได้ดียิ่งขึ้น คุณได้ขอให้นักพัฒนารายอื่นสร้างระบบที่สร้างข้อแก้ตัวสำหรับนักศึกษามหาวิทยาลัยโดยเฉพาะ ทีมพัฒนาได้ทำการวิจัยตลาด จัดอันดับข้อแก้ตัว ติดตั้งปัญญาประดิษฐ์ และรวมบริการเข้ากับรายงานการจราจร รายงานสภาพอากาศ และอื่นๆ ตอนนี้ คุณมีไลบรารีสำหรับสร้างข้อแก้ตัวสำหรับนักศึกษามหาวิทยาลัย แต่มีอินเทอร์เฟซที่แตกต่างกันStudentExcuse
:
public interface StudentExcuse {
String generateExcuse();
void dislikeExcuse(String excuse);
}
อินเทอร์เฟซนี้มีสองวิธี: generateExcuse
ซึ่งสร้างข้อแก้ตัว และdislikeExcuse
ซึ่งจะป้องกันไม่ให้ข้อแก้ตัวปรากฏขึ้นอีกในอนาคต ไม่สามารถแก้ไขไลบรารีของบุคคลที่สามได้ นั่นคือคุณไม่สามารถเปลี่ยนซอร์สโค้ดได้ สิ่งที่เรามีตอนนี้คือระบบที่มีสองคลาสที่ใช้Excuse
อินเทอร์เฟซ และไลบรารีที่มีSuperStudentExcuse
คลาสที่ใช้StudentExcuse
อินเทอร์เฟซ:
public class SuperStudentExcuse implements StudentExcuse {
@Override
public String generateExcuse() {
// Logic for the new functionality
return "An incredible excuse adapted to the current weather conditions, traffic jams, or delays in public transport schedules.";
}
@Override
public void dislikeExcuse(String excuse) {
// Adds the reason to a blacklist
}
}
ไม่สามารถเปลี่ยนรหัสได้ ลำดับชั้นของคลาสปัจจุบันมีลักษณะดังนี้: ![รูปแบบการออกแบบอะแด็ปเตอร์ช่วยแก้ปัญหาอะไรได้บ้าง - 2](https://cdn.codegym.cc/images/article/5eb3ced7-e0e6-4dec-94a1-298d797d0e7d/800.jpeg)
![รูปแบบการออกแบบอะแด็ปเตอร์ช่วยแก้ปัญหาอะไรได้บ้าง - 3](https://cdn.codegym.cc/images/article/97a9295a-155b-4043-9c37-b187044a02df/800.jpeg)
Excuse
อินเทอร์เฟซ แต่ลำดับชั้นพิเศษเป็นสิ่งที่ไม่พึงปรารถนาในการใช้งานที่ร้ายแรง: การแนะนำองค์ประกอบรูททั่วไปทำให้สถาปัตยกรรมเสียหาย คุณควรใช้คลาสระดับกลางที่จะให้เราใช้ทั้งฟังก์ชันใหม่และเก่าโดยสูญเสียน้อยที่สุด กล่าวโดยย่อคุณต้องมีอะแดปเตอร์
หลักการเบื้องหลังรูปแบบอะแดปเตอร์
อะแด็ปเตอร์เป็นออบเจกต์ระดับกลางที่ช่วยให้การเรียกเมธอดของอ็อบเจ็กต์หนึ่งเข้าใจได้โดยอีกอ็อบเจ็กต์หนึ่ง ลองใช้อะแดปเตอร์สำหรับตัวอย่างของเราและเรียกมันMiddleware
ว่า อะแดปเตอร์ของเราต้องใช้อินเทอร์เฟซที่เข้ากันได้กับหนึ่งในวัตถุ ปล่อยให้มันExcuse
เป็น สิ่งนี้ทำให้สามารถMiddleware
เรียกใช้เมธอดของวัตถุตัวแรกได้ Middleware
รับสายและส่งต่อด้วยวิธีที่เข้ากันได้ไปยังวัตถุที่สอง นี่คือMiddleware
การใช้งานกับgenerateExcuse
และdislikeExcuse
วิธีการ:
public class Middleware implements Excuse { // 1. Middleware becomes compatible with WorkExcuse objects via the Excuse interface
private StudentExcuse superStudentExcuse;
public Middleware(StudentExcuse excuse) { // 2. Get a reference to the object being adapted
this.superStudentExcuse = excuse;
}
@Override
public String generateExcuse() {
return superStudentExcuse.generateExcuse(); // 3. The adapter implements an interface method
}
@Override
public void dislikeExcuse(String excuse) {
// The method first adds the excuse to the blacklist,
// Then passes it to the dislikeExcuse method of the superStudentExcuse object.
}
// The likeExcuse method will appear later
}
การทดสอบ (ในรหัสลูกค้า):
public class Test {
public static void main(String[] args) {
Excuse excuse = new WorkExcuse(); // We create objects of the classes
StudentExcuse newExcuse = new SuperStudentExcuse(); // that must be compatible.
System.out.println("An ordinary excuse for an employee:");
System.out.println(excuse.generateExcuse());
System.out.println("\n");
Excuse adaptedStudentExcuse = new Middleware(newExcuse); // Wrap the new functionality in the adapter object
System.out.println("Using new functionality with the adapter:");
System.out.println(adaptedStudentExcuse.generateExcuse()); // The adapter calls the adapted method
}
}
เอาท์พุต:
An ordinary excuse for an employee:
I was late today because my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia.
There is no excuse for my actions. I am not worthy of this position.
Using new functionality with the adapter:
ข้อแก้ตัวที่เหลือเชื่อที่ปรับให้เข้ากับสภาพอากาศในปัจจุบัน การจราจรติดขัด หรือความล่าช้าของตารางการขนส่งสาธารณะ เมธอดgenerateExcuse
เพียงแค่ส่งการเรียกไปยังอ็อบเจกต์อื่น โดยไม่มีการเปลี่ยนแปลงเพิ่มเติมใดๆ วิธีการ นี้dislikeExcuse
กำหนดให้เราต้องขึ้นบัญชีดำเพื่อแก้ตัวก่อน ความสามารถในการประมวลผลข้อมูลระดับกลางคือเหตุผลที่ผู้คนชื่นชอบรูปแบบอะแดปเตอร์ แล้วเมธอดล่ะlikeExcuse
ซึ่งเป็นส่วนหนึ่งของExcuse
อินเทอร์เฟซ แต่ไม่ใช่ส่วนหนึ่งของStudentExcuse
อินเทอร์เฟซ ฟังก์ชันใหม่ไม่รองรับการทำงานนี้ ถูกUnsupportedOperationException
ประดิษฐ์ขึ้นสำหรับสถานการณ์นี้ จะถูกส่งออกหากการดำเนินการที่ร้องขอไม่ได้รับการสนับสนุน มาใช้กันเถอะ นี่คือลักษณะของMiddleware
การใช้งานใหม่ของคลาส:
public class Middleware implements Excuse {
private StudentExcuse superStudentExcuse;
public Middleware(StudentExcuse excuse) {
this.superStudentExcuse = excuse;
}
@Override
public String generateExcuse() {
return superStudentExcuse.generateExcuse();
}
@Override
public void likeExcuse(String excuse) {
throw new UnsupportedOperationException("The likeExcuse method is not supported by the new functionality");
}
@Override
public void dislikeExcuse(String excuse) {
// The method accesses a database to fetch additional information,
// and then passes it to the superStudentExcuse object's dislikeExcuse method.
}
}
เมื่อมองแวบแรก โซลูชันนี้ดูไม่ค่อยดีนัก แต่การเลียนแบบฟังก์ชันการทำงานอาจทำให้สถานการณ์ซับซ้อนขึ้นได้ หากไคลเอนต์ให้ความสนใจ และอะแดปเตอร์ได้รับการจัดทำเป็นเอกสารอย่างดี โซลูชันดังกล่าวก็เป็นที่ยอมรับได้
เมื่อใดควรใช้อะแดปเตอร์
-
เมื่อคุณต้องการใช้คลาสของบุคคลที่สาม แต่อินเทอร์เฟซนั้นเข้ากันไม่ได้กับแอปพลิเคชันหลัก ตัวอย่างด้านบนแสดงวิธีสร้างออบเจกต์อะแดปเตอร์ที่รวมการโทรในรูปแบบที่ออบเจ็กต์เป้าหมายสามารถเข้าใจได้
-
เมื่อคลาสย่อยที่มีอยู่หลายคลาสต้องการฟังก์ชันทั่วไปบางอย่าง แทนที่จะสร้างคลาสย่อยเพิ่มเติม (ซึ่งจะนำไปสู่การทำซ้ำโค้ด) ควรใช้อะแดปเตอร์
ข้อดีและข้อเสีย
ข้อได้เปรียบ:อะแด็ปเตอร์จะซ่อนรายละเอียดการประมวลผลคำขอจากอ็อบเจ็กต์หนึ่งไปยังอีกอ็อบเจ็กต์หนึ่งจากไคลเอ็นต์ รหัสลูกค้าไม่คิดเกี่ยวกับการจัดรูปแบบข้อมูลหรือจัดการการเรียกไปยังวิธีการเป้าหมาย มันซับซ้อนเกินไป และโปรแกรมเมอร์ก็ขี้เกียจ :) ข้อเสีย:โค้ดเบสของโปรเจ็กต์ซับซ้อนด้วยคลาสเพิ่มเติม หากคุณมีอินเทอร์เฟซที่เข้ากันไม่ได้จำนวนมาก จำนวนคลาสเพิ่มเติมอาจไม่สามารถจัดการได้อย่าสับสนระหว่างอะแดปเตอร์กับส่วนหน้าหรือตัวตกแต่ง
ด้วยการตรวจสอบเพียงผิวเผิน อะแดปเตอร์อาจสับสนกับรูปแบบด้านหน้าอาคารและตัวตกแต่ง ความแตกต่างระหว่างอะแด็ปเตอร์และส่วนหน้าคือส่วนหน้าจะแนะนำอินเทอร์เฟซใหม่และรวมระบบย่อยทั้งหมด และมัณฑนากรซึ่งแตกต่างจากอะแดปเตอร์คือเปลี่ยนวัตถุเองแทนที่จะเป็นอินเทอร์เฟซอัลกอริทึมทีละขั้นตอน
-
ก่อนอื่น ต้องแน่ใจว่าคุณมีปัญหาที่รูปแบบนี้สามารถแก้ไขได้
-
กำหนดอินเทอร์เฟซไคลเอ็นต์ที่จะใช้โต้ตอบทางอ้อมกับวัตถุที่เข้ากันไม่ได้
-
ทำให้คลาสอะแด็ปเตอร์สืบทอดอินเตอร์เฟสที่กำหนดไว้ในขั้นตอนก่อนหน้า
-
ในคลาสอะแด็ปเตอร์ ให้สร้างฟิลด์เพื่อจัดเก็บการอ้างอิงไปยังอ็อบเจกต์อะแด็ปเตอร์ การอ้างอิงนี้จะถูกส่งผ่านไปยังตัวสร้าง
-
ใช้วิธีอินเทอร์เฟซไคลเอนต์ทั้งหมดในอะแด็ปเตอร์ วิธีการอาจ:
-
ส่งต่อสายโดยไม่ต้องทำการเปลี่ยนแปลงใดๆ
-
แก้ไขหรือเสริมข้อมูล เพิ่ม/ลด จำนวนการโทรไปยังเมธอดเป้าหมาย เป็นต้น
-
ในกรณีที่รุนแรง หากเมธอดใดยังคงใช้ร่วมกันไม่ได้ ให้โยน UnsupportedOperationException การดำเนินการที่ไม่ได้รับการสนับสนุนจะต้องมีการจัดทำเป็นเอกสารอย่างเคร่งครัด
-
-
หากแอ็พพลิเคชันใช้เฉพาะคลาสของอะแด็ปเตอร์ผ่านไคลเอ็นต์อินเทอร์เฟซ (ตามตัวอย่างด้านบน) อะแด็ปเตอร์จะขยายได้โดยไม่ลำบากในอนาคต