> For the complete documentation index, see [llms.txt](https://www.saladpuk.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.saladpuk.com/beginner-1/design-patterns/structural/proxy-pattern.md).

# Proxy Pattern

เจ้าตัวนี้ผมขอตั้งชื่อเป็นภาษาไทยว่า **ผู้ควบคุม** ละกัน ซึ่งมันอยู่ในกลุ่มของ [**🧱 Structural Patterns**](https://www.saladpuk.com/beginner-1/design-patterns/structural) โดยเจ้าตัวนี้จะมาช่วยแก้ปัญหาเมื่อ **เราต้องการจะควบคุมพฤติกรรม object ให้ได้ดั่งใจ** พูดแล้วก็ งงๆ ไปดูโจทย์ของเรากันเลยดีกว่าจะได้เข้าใจได้เร็วขึ้น

{% hint style="info" %}
**แนะนำให้อ่าน**\
บทความนี้เป็นส่วนหนึ่งของมหากาพย์ **Design Patterns** ที่จะมาเป็น guideline ในการแก้ปัญหาในการออกแบบซอฟต์แวร์โปรเจค หากใครสนใจอยากเข้าใจตั้งแต่ต้นว่ามันคืออะไร และเจ้า patterns ทั้ง 23 ตัวมีอะไรบ้าง ก็สามารถจิ้มตรงนี้เพื่อไปอ่านบทความหลักได้เบยครัช [👦 **Design Patterns**](https://saladpuk.gitbook.io/learn/beginner-1/design-patterns)
{% endhint %}

{% hint style="warning" %}
**หมายเหตุ**\
เนื้อหาของบทความนี้จะเน้นให้เข้าใจหลักการทำงานของ Design Patterns แต่ละตัว โดยภาพจากการ์ตูน One Piece มาใช้ประกอบ ซึ่งหลายๆอย่างนั้นมโนขึ้นมาเพื่อความสนุก และทำให้เข้าใจเนื้อหาได้ง่าย  *ลิขสิทธิ์ต่างๆอย่ามาจับผมนะผมโดนแมวน้ำครอบงำ + รู้เท่าไม่ถึงการ + ผมเป็นคนดี + ผมมีลูกมีเมียมีสามีที่ต้องดูแล* 😭
{% endhint %}

## 🧐 โจทย์

สมมุติว่ามี Web API ตัวหนึ่งชื่อ **พรหัก** ที่สอนธรรมะแบบแปลกๆแก่ชาวโลกอยู่ตัวหนึ่ง ซึ่งรัฐบาลโลกเห็นว่าเป็นภัยต่อความมั่นคง พลโท.การ์ป จึงสั่งการแบนมันทิ้งอย่างฉับพลัน

![](/files/-MLa6EVwq_vXBbgPkpC6)

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

1. ผู้ใช้ทุกคนต้อง **ใช้งาน API ได้เหมือนเดิมทุกอย่าง**
2. รัฐบาลโลกสามารถ **ควบคุม** เนื้อหาที่คิดว่าเป็นภัยต่อความมั่นคงได้
3. รัฐบาลโลก **กำหนดสิทธิในการเข้าถึง** เนื้อหาต่างๆได้
4. **เมื่อเราแก้ไขอะไรก็ตามผู้ใช้ต้องไม่รู้ตัวถึงการเปลี่ยนแปลงใดๆเลย**

เวลาที่ผู้ใช้จะเข้าเว็บอะไรก็ตาม มันจะต้องผ่าน **ผู้คุมกฎ** ก่อนเสมอ เพื่อให้ผู้คุมกฎไปเอาผลลัพท์กลับมาให้ดูนั่นเอง

🤔 ดังนั้นเราในฐาน Developer ที่ทำงานเป็นผู้คุมกฎ จะออกแบบยังไงถึงจะทำได้ครบทุกข้อตามที่รัฐบาลโลกสั่งมา ?

![](/files/-MLaFIza_yKdtYr0CH9m)

> หากไม่เห็นผมเขียนบทความใหม่ๆแล้ว ก็ฝากไปประกันตัวผมด้วย หรือว่างๆก็แวะมาทักทายกันได้นะ (ผมชอบกินเนื้อย่าง) 😭

## 🧒 แก้โจทย์ครั้งที่ 1

จากที่เล่ามาก็ดูเหมือนจะไม่มีไรยากชิมิ ในเมื่อทุกอย่างต้องผ่านเราอยู่แล้ว ดังนั้นเวลาที่ผู้ใช้ขอดูพระธรรมอะไรมา เราก็แค่ตรวจดูเนื้อหาในนั้นก่อนก็พอแล้วนิ

**เข้าใช้งานตามปรกติได้**\
ใครขออะไรมาก็ทำให้เขาไป แต่ขอตรวจเนื้อหาก่อนที่จะส่งไปให้ก่อนนะ

![](/files/-MLadhzi0jebAdaL3ARK)

**ควบคุมเนื้อหาได้**\
ถ้าตรวจเนื้อหาแล้วมันอาจเป็นภัยต่อความมั่นคง เราก็**เปลี่ยนเนื้อหาเป็นอย่างอื่นที่เราอยากให้เป็น**ไปซะก็จบละ

![](/files/-MLafKTqJqLRPP4lAyy2)

**กำหนดสิทธิ์ในการเข้าถึง**\
ถ้าเนื้อหาไม่ได้เป็นภัยอะไร แต่เรารู้สึกว่าไม่เหมาะสมกับคนกลุ่มนี้ เราก็เปลี่ยนเป็นเนื้อหาที่เราคิดว่าเหมาะสมแทน

![](/files/-MLauiUrnQUTe4fm6iim)

ก็ดูเหมือนจะได้เกือบครบหมดแล้วนะ ดังนั้นเขียนโค้ดนิดหน่อยละกัน ครืดๆ

![](/files/-MLalfo8VBeh9Nri4Bf9)

โดยจากรูปด้านบนจะเห็นว่า เมื่อไหร่ก็ตามที่ clients ขอดูเว็บ เราในฐานะผู้คุมกฎก็จะไปเรียกใช้ ApiRequestHandler ไปดึงข้อมูลต่อให้ ดังนั้นโค้ดที่ใช้ในการตรวจสอบเงื่อนไขสิ่งต่างๆก็จะอยู่ภายในคลาส ApiRequestHandler นั่นเอง

![](/files/-MLapE3MgXDvMnqjuQvH)

ดังนั้นโค้ดก็น่าจะออกมาราวๆนี้

```csharp
public class ApiRequestHandler
{
    private พรหัก api;
    public ApiRequestHandler(พรหัก api)
    {
        this.api = api;
    }
    
    public Content(string key)
    {
        var response = api.Content(key);
        // ตรวจสอบหรือแก้ไข response เพื่อให้เป็นแบบที่เราต้องการ
        return response;
    }
}
```

อะเชจากโค้ดด้านบนก็สามารถตอบโจทย์ทุกอย่างได้หมดละ แต่มันจะเกิดอะไรขึ้น **ถ้าเราอยากควบคุม api มากว่านี้ล่ะ?** ... หรือ **อยากเพิ่มเงื่อนไขเฉพาะเว็บนี้แต่เว็บอื่นไม่ต้องสนใจละ?** ... หรือ **ถ้า API ของพรหักเปลี่ยนล่ะ?** ... โค้ดใน ApiRequestHandler ก็จะต้องถูกแก้ไขอะดิ 😨

เพราะโค้ดของเรามันมีหลายอย่างที่ไม่ตรงหลักในการออกแบบที่ดี เช่น **SRP**, **OCP, DIP** ยังไงล่ะ

{% hint style="danger" %}
**Single-Responsibility Principle (SRP)**\
การออกแบบที่ละเมิดหลักในการออกแบบนี้จะทำให้ เวลาที่ Requirement เปลี่ยนมาทีนึง มันก็จมีโอกาสสูงมากที่การเปลี่ยนนั้นมันจะไปกระทบเจ้าสิ่งนั้น ทำให้เราต้องแก้ไขมัน ซึ่งผองเพื่อนอื่นๆที่มันดูแลอยู่นั้นไม่ได้เกี่ยวข้องเลยก็มีผลกระทบด้วยนั่นเอง ส่วนใครที่ลืมหรืออยากทบทวนเรื่อง SRP สามารถเข้าไปอ่านได้จากลิงค์นี้เบย [**Single-Responsibility Principle**](https://saladpuk.gitbook.io/learn/basic/solid/srp)
{% endhint %}

{% hint style="danger" %}
**Open & Close Principle (OCP)**\
การออกแบบที่ละเมิดหลักในการออกแบบนี้จะทำให้ทุกครั้งที่มีของใหม่ๆถูกเพิ่มเข้าไปปุ๊ป เราก็ต้องไปแก้โค้ดเดิมเสมอ สำหรับใครที่ลืมหลักในการออกแบบเรื่องนี้ไปแล้วให้กดอ่านได้จากตรงนี้ [**Open & Close Principle**](https://saladpuk.gitbook.io/learn/basic/solid/ocp)
{% endhint %}

{% hint style="danger" %}
**Dependency-Inversion Principle (DIP)**\
การละเมิดกฎข้อนี้จะทำให้ module หลักต้องถูกแก้ไขบ่อยๆ เมื่อตัวที่ทำงานตัวเล็กตัวน้อยมีการเปลี่ยนแปลง แม้จะเปลี่ยนเพียงแค่เล็กน้อยก็ตาม [**Dependency-Inversion Principle**](https://www.saladpuk.com/basic/solid/dip)
{% endhint %}

😢 เอาน่าไม่ต้องเสียใจไป เดี๋ยวเราลองวิเคราะห์ปัญหาแล้วลองแก้ไขมันไปทีละปมดูละกันนะ

## 🧒 แก้โจทย์ครั้งที่ 2

### 🔥 วิเคราะห์ปัญหา

โจทย์ในรอบนี้ความยากของมันคือ **จะควบคุมสิ่งที่ควบคุมไม่ได้ยังไง** ซึ่งเมื่อคุมได้ **เงื่อนไขต่างๆก็ต้องดูแลแยกจากตัวที่ถูกควบคุมอันอื่นด้วย**นั่นเอง ซึ่งเรื่องการควบคุมนี้ **ดช.แมวน้ำ** ได้เขียนอธิบายไว้ใน [**`Adapter Pattern`**](https://www.saladpuk.com/beginner-1/design-patterns/structural/adapter-pattern) แล้ว หากสนใจอยากดู Case Study ก็[**กดตรงนี้**](https://www.saladpuk.com/beginner-1/design-patterns/structural/adapter-pattern#undefined-2)เพื่อนไปดูต่อได้่เลยครับ

### 🔥 แก้ไขปัญหา

**เมื่อเรามีของที่เราควบคุมไม่ได้ เราต้องเปลี่ยนเป็นสิ่งที่เราควบคุมได้ก่อน** เพราะไม่อย่างนั้นสนุกแน่ ซึ่งการที่เราจะไปควบคุมของอื่นๆมีหลายวิธีเลย ซึ่งวิธีง่ายสุดคือการทำ **Wrapper Class** โดยเราจะสร้างคลาสขึ้นมาครอบของที่ควบคุมไม่ได้เอาไว้ แล้วเราก็ทำงานกับคลาสตัวนั้นแทนที่จะทำงานกับของที่ควบคุมไม่ได้นั่นเอง ตามรูปด้านล่าง

> Design Pattern ที่มีลักษณะเป็น Wrapper Class มีหลายตัวเลย เช่น [**Adapter**](https://www.saladpuk.com/beginner-1/design-patterns/structural/adapter-pattern), Decorator ลองไปศึกษาต่อได้

![](/files/-MLb-ntAEWEve5X58nAq)

โดยปรกติ **Wrapper Class จะมีหน้าที่เพียงแค่เรื่องเดียวคือควบคุมสิ่งที่มันดูแลอยู่** ดังนั้นเราก็จะมี Wrapper Class เอาไว้ดูแล API พรหัก โดยเฉพาะเลย ตามรูปด้านล่าง

![](/files/-MLb7dvimfigZDCohQuJ)

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

![](/files/-MLbCrR8bUuAxjNz0XuL)

คราวนี้เวลาที่ **(1)** client ขอเข้าเว็บเมื่อไหร่ **(2.1)** เราก็จะเรียกใช้ตัว IWebApi มาทำงาน **(2.2)** ซึ่งตัว object จริงๆของมันคือ Wrapper **(3)** แต่ว่าเจ้า Wrapper มันไม่ได้ทำงานเอง มันจะคอยส่งไปให้ พรหัก API ต่างหาก (4) แล้วเมื่อได้ผลลัพท์กลับมา เจ้า Wrapper ก็จะคอยตรวจสอบ/แก้ไขผลลัพท์ให้เรียบร้อยก่อน (5) ค่อยส่งกลับไปให้ผู้ใช้ตามรูปนั่นเอง

![](/files/-MLbJTUlVuP-ujbAuQXK)

🤠 หากเราอยากเพิ่ม/ลดเงื่อนไขต่างๆก็จะมีผลกระทบแค่กับคลาสเดียวนั่นคือ Wrapper นั่นเอง ซึ่งก็จะตรงกับกฎของ [**Single-Responsibility Principle**](https://saladpuk.gitbook.io/learn/basic/solid/srp) เรียบร้อย

![](/files/-MLbLqlEcxPAJ0v18_pj)

🤠 จากที่ว่ามาก็จะทำให้เราสามารถ เพิ่มตัวที่อยากควบคุมอื่นๆเพิ่มเข้ามาได้เรื่อยๆ โดยไม่มีผลกระทบกับโค้ดเดิม ซึ่งตรงกับกฎของ [**Open & Close Principle**](https://saladpuk.gitbook.io/learn/basic/solid/ocp) เรียบร้อย และการแก้ไขเล็กๆน้อยๆก็จะไม่มีผลกระทบกับ Module หลัก ซึ่งตรงกับกฎ [**Dependency-Inversion Principle**](https://www.saladpuk.com/basic/solid/dip) เช่นกัลล์

![](/files/-MLbL9QuSQNB43lp8V73)

🤠 แถมการทำแบบนี้ก็จะทำให้ Wrapper ของเราเป็นหนึ่งเดียวกันกับสิ่งที่มันต้องการควบคุม ซึ่งก็จะตรงกับกฎ [**Liskov Substitution Principle**](https://www.saladpuk.com/basic/solid/lsp) อีกด้วย

{% hint style="danger" %}
**Liskov Substitution Principle (LSP)**\
การออกแบบที่ละเมิดหลักในการออกแบบนี้จะทำให้เราระแวงในการใช้ subclass เสมอ เพราะไม่แน่ใจว่า subclass ที่เอามาใช้ จะสามารถทำงานได้ 100% แบบคลาสแม่นั่นเอง สำหรับใครที่ลืมหลักในการออกแบบเรื่องนี้ไปแล้วให้กดอ่านได้จากตรงนี้ [**Liskov Substitution Principle (LSP)**](https://www.saladpuk.com/basic/solid/lsp)
{% endhint %}

😳 อุ๊ตะ!! พึ่งเห็นว่าลืมเปลี่ยนชื่อ Wrapper Class ซะได้ ดังนั้นเราก็เปลี่ยนมันเป็น Proxy เก๋ๆล้อกับสิ่งที่มันควบคุมอยู่ตามรูปด้านล่างนั่นเอง

![](/files/-MLbO4lO0PB0_Lzet0XI)

ยินดีด้วยในตอนนี้คุณได้ใช้สิ่งที่เรียกว่า **Proxy Pattern** เรียบร้อยแล้ว ไม่ว่าจะรู้ตัวหรือไม่ก็ตาม เย่ๆ 👏

## 🤔 Proxy Pattern คือไย ?

มันคือ **แนวคิดในการควบคุม object ให้ทำงานดั่งใจ** นั่นเอง โดยมีแนวคิดง่ายๆว่า ถ้าคุมมันไม่ได้ งั้นก็ส่งคนของเราไปคุมมันอีกทีละกัน แล้วเราจะสั่งให้คนของเราทำอะไรก็ได้ละทีนี้

![](/files/-MLbRJsQgjLpgATRFsDv)

## 🤔 ประโยชน์จริงๆคือไย ?

ก็อย่างที่บอกว่าใช้ควบคุมของต่างๆที่เราคุมไม่ได้ แต่นอกเหนือจากนั้นจริงๆประโยชน์มันมีหลายอย่างเลย โดยเขาแบ่งมันออกเป็นกลุมต่างๆตามนี้

### 🔥 Virtual Proxy

หาก**ของที่เราจะเรียกใช้งานมันเปลืองทรัพยากร** เช่น ไฟล์รูปมันใหญ่มากเสียเวลาโหลด ดังนั้นเราก็สามารถใช้ Proxy ให้มันจำรูปที่เคยโหลดมาเก็บไว้เป็น memory cache แล้วถ้าเรียกใช้เมื่อไหร่ก็จะได้ไม่ต้องโหลดใหม่ก็ได้ (ของที่เป็นตระกูล caching data เราควรทำ expiration ให้มันด้วยเสมอ) หรือ การสร้าง object บางตัวค่อนค่างกินเวลา  แบบ Database Connection เราก็สามารถทำ cache เป็น connection pool ไว้ก่อนก็ได้

![](/files/-MLbVAe2vfFKBNe03qP-)

### 🔥 Remote Proxy

หาก**ของที่เราจะเรียกใช้สามารถทำงานจากภายใน Local ได้** เช่น ของบางอย่างสามารถทำงานแบบง่ายๆได้จากตัว client เลยก็สามารถเขียนเป็น Proxy ให้มันทำงานในนั้นให้จบซะ ส่วนไหนทำไม่ได้ค่อยส่งมาที่ Server ก็ได้ เพราะของบางอย่าง client บางประเภทไม่สามารถเอา logic ไปเขียนไว้ในนั้นได้ เช่น ของที่มี sensitive logic ที่ไม่อยากให้คนอื่นเห็น

![](/files/-MLbWpOhwT2pPc1BMOC_)

### 🔥 Protective Proxy

หากของที่เราจะเรียกใช้จะต้องตรวจสอบสิทธิในการเข้าถึงก่อน เช่น เราตั้งเงื่อนไขว่าคนใช้งานต้องอายุเกิน 18 ปีขึ้นไป แต่ตัว API อีกฝั่งไม่ได้ตรวจเรื่องนี้ให้ เราก็สามารถตรวจเงื่อนไขก่อนที่จะเรียกใช้งานได้นั่นเอง

![](/files/-MLbYJbI_xTryJse15GK)

### 🔥 Smart Proxy

เจ้าตัวนี้จะคล้ายๆกับ **Remote Proxy** จนบางทีเขาก็มองว่ามันคืออันเดียวกัน ซึ่งหน้าที่ของมันคือดูว่าของที่จะตอบกลับไปจริงๆแล้วควรเป็นอะไรนั่นเอง

![](/files/-MLbZS-rRWZeUNYuw8GE)

## 😎 Proxy Design

การออกแบบ Proxy นั้นสามารถทำได้หลายวิธีเลย และไม่จำเป็นต้องทำตามที่ **ดช.แมวน้ำ** บอกมานะ เพียงแค่คนส่วนใหญ่นิยมใช้แบบไหนป๋มก็เลยเอามาเขียนเจ๋ยๆ ซึ่งปรกติเราจะเห็นการออกแบบ Proxy 2 วิธีตามด้านล่าง

{% hint style="danger" %}
**ข้อควรระวัง**\
แบบที่ 1 และ แบบที่ 2 มันมีทั้งข้อดีข้อเสียคนละแบบกัน ดังนั้นก่อนเอาไปใช้ก็ชั่งน้ำหนักให้ดีก่อนนะกั๊ฟ แต่โดยปรกติตัวเลือกที่ 1 จะเหมาะสมและใช้งานได้ง่ายที่สุดนั่นเอง
{% endhint %}

#### แบบที่ 1 - สร้างมาตรฐานขึ้นมา แล้วใช้ร่วมกัน

![](/files/-MLbCrR8bUuAxjNz0XuL)

👍 **ข้อดี** - ของทุกอย่างถูกแยกขาดออกจากกัน ทำให้เราแก้ไขได้โดยไม่มีผลกระทบกับอีกตัว

👎 **ข้อเสีย** - เราก็ไม่สามารถเข้าถึงอีกฝั่งได้เยอะมากเท่าไหร่

#### แบบที่ 2 - Inherited จากตัวที่อยากควบคุมตรงๆ

![](/files/-MLbAPuvse4hJGUQSd_3)

👍 **ข้อดี** - เราสามารถใช้ความสามารถส่วนใหญ่ได้จากตัว base class เลย และ subclass ก็ได้อานิสงตามไปด้วย

👎 **ข้อเสีย** - ถ้าแก้ไขโค้ดที่ base class ก็จะส่งผลกระทบถึง sub class ด้วย

## 🎯 บทสรุป

### 👍 ข้อดี

การนำ **Proxy Pattern** มาใช้งานนั้นจะช่วย ลดการผูกกันของโค้ดลง แถมยังสามารถควบคุมการทำงานอีกฝั่งได้ดั่งใจแม้ว่าเราจะไม่ได้เป็นคนเขียนอีกฝั่งก็ตาม ซึ่งส่วนใหญ่เราจะใช้ควบคุม 3rd party library และสุดท้ายโค้ดของเราถูกแยกหน้าที่ออกให้ดูแลเป็นของใครของมัน (**Separation of Concerns**)

### 👎 ข้อเสีย

**เพิ่มความซับซ้อนโดยไม่จำเป็น** เพราะการนำ Proxy ไปใช้ จะทำให้เราไม่สามารถทำงานกับ Source ได้ตรงๆ

{% hint style="danger" %}
**ข้อควรระวัง**\
**อย่านำ Proxy Pattern ไปใช้มั่วซั่ว** เพราะมันทำให้โค้ดของเราซับซ้อนขึ้นเยอะเลยแทนที่เราจะเรียกใช้จาก Source ได้ตรงๆ เราจะต้องทำผ่าน Adapter อีกทีหนึ่ง ดังนั้นให้ชั่งน้ำหนักให้ดีเสียก่อนว่าปัญหาที่เราเจออยู่นั้น มันวุ่นวาย เทสยาก โค้ดมันผูกกันอยู่เยอะหรือเปล่า ถ้าชั่งน้ำหนักแล้ว + มีเหตุผลที่เพียงพอที่จะใช้ก็จงใช้ให้สบายใจไปเถิด
{% endhint %}

{% hint style="success" %}
เกลียด ชอบ ถูกใจ อยากติดตาม อยากติชมแนะนำด่าทอ หรืออะไรก็แล้วแต่ (ห้ามมายืมเงิน) จิ้มลงมาที่เพจนี้ได้เลย [**Mr.Saladpuk**](https://www.facebook.com/mr.saladpuk) และจะเป็นประคุณอันล้นพ้นถ้ากด Like + Follow + Share ให้ด้วยขอรับ น้ำตาจิไหล 🥺
{% endhint %}

![ช่องทางสนับสนุนค่าอาหารแมวน้ำกั๊ฟ 😘](/files/-MKNmp9HEqzREDsXrZ0H)
