Coding Gun

MVC คืออะไร?

MVC ย่อมาจาก Model-View-Controller คือ Enterprise Architecture Pattern(Design Pattern) ซึ่งจะแยกการทำงานของ Code ออกเป็น 3 ส่วน Model, View และ Controller ซึ่ง MVC ถือว่าเป็นพื้นฐานที่สำคัญของการเขียนโปรแกรมมากๆ ถึงแม้ว่าวันนี้จะมี Pattern อย่าง Model-View-ViewModel(MVVM) ขึ้นมาแทนที่ก็ตาม

Design Patterns คือรูปแบบการออกแบบหรือการเขียน Code ที่เป็น Best Practices เราสามารถหยิบเอา Design Patterns เข้ามาใช้ได้เลยถ้าเรารู้ว่า Design Patterns นั้นมีไว้ทำไมและแก้ปัญหาอะไร

Separation of Concerns

หลักการของทั้่ง OOP และ MVC คือ Separation of Concerns หมายถึงการแยกสิ่งที่ไม่เกี่ยวข้องกันออกจากกัน ยิ่งเราแยกได้ละเอียดเท่าไหร่เวลาแก้มันก็จะยิ่งง่าย แต่ตอนแยก Code ออกจากกันต้องระวังไม่ให้เกิด Coupling เพิ่มขึ้นมากเกินไป เช่น ถ้าแยก Class ออกมาแล้วต้องเรียกหากันตลอดเวลา แบบนี้เลือกที่จะอยู่รวมกันน่าจะดีกว่า

ทำไมต้องเป็น MVC?

การแบ่งแยก software ออกเป็น object ตามหลักการของ Object Oriented Programming นั้นใหญ่เกินไป เพราะ 1 Object ต้องทำหน้าที่ทุกอย่างตั้งแต่จัดเตรียม data(หน้าที่ของ Model) กำหนด Workflow การทำงาน(Controller) และจัดรูปแบบการแสดงผล(View)

ตาม SOLID Principles ซึ่งเป็น Best Practices ในการเขียน OOP ได้ระบุไว้ว่า Object ที่ดีต้องมี Single Responsibility(S ตัวแรก) หมายความว่า แต่ละ Object ต้องทำงานแค่เพียงอย่างเดียวเท่านั้น

เราเลยเอา Object แต่ละตัวมาแตกออกเป็น MVC เพื่อให้ทำงานเฉพาะเรื่อง(Object) และเฉพาะงาน(MVC) มากขึ้น

Model-View-Controller(MVC)
Model-View-Controller(MVC)

MVC แต่ละส่วนแบ่งหน้าที่กันยังไง?

MVC จะแยก Code ออกเป็น 3 ส่วนคือ Model, View และ Controller ซึ่งแต่ละส่วนจะรับหน้าที่ดังนี้

Model(M)

Model ทำหน้าที่จัดเตรียม Information ให้กับระบบ ดังนั้น Model จึงมีกหน้าที่อยู่ 2 อย่างหลักๆคือ

  1. ติดต่อกับ Database ก่อนที่เราจะสร้าง Information เราต้องมี Data ก่อน
  2. นำ Data ที่ได้มาจาก Database มาใส่ Business Logic เพื่อให้กลายเป็น Information

ส่วนใหญ่ใน Layer ของ Model เราจะใช้ Object Relational Mapping(ORM) มาเป็นตัวกลางในการคุยกับ Database ซึ่งประโยชน์ที่จะได้จากการมี ORM คือ

View(V)

View ทำหน้าที่นำข้อมูลไปแสดงผลที่หน้าจอหรือติดต่อกับผู้ใช้่งาน เช่น print ข้อมูลออกหน้าจอหรือการ Binding เข้าไปใน Template Engine

ในปัจจุบันเรานิยมสร้าง Backend เป็น Web API ดังนั้นในส่วนของ View จะเปลี่ยนไปเป็น Frontend framework เช่น Angular, React และ Vue แทน

Controller(C)

ทำหน้าที่กำหนด Workflow ของระบบ โดยจะเริ่มตั้งแต่การรับ Request เข้่ามาผ่านทางการ Routing หลังจากนั้นเราจะดึงข้อมูลหรือ Save ข้อมูลผ่านทาง Model และเมื่อการทำงานเสร็จสมบูรณ์เรากจะแสดงผลลัพธ์ออกไปถึงผู้ใช้งานผ่านทาง View

ดังนั้นเราจะเห็นว่า Controller จะทำหน้าที่เชื่อมโยง Model และ View เข้าหากัน

จุดอ่อนของ MVC คือถ้าจัดการ Code ได้ไม่ดีขนาดของ Controller จะใหญ่เกินไป

ประโยชน์ที่ได้จาก MVC

การใช้ MVC Design Patterns จะช่วยให้ Code สามารถรองรับการเปลี่ยนได้มากขึ้น เช่น เมื่อมี Business Logic ที่เปลีั่ยนไปเราจะแก้แค่ใน Model เท่านั้น เมื่อต้องการเปลี่ยนแปลงรูปแบบการแสดงผลเราก็เข้าไปแก้ที่ View เท่านั้น หรือถ้าต้องการเปลี่ยน Workflow เราก็จะเข้าไปแก้ที่ Controller

แต่บางคนก็จะพบปัญหาเวลาเขียน MVC คือพอเพิ่ม Field ใน Database เข้าไปเราต้องตามไปแก้ Model, ปรับเปลี่ยน Controller แล้วก็เข้าไปเพิ่มการแสดงผลในหน้าจอที่ View ทำให้ Developer บางคนรู้สึกยุ่งยาก แต่นี่คืิอสิ่งที่เป็นการแลกเปลี่ยนใน MVC เราอาจต้องเขียน Code มากกว่าเดิมเพราะต้องแยกเป็น 3 Layers(บางคนแยก Service Layer ใน Model ออกมาทำให้มี 4 Layers) แต่ตอนแก้ไขเราจะเข้าไปแก้ได้ง่ายเพราะเราจะเข้าไปแก้เฉพาะส่วนที่เกิดปํญหาเท่านั้น

ตัวอย่างของ MVC

ในปัจจุบัน Framework ส่วนใหญ่จะเป็น MVC แทบจะทุกตัว นอกจากฝั่ง Front-end ที่ส่วนใหญ่จะใช้ MVVM แทน ซึ่ง Framework ที่เป็น MVC ได้แก่

การทำงานของ MVC
ขั้นตอนการทำงานของ MVC
ในตัวอย่างนี้ผมจะใช้ ASP.NET MVC ในการอธิบายการทำงานของ MVC โดยจะเริ่มจาก

1. Routing

User เข้าถึงหน้า Web ผ่านทาง URL ซึ่งรูปแบบของ URL จะถูกเก็บอยู่ที่ Routing

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});

โดย Default ASP.net จะใช้ pattern ของ URL เป็นชื่อ Controller ตามด้วยชื่อ Action(Method ที่อยู่ใน Controller จะเรียกว่า Action) หลังจากนั้น id จะเป็น Optional(ใส่หรือไม่ใส่ก็ได้)

2. Controller

หลังจากนั้น Method ที่อยู่ใน Controller จะถูกเรียกใช้งาน เช่นถ้าเราเรียก URL http://localhost:5000/Movies/ Method Index ใน MoviesController จะถูกเรียก

เราไม่ได้ใส่ชื่อ Action เข้าไป Index จึงถูกเรียกใช้งานโดย Default

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class MoviesController : Controller
{
    ...

    public async Task<IActionResult> Index(string id)
    {
        var movies = from m in _context.Movie select m;

        if (!String.IsNullOrEmpty(id))
        {
            movies = movies.Where(s => s.Title.Contains(id));
        }

        return View(await movies.ToListAsync());
    }
}

3. Model

หลังจากนั้น เราจะเข้าถึงข้อมูลด้วยการ query ในบรรทัดที่ 7 ใน MovieController และหน้าตาของ Model ที่ Map กับ Database ผ่านทาง Entity Framework(ทำหน้าที่เป็น ORM) จะเป็นแบบนี้

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
namespace MvcMovie.Models
{
    public class Movie
    {
        public int ID { get; set; }
        public string Title { get; set; }

        [Display(Name = "Release Date")]
        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }
}

เราสามารถใส่คำอธิบายเพิ่มเติมเข้าไปได้ว่าจะไปแสดงผลในหน้าจอด้วยชื่อ field ว่าอะไร(Display) หรือระบุ DataType ของ field นี้ด้วย(Datatype) ตามตัวอย่าง Code ในบรรทัดที่ 8 และ 9

4. View

สุดท้าย Controller จะนำข้อมูลที่ได้จาก Model ส่งออกไปแสดงผลทาง View ด้วย Code บรรทัดที่ 14 ใน MovieController ซึ่ง Code ในส่วนของ View เราจะใช้ Razor Template Engine มาดูแลเรื่องการแสดงผล โดยจะมี Syntax แบบนี้

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@model IEnumerable<MvcMovie.Models.Movie>

<table>
    <thead> ... </thead>
    <tbody>
        @foreach (var item in Model.movies)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Title)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.ReleaseDate)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Genre)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Price)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Rating)
                </td>
            </tr>
        }
    </tbody>
</table>

นอกจาก MVC แล้วมีอะไรอีกบ้าง

ณ ปัจจุบันนอกจากจะใช้ MVC แล้วเรายังมีอีกหลาย Pattaerns ให้เลือก ยกตัวอย่างเช่น

Model-View-Presentation(MVP)

MVP จะแบ่งแยก Code ออกเป็น 3 ส่วนดังนี้

ประเภทของ MVP

เราสามารถจำแนกประเภทของ MVP ได้ 2 แบบดังนี้

  1. ถ้า View ของเราไม่ได้เชื่อมต่อกับ Model โดยตรง คุยผ่านทาง Presenter อย่างเดียวเราจะเรียก MVP แบบนี้ว่า Passive View

    MVP แบบ Passive View
    Model-View-Presenter แบบ Passive View

  2. ถ้าใน View มีการเรียกใช้ Object ที่อยู่ในชั้นของ Model เราจะเรียก MVP แบบนี้ว่า Supervise Controller

    MVP แบบ Supervise Controller
    Model-View-Presenter แบบ Supervise Controller

เป็น Pattern ที่ได้รับความนิยมใน Desktop Application และ Android ในยุคแรกๆ ซึ่งจะทำให้ Application ของเราเขียน Test ได้ง่ายขึ้น

ซึ่งข้อเสียของ MVP คือ ขนาดของ Presenter ซึ่งจะเป็นปัญหาแบบเดียวกับ Controller ใน MVC

Model-View-ViewModel(MVVM)

ในยุคใหม่เราจะนิยมใช้ MVVM กันมากกว่าเพราะเราสามารถเห็นการเปลี่ยนแปลงของ Model ได้เลยทันที(ทุกครั้งที่ Model เปลี่ยน View จะเปลี่ยนตามทันที) โดยไม่ต้องมีการคลิกกหรือ Refresh หน้า Web เลย

ใน MVVM จะแบ่ง Code ออกเป็น 3 ส่วนด้วยกันคือ

  1. Model ทำหน้าที่เป็น Information ของระบบเหมือนเดิม
  2. View ทำหน้าที่แสดงผล และติดต่อกับผู้ใช้งาน
  3. ViewModel ทำหน้าที่ Binding data ใน Model กับ Control หรือ Component ใน View

Model-View-ViewModel(MVVM)
Model-View-ViewModel(MVVM)

เป็น Patterns ที่นิยมนำไปใช้ใน Frontend framework และ Mobile Application อย่าง Android และ iOS

MVC vs MVP vs MVVP

หลังจากเรารู้จักกับทั้ง 3 Design Patterns กันไปแล้ว หลังจากนี้ลองมาดูความเหมือนและความแตกต่างของทั้ง 3 Patterns นี้กัน

สิ่งที่เหมือนกันของ MVC,MVP และ MVVM

ทั้ง MVC, MVP และ MVVM นั้นมีสิ่งที่เหมือนกันคือ

  1. ทำการ Decoupling Code ออก(Seperation of Concerns)
  2. Code ต้องเขียน Test ได้ง่ายขึ้น เพราะมีการแบ่ง Code ออกเป็นส่วนๆ

ส่วนที่แตกต่างกันของ MVC,MVP และ MVVM

ความแตกต่างของ MVC,MVP และ MVVM เป็นไปตามตารางนี้

MVC MVP MVVM
User ต้องติดต่อกับ Controller View View
Code ใน UI มีน้อย มีเยอะ มีน้อย
ติดต่อกับ Model มี แบบ Passive View ไม่มี
แบบ Supervisor Controller มี
ไม่มี
Data Binding ไม่มี หรือ มีน้อย แบบ Passive View ไม่มี
แบบ Supervisor Controller มี
มี(เป็นจุดขายหลัก)

การประยุกต์ใช้ MVC

ปัญหาของ MVC คือ Controller จะมี Code ค่อนข้างเยอะ(ขนาดใหญ่) ดังนั้นตอนนำ MVC ไปใช้งานจริงเราสามารถเพิ่ม Layer เข้าไปเพื่อลดขนาดของ Controller ลงได้ เช่น ในกรณีที่เรามีการทำ Caching เราสามารถเพิ่ม Caching Layer เข้าไปไว้หลัง Controller เพื่อให้เราไม่ต้องเขียน Code ในการอ่านเขียน Cache ซำ้ๆใน Controller

เพิ่ม Caching เข้าไปใน MVC
เพิ่ม Caching Layer เข้าไปใน MVC

ดังนั้นการใช้งาน MVC ในทางปฎิบัติเราไม่จำเป็นต้องยึดติดกับการแบ่งออกเป็น 3 Layers เท่านั้น

อ่านข้อมูลเพิ่มเติมเกี่ยวกับ MVC ต่อได้ที่นี่

Phanupong Permpimol
Follow me