Design Pattern 101 — Proxy Pattern

Phayao Boonon
3 min readAug 25, 2019

--

Proxy Pattern เป็น Design Pattern ในกลุ่ม Structural Pattern สุดท้ายในซีรี่ย์นี้ ซึ่ง pattern นี้เปลี่ยบเสมือนตัวแทนของ object ที่ต้องการเข้าถึง แต่ไม่ได้เข้าถึง object นั้นจริงๆ ก็จะใช้ proxy มาเป็นตัวแทน ในบทความนี้จะมาทบทวนและเรียนรู้ว่า Proxy Pattern มาช่วยแก้ปัญหาอะไรได้บ้าง

https://refactoring.guru/design-patterns/proxy

Virtual Proxy Pattern

Problem

ถ้าเราต้องพัฒนาแอพพลิเคชันที่แสดงรูปโปสเตอร์ของรายการหนังที่ชื่นชอบ เราอาจจะสร้าง menu สำหรับชื่อหนังแต่ละเรื่อง และเมื่อคลิ๊กที่ชื่อหนังก็จะไปโหลดรูปโปสเตอร์หนังจาก network (internet) มาแสดงบนหน้าแอพพลิเคชัน

ถ้าใช้ Swing GUI widget toolkitในการสร้าง GUI ของ application ของเราอาจจะสร้างเป็น Icon และให้มันโหลดรูปภาพจาก network มาแสดง

แต่ปัญหาคือความเร็วของ network ของผู้ใช้งานแต่ละคน อาจจะไม่เท่ากัน บางคนใช้งาน internet ความเร็วสูงทำให้โหลดรูปภาพได้ไว แต่บางคนอาจจะใช้ internet ความเร็วต่ำทำให้โหลดรูปได้ช้า สำหรับ internet ความเร็วสูงอาจจะไม่มีปัญหา แต่สำหรับ internet ความเร็วต่ำนั้น โปรแกรมของเราอาจจะค้างอยู่ในช่วงที่โหลดรูปภาพ ซึ่งเราไม่ต้องการให้ผู้ใช้งานรู้สึกแบบนั้น เราอาจจะให้แสดงข้อความ ในระหว่างที่มีการโหลดรูปภาพอยู่

private void mainWindow() {
// setup frame and menus

Icon icon = new ImageIcon(initialUrl); // create icon
imageComponent = new ImageComponent(icon);
frame.getContentPane().add(imageComponent);

}

Solution

ดังนั้นจากที่เราจะสร้าง Icon แล้วโหลดรูปภาพตรงๆ เราจะใช้ Virtual Proxy Pattern มาเป็นตัวแทนสำหรับสร้าง Icon และโหลดรูปภาพให้ แต่ถ้ายังไม่ได้โหลดรูปภาพก็จะแสดง ข้อความ ว่ากำลังโหลดรูปภาพอยู่

โดยที่จะสร้าง Proxy Class ImageProxy ที่ implement interface Icon ของ Swing ซึ่งมี method painIcon() , getIconWidth() และ getIconHeight()

public interface Icon
{
void paintIcon(Component c, Graphics g, int x, int y);
int getIconWidth();
int getIconHeight();
}

เพื่อให้เป็น type เดียวกับ ImageIcon ที่จะไปโหลดรูปภาพจาก network มาให้

public class ImageProxy implements Icon {
ImageIcon imageIcon;
URL imageURL;
Thread retrievalThread;
boolean retrieving = false;

public ImageProxy(URL url) {
this.imageURL = url;
}

@Override
public int getIconWidth() {
if (imageIcon != null) {
return imageIcon.getIconWidth();
}
return 500;
}

@Override
public int getIconHeight() {
if (imageIcon != null) {
return imageIcon.getIconHeight();
}
return 500;
}

@Override
public void paintIcon(final Component c, Graphics g, int x, int y) {
if (imageIcon != null) {
imageIcon.paintIcon(c, g, x, y);
} else {
g.drawString("Loading movie poster, please wait ..",
x + 120, y + 190);
if (!retrieving) {
retrieving = true;
retrievalThread = new Thread(new Runnable() {
@Override
public void run() {
imageIcon = new ImageIcon(imageURL);
c.repaint();
}
});
retrievalThread.start();
}
}
}
}

จะเห็นได้ว่า pass URL ของรูปภาพที่จะโหลดมาที่ constractor ของ ImageProxy และค่าเริ่มต้นของตัวแปล imageIcon นั้นเป็น null ซึ่งหมายความว่าใน ImageProxy นี้ยังไม่ได้โหลดรูปภาพมา โดยที่จะตรวจสอบในตอนแรกของ method paintIcon ซึ่งเป็น null จะแสดงขอความ “Loading movie poster, please wait ..” และสร้าง thread ขึ้นมากทำการโหลดรูปภาพด้วย ImageIcon เมื่อโหลดเสร็จแล้วจะเรียก method repaint() เพื่อวาดรูปภาพนั้นบน frame (หน้าจอ)

การนำเอา ImageProxy ไปใช้งาน นั้นเพียงแค่เอาไปแทน class ImageIcon เดิมด้วย ImageProxy

private void mainWindow() {
// setup frame and menus

Icon icon = new ImageProxy(initialUrl); // change this
imageComponent = new ImageComponent(icon);
frame.getContentPane().add(imageComponent);

}

และเมื่อรันแอพพลิเคชันก็จะเห็นข้อความแสดงในระหว่างที่โหลดรูปภาพ

Proxy Pattern จะมี ตัวแทน (surrogate) หรือ ตัวยึด (placeholder) สำหรับ object อื่น เพื่อจะควาบคุมการเข้าถึงของ object นั้น

โดยที่ proxy จะเปลี่ยบเสมือนว่าเป็น ตัวแทน ของอีก object หนึ่ง แต่ Proxy Pattern มีหลายรูปแบบ คือ

  • Remote Proxy — เป็นการควบคุมการเข้าถึงของ remote object
  • Virtual Proxy — เป็นการควบคุมการเข้าถึงของ resource ที่ใช้ทรัพยาการมากในการสร้าง (ที่แสดงไปข้างต้น)
  • Protection Proxy — เป็นการควบคุมการเข้าถึง resource ด้วยสิทธิการใช้งาน
Head First Design Patterns, หน้า 461

จาก diagram เรามี Subject ซี่งเป็น interface สำหรับ RealObject และ Proxy โดยการ implementation interface เดียวกัน Proxy สามารเป็น ตัวแทน (substituted) ของ RealObject ได้

ซึ่ง RealObject เป็น object จริงที่ทำงานในโลกความจริง โดยใช้ Proxy เป็นตัวแทนและควบคุมการเข้าถึงของ RealObject

Proxy จะเก็บ reference ของ RealObject ไว้ ในบางกรณี Proxy อาจจะทำหน้าที่สร้างและทำลาย RealObject ซึ่ง ผู้ใช้งาน (Client) จะติดต่อกับ RealObject ผ่านทาง Proxy เพราะว่า Proxy และ RealObject implement interface เดียวกัน คือ Subject

Proxy ยังควบคุมการเข้าถึงของ RealObject ซึ่งการควบคุมนี้อาจจะจำเป็นถ้า Subject ทำงานบน Remote Machine ในบางกรณีถ้า Subject ใช้ทรัพยากรมาในการสร้าง หรือในบางกรณี ถ้าการเข้าถึงของ Subject จำเป็นที่ต้องถูกปกป้อง

สรุป

จากการได้ทบทวนและใช้งาน Proxy Pattern นี้ทำให้ได้เห็นประโยชน์ของ pattern นี้ที่เป็นตัวแทนของ object จริงที่ตองการเข้าถึง แต่ในบทความนี้จะครอบคลุมเฉพาะแบบ Virtual Proxy เท่านั้นสำหรับ Remote Proxy และ Protectoin Proxy ไม่ได้ลงรายละเอียดแต่ก็สามารถหาอ่านได้ทั่วไป และเช่นเคยตัวอย่างของบทความนี้ก็เอามาจาก Head First Design Pattern นั้นเอง 😆

--

--

Phayao Boonon
Phayao Boonon

Written by Phayao Boonon

Software Engineer 👨🏻‍💻 Stay Hungry Stay Foolish

No responses yet