"สวัสดีเพื่อน!"
"สวัสดี บิลาโบ!"
“เรายังเหลือเวลาอีกพอสมควร ดังนั้นฉันจะบอกคุณเกี่ยวกับรูปแบบอีกสามรูปแบบ”
“อีกสาม? มีทั้งหมดกี่ตัว?”
"ปัจจุบันมีรูปแบบยอดนิยมหลายสิบรูปแบบ แต่จำนวน «การแก้ปัญหาที่ประสบความสำเร็จ» มีไม่จำกัด"
“เข้าใจแล้ว ฉันต้องเรียนรู้รูปแบบต่างๆ มากมาย?”
"จนกว่าคุณจะมีประสบการณ์ในการเขียนโปรแกรมจริงๆ พวกเขาจะไม่ให้อะไรคุณมากนัก"
"คุณควรได้รับประสบการณ์เพิ่มขึ้นเล็กน้อย จากนั้นในหนึ่งปี ให้กลับมาที่หัวข้อนี้และพยายามทำความเข้าใจให้ลึกซึ้งยิ่งขึ้น รูปแบบการออกแบบที่ได้รับความนิยมสูงสุดอย่างน้อยสองโหล"
"มันเป็นบาปที่จะไม่ใช้ประสบการณ์ของคนอื่นและประดิษฐ์บางสิ่งบางอย่างแทนเป็นครั้งที่ 110"
"ฉันเห็นด้วย."
"งั้นเรามาเริ่มกันเลย"
รูปแบบอะแดปเตอร์ (หรือกระดาษห่อหุ้ม)

"ลองนึกภาพว่าคุณมาที่ประเทศจีนแล้วพบว่าปลั๊กไฟมีมาตรฐานที่ต่างออกไป รูไม่กลมแต่แบน ในกรณีนี้ คุณจะต้องมีอะแดปเตอร์"
"สิ่งที่คล้ายกันสามารถเกิดขึ้นได้ในการเขียนโปรแกรม คลาสทำงานบนอินเทอร์เฟซที่คล้ายกันแต่ต่างกัน ดังนั้นเราจึงจำเป็นต้องสร้างอะแดปเตอร์ระหว่างกัน"
"นี่คือลักษณะ:"
interface Time
{
int getSeconds();
int getMinutes();
int getHours();
}
interface TotalTime
{
int getTotalSeconds();
}
"สมมติว่าเรามีสองอินเทอร์เฟซ: เวลา และ เวลาทั้งหมด "
"อินเทอร์เฟซเวลาช่วยให้คุณได้รับเวลาปัจจุบันโดยใช้เมธอดgetSeconds (), getMinutes () และ getHours ()"
" อินเทอร์เฟซ TotalTimeช่วยให้คุณได้รับจำนวนวินาทีที่ผ่านไปตั้งแต่เที่ยงคืนถึงช่วงเวลาปัจจุบัน"
"เราควรทำอย่างไรหากมี วัตถุ TotalTimeแต่เราต้องการ วัตถุ เวลาหรือในทางกลับกัน"
"เราสามารถเขียนคลาสอะแด็ปเตอร์สำหรับสิ่งนี้ได้ ตัวอย่างเช่น:"
class TotalTimeAdapter implements Time
{
private TotalTime totalTime;
public TotalTimeAdapter(TotalTime totalTime)
{
this.totalTime = totalTime;
}
int getSeconds()
{
return totalTime.getTotalSeconds() % 60; // seconds
}
int getMinutes()
{
return totalTime.getTotalSeconds() / 60; // minutes
}
int getHours()
{
return totalTime.getTotalSeconds() / (60 * 60); // hours
}
}
TotalTime totalTime = TimeManager.getCurrentTime();
Time time = new TotalTimeAdapter(totalTime);
System.out.println(time.getHours() + " : " + time.getMinutes () + " : " +time.getSeconds());
"และอะแดปเตอร์ในทิศทางอื่น:"
class TimeAdapter implements TotalTime
{
private Time time;
public TimeAdapter(Time time)
{
this.time = time;
}
int getTotalSeconds()
{
return time.getHours() * 60 * 60 + time.getMinutes() * 60 + time.getSeconds();
}
}
Time time = new Time();
TotalTime totalTime = new TimeAdapter(time);
System.out.println(time.getTotalSeconds());
“อา ฉันชอบนะ แต่มีตัวอย่างบ้างไหม?”
"แน่นอน ตัวอย่างเช่น InputStreamReader เป็นอะแดปเตอร์แบบคลาสสิก โดยจะแปลง InputStream เป็น Reader"
"บางครั้งรูปแบบนี้เรียกอีกอย่างว่า wrapper เนื่องจากคลาสใหม่ 'wraps' วัตถุอื่น"
"คุณสามารถอ่านสิ่งที่น่าสนใจอื่น ๆได้ที่นี่ "
รูปแบบหนังสือมอบฉันทะ
"รูปแบบพร็อกซีค่อนข้างคล้ายกับรูปแบบแรปเปอร์ แต่จุดประสงค์ไม่ใช่เพื่อแปลงอินเทอร์เฟซ แต่เพื่อควบคุมการเข้าถึงวัตถุต้นฉบับที่จัดเก็บไว้ในคลาสพร็อกซี นอกจากนี้ ทั้งคลาสดั้งเดิมและพร็อกซีมักจะมีอินเทอร์เฟซเดียวกัน ซึ่งทำให้ง่ายต่อการแทนที่วัตถุของคลาสเดิมด้วยวัตถุพร็อกซี"
"ตัวอย่างเช่น:"
interface Bank
{
public void setUserMoney(User user, double money);
public int getUserMoney(User user);
}
class CitiBank implements Bank
{
public void setUserMoney(User user, double money)
{
UserDAO.updateMoney(user, money);
}
public int getUserMoney(User user)
{
return UserDAO.getMoney(user);
}
}
class BankSecurityProxy implements Bank
{
private Bank bank;
public BankSecurityProxy(Bank bank)
{
this.bank = bank;
}
public void setUserMoney(User user, double money)
{
if (!SecurityManager.authorize(user, BankAccounts.Manager))
throw new SecurityException("User can’t change money value");
bank.setUserMoney(user, money);
}
public int getUserMoney(User user)
{
if (!SecurityManager.authorize(user, BankAccounts.Manager))
throw new SecurityException("User can’t get money value");
return bank.getUserMoney(user);
}
}
"ในตัวอย่างข้างต้น เราได้อธิบาย อินเทอร์เฟซ ของธนาคารและ คลาส CitiBankซึ่งเป็นการใช้งานอินเทอร์เฟซนี้"
"อินเทอร์เฟซช่วยให้คุณได้รับหรือเปลี่ยนยอดเงินในบัญชีของผู้ใช้"
จากนั้นเราก็สร้างBankSecurityProxyซึ่งใช้งาน อินเทอร์เฟซ ธนาคารและเก็บข้อมูลอ้างอิงไปยังอินเทอร์เฟซอื่นของธนาคาร วิธีการของคลาสนี้จะตรวจสอบว่าผู้ใช้เป็นเจ้าของบัญชีหรือผู้จัดการธนาคาร ถ้าไม่ใช่ แสดงว่า SecurityException จะถูกส่งออกไป"
"นี่คือวิธีการทำงานในทางปฏิบัติ:"
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank.setUserMoney(user, 1000000);
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank = new BankSecurityProxy(bank);
bank.setUserMoney(user, 1000000);
"ในตัวอย่างแรก เราสร้างวัตถุธนาคารและเรียกใช้เมธอดsetUserMoney
"ในตัวอย่างที่สอง เรารวมออบเจกต์ธนาคารเดิมไว้ใน ออบเจ็กต์ BankSecurityProxyโดยมีอินเทอร์เฟซเดียวกัน ดังนั้นโค้ดที่ตามมาจึงทำงานต่อไปเหมือนเดิม แต่ตอนนี้จะมีการตรวจสอบความปลอดภัยทุกครั้งที่มีการเรียกใช้เมธอด"
"เย็น!"
"ใช่ คุณสามารถมีผู้รับมอบฉันทะจำนวนมากได้ ตัวอย่างเช่น คุณสามารถเพิ่มผู้รับมอบฉันทะอีกรายที่ตรวจสอบว่ายอดเงินในบัญชีมีมากเกินไปหรือไม่ ผู้จัดการธนาคารอาจตัดสินใจใส่เงินจำนวนมากในบัญชีของเขาเองและหลบหนีไปยังคิวบาพร้อมกับเงิน "
"ยิ่งไปกว่านั้น... การสร้างห่วงโซ่ของออบเจกต์ทั้งหมดนี้สามารถใส่ลงใน คลาส BankFactoryซึ่งคุณสามารถเปิด/ปิดสิ่งที่คุณต้องการได้"
" BufferedReaderทำงานโดยใช้หลักการที่คล้ายกัน มันเป็นReaderแต่ทำงานเพิ่มเติม"
"วิธีการนี้ช่วยให้คุณ «ประกอบ» วัตถุที่มีฟังก์ชันการทำงานที่จำเป็นจาก «ชิ้นส่วน» ต่างๆ ได้"
"โอ้ ฉันเกือบลืม พร็อกซีถูกใช้อย่างแพร่หลายมากกว่าที่ฉันเพิ่งแสดงให้คุณ คุณสามารถอ่านเกี่ยวกับการใช้งานอื่น ๆ ได้ที่นี่ "
ลายสะพาน

"บางครั้งเมื่อโปรแกรมทำงาน จำเป็นต้องเปลี่ยนฟังก์ชันการทำงานของวัตถุอย่างมาก ตัวอย่างเช่น สมมติว่าคุณมีเกมที่มีตัวละครลาซึ่งต่อมากลายเป็นมังกรโดยผู้วิเศษ มังกรมีพฤติกรรมและคุณสมบัติที่แตกต่างกันอย่างสิ้นเชิง แต่มันคือ ของชิ้นเดียวกัน!"
"เราไม่สามารถสร้างวัตถุใหม่และเสร็จสิ้นกับมันได้หรือไม่"
"ไม่เสมอไป สมมติว่าลาของคุณเป็นเพื่อนกับตัวละครหลายตัว หรือบางทีมันอาจอยู่ภายใต้อิทธิพลของเวทมนตร์หลายคาถา หรือมันเกี่ยวข้องกับภารกิจบางอย่าง กล่าวอีกนัยหนึ่ง วัตถุนั้นอาจถูกใช้งานในหลายสถานที่แล้ว— และเชื่อมโยงกับออบเจกต์อื่นๆ มากมาย ดังนั้น ในกรณีนี้ จึงไม่ใช่ตัวเลือกที่จะสร้างออบเจกต์ใหม่เพียงอย่างเดียว"
“อืม แล้วจะทำอะไรได้ล่ะ”
"รูปแบบสะพานเป็นหนึ่งในโซลูชั่นที่ประสบความสำเร็จมากที่สุด"
"รูปแบบนี้นำมาซึ่งการแยกวัตถุออกเป็นสองวัตถุ: «วัตถุส่วนต่อประสาน» และ «วัตถุการใช้งาน»"
"ความแตกต่างระหว่างอินเทอร์เฟซและคลาสที่ใช้งานคืออะไร"
"ด้วยอินเทอร์เฟซและคลาส เราลงเอยด้วยวัตถุชิ้นเดียว แต่ที่นี่ เรามีสองชิ้น ดูตัวอย่างนี้:"
class User
{
private UserImpl realUser;
public User(UserImpl impl)
{
realUser = impl;
}
public void run() //Run
{
realUser.run();
}
public void fly() //Fly
{
realUser.fly();
}
}
class UserImpl
{
public void run()
{
}
public void fly()
{
}
}
"จากนั้น คุณสามารถประกาศคลาสย่อยของ UserImpl ได้หลายคลาส เช่นUserDonkey (ลา) และUserDragon (มังกร)"
“เหมือนกันเลย ฉันไม่เข้าใจจริงๆ ว่าวิธีนี้จะทำงานยังไง”
"อืม อะไรประมาณนี้:"
class User
{
private UserImpl realUser;
public User(UserImpl impl)
{
realUser = impl;
}
public void transformToDonkey()
{
realUser = new UserDonkeyImpl();
}
public void transformToDragon()
{
realUser = new UserDragonImpl();
}
}
User user = new User(new UserDonkey()); // Internally, we're a donkey
user.transformToDragon(); // Now we're a dragon internally
"ดังนั้นมันจึงเหมือนกับพร็อกซี"
"ใช่ แต่ในพร็อกซี อ็อบเจ็กต์หลักสามารถเก็บไว้ที่ไหนสักแห่งแยกต่างหาก และโค้ดจะทำงานร่วมกับพร็อกซีแทน ในที่นี้เรากำลังบอกว่าทุกคนทำงานกับอ็อบเจกต์หลัก แต่ส่วนต่างๆ ของมันเปลี่ยนแปลงภายใน"
"อ่า ขอบคุณ ขอลิงค์สำหรับอ่านเพิ่มเติมหน่อยได้มั้ยคะ"
"แน่นอน Amigo เพื่อนของฉัน ไปเลย: รูปแบบสะพาน "
GO TO FULL VERSION