"สวัสดีเพื่อน!"

"สวัสดี บิลาโบ!"

“เรายังเหลือเวลาอีกพอสมควร ดังนั้นฉันจะบอกคุณเกี่ยวกับรูปแบบอีกสามรูปแบบ”

“อีกสาม? มีทั้งหมดกี่ตัว?”

"ปัจจุบันมีรูปแบบยอดนิยมหลายสิบรูปแบบ แต่จำนวน «การแก้ปัญหาที่ประสบความสำเร็จ» มีไม่จำกัด"

“เข้าใจแล้ว ฉันต้องเรียนรู้รูปแบบต่างๆ มากมาย?”

"จนกว่าคุณจะมีประสบการณ์ในการเขียนโปรแกรมจริงๆ พวกเขาจะไม่ให้อะไรคุณมากนัก"

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

"มันเป็นบาปที่จะไม่ใช้ประสบการณ์ของคนอื่นและประดิษฐ์บางสิ่งบางอย่างแทนเป็นครั้งที่ 110"

"ฉันเห็นด้วย."

"งั้นเรามาเริ่มกันเลย"

รูปแบบอะแดปเตอร์ (หรือกระดาษห่อหุ้ม)

รูปแบบ: Adapter, Proxy, Bridge - 1

"ลองนึกภาพว่าคุณมาที่ประเทศจีนแล้วพบว่าปลั๊กไฟมีมาตรฐานที่ต่างออกไป รูไม่กลมแต่แบน ในกรณีนี้ คุณจะต้องมีอะแดปเตอร์"

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

"นี่คือลักษณะ:"

ตัวอย่าง
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แต่ทำงานเพิ่มเติม"

"วิธีการนี้ช่วยให้คุณ «ประกอบ» วัตถุที่มีฟังก์ชันการทำงานที่จำเป็นจาก «ชิ้นส่วน» ต่างๆ ได้"

"โอ้ ฉันเกือบลืม พร็อกซีถูกใช้อย่างแพร่หลายมากกว่าที่ฉันเพิ่งแสดงให้คุณ คุณสามารถอ่านเกี่ยวกับการใช้งานอื่น ๆ ได้ที่นี่ "

ลายสะพาน

รูปแบบ: Adapter, Proxy, Bridge - 2

"บางครั้งเมื่อโปรแกรมทำงาน จำเป็นต้องเปลี่ยนฟังก์ชันการทำงานของวัตถุอย่างมาก ตัวอย่างเช่น สมมติว่าคุณมีเกมที่มีตัวละครลาซึ่งต่อมากลายเป็นมังกรโดยผู้วิเศษ มังกรมีพฤติกรรมและคุณสมบัติที่แตกต่างกันอย่างสิ้นเชิง แต่มันคือ ของชิ้นเดียวกัน!"

"เราไม่สามารถสร้างวัตถุใหม่และเสร็จสิ้นกับมันได้หรือไม่"

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

“อืม แล้วจะทำอะไรได้ล่ะ”

"รูปแบบสะพานเป็นหนึ่งในโซลูชั่นที่ประสบความสำเร็จมากที่สุด"

"รูปแบบนี้นำมาซึ่งการแยกวัตถุออกเป็นสองวัตถุ: «วัตถุส่วนต่อประสาน» และ «วัตถุการใช้งาน»"

"ความแตกต่างระหว่างอินเทอร์เฟซและคลาสที่ใช้งานคืออะไร"

"ด้วยอินเทอร์เฟซและคลาส เราลงเอยด้วยวัตถุชิ้นเดียว แต่ที่นี่ เรามีสองชิ้น ดูตัวอย่างนี้:"

ตัวอย่าง
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 เพื่อนของฉัน ไปเลย: รูปแบบสะพาน "