Tải bản đầy đủ (.pdf) (21 trang)

3 chuong 03 tủ tài liệu bách khoa

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (626.5 KB, 21 trang )

CHƯƠNG 3: MÔ HÌNH MVC
Lịch sử của MVC
Khái niệm Model, View, Controller đã được sử dụng từ những năm 70 của thế
kỷ 20 và phát triển từ đồ án Smalltalk của XeroxPARC. Tại đây nó đã được nhìn
nhận như một cách tổ chức các ứng dụng GUI đầu tiên.
Một số nội dung chi tiết ban đầu của mô hình MVC gắn liền với khái niệm trong
Smalltalk như màn hình và các công cụ, nhưng khái niệm broader vẫn được áp
dụng vào ứng dụng. Những khái niệm này hoàn toàn phù hợp với một ứng dụng
web
Tương tác với ứng dụng MVC dựa theo một vòng tuần hoàn của hoạt động
người dùng và cập nhật view khi mà các view sẽ không có trạng thái nhất định
(stateless). Mô hình này phù hợp với các HTTP request và response được dùng
trong ứng dụng web. Xa hơn nữa, MVC yêu cầu tách rời các yếu tố bao gồm
domain model và controller logic tách riêng khỏi giao diện người dùng (UI) trong ứng
dụng web. Nghĩa là HTML sẽ được tách rời khỏi phần còn lại của ứng dụng dẫn đến
việc bảo trì và sửa lỗi sẽ trở nên dễ dàng và đơn giản hơn. Ruby on Rails dẫn đầu
trong việc xây dựng mới lại mô hình MVC và hiện các ứng dụng vẫn xây dựng dựa
trên mô hình này. Một số MVC framework khác đã được giới thiệu và trình diễn
những ưu điểm của mô hình MVC, trong số đó có ASP.NET


Tìm hiểu mô hình MVC
Ở khái niệm mức cao, mô hình MVC có nghĩa là ứng dụng MVC sẽ được chia
làm ít nhất 3 phần tách biệt, Models bao gồm những thể hiện của dữ liệu mà người
dùng sẽ làm việc trên đó, đó có thể là 1 view model đơn giản dùng để thể hiện
những luồng dữ liệu giữa View và controller hoặc đó có thể là domain models chứa
những dữ liệu business cũng như các hàm tính, chuyển đổi và những quy ước khi
thao tác với dữ liệu đó.
Views dùng để biểu diễn 1 phần dữ liệu của model trên giao diện người dùng.
Controllers xử lý những yêu cầu đến, thực hiện những tác vụ từ phía model và lựa
chọn view để biểu diễn dữ liệu đến người dùng. Model được định nghĩa là không


gian không gian mà ứng dụng của bạn hoạt động dựa theo đó
Ví dụ, trong một ứng dụng ngân hàng (Banking), model đại diện cho tất cả
những gì mà ứng dụng banking hỗ trợ như account, sổ cái, giới hạn tiền gửi của
khách hàng … cũng như các hoạt động có thể dùng để thao tác trên dữ liệu như
chuyển khoản hoặc rút tiền từ tài khoản. Model cũng chịu tách nhiệm đảm bảo tính
toàn vẹn và nhất quán của dữ liệu.
Ví dụ: đảm bảo 66 giao dịch đều được ghi vào trong sổ cái cũng như người
dùng không thể rút hơn số tiền mà người đó có trong ngân hàng.
Các models cũng được định nghĩa dựa trên những gì nó đại diện. Model không
đảm nhận công việc biểu diễn dữ liệu trên giao diện người dùng, đó là công việc của
Views và Controllers.
View chứa các yêu cầu logic để biểu diễn các thuộc tính của View cho người
dùng, đó là công việc duy nhất mà View đảm nhận, nó không quan tâm trực tiếp đến
model cũng như không trực tiếp giao tiếp với tầng Model.
Controller là cầu nối giữa Views và Models. Yêu cầu được gửi từ phía client và
được giải quyết bởi controler bao gồm lựa chọn view thích hợp để biểu diễn nội
dung đến người dùng, nếu cần, sẽ thực hiện các tác vụ ở tầng model.
Mỗi thành phần của MVC đều được định nghĩa chi tiết và độc lập dựa vào
những yêu cầu khác nhau. Những tác vụ logic điều khiển dữ liệu chỉ nằm duy nhất
trong Model. Những tác vụ logic dùng để hiển thị dữ liệu chỉ nằm ở tầng View và
phần code xử lý yêu cầu người dùng bao gồm cả các thông tin đầu vào chỉ có trong


controller. Với sự phân chia rõ ràng từng phần như vậy, công tác bảo trì và mở rộng
ứng dụng sẽ trở nên dễ dàng xuyên suốt vòng đời của nó, bất chấp độ lớn ứng
dụng mà ứng dụng đó được nới rộng.
Tìm hiểu Domain Model
Phần quan trọng nhất trong một ứng dụng MVC là Domain model. Chúng ta
tạo một model bằng cách xác định những thực thể, tác vụ và các quy tắc tồn tại
trong kinh doanh hoặc trong các hoạt động mà ứng dụng đó hỗ trợ. Sau đó chúng ta

tạo nên phần mềm để diễn tả các domain đó, gọi là domain model. Nhằm đáp ứng
mục tiêu của ASP.NET MVC framework, domain model là 1 bộ các C# types
(classes, struct..) được biết đến như là các domain types. Những hoạt động của
domain được biểu diễn bởi các methods được xác định trong domain types. Và
những quy tắc của domain sẽ được biểu diễn bởi những hàm logic bên trong các
method đó.
(Có thể tìm xem lại ở chương trước, bằng cách áp dụng các thuộc tính C#, khi
một thể hiện (instance) của domain type được tạo ra để thể hiện một phần của dữ
liệu, nó gọi là một domain object
Domain model thường được bảo toàn với vòng đời dài, có rất nhiều giải pháp
cho những tính chât này của domain model nhưng cơ sở dữ liệu quan hệ vẫn là lựa
chọn phổ biến nhất )
Một cách ngắn gọn, domain model là định nghĩa duy nhất, cao cấp nhất về
business data và các quy trình bên trong ứng dụng. Một domain model cố định cũng
nắm giữ định nghĩa cho các trạng thái mà domain đó thể hiện
Chúng ta có thế tiếp cận và xử lý các vấn đề nảy sinh của Domain model trong
quá trình bảo trì ứng dụng. Nếu cần thiết phải điều chỉnh các dữ liệu của Model hoặc
thêm váo các hoạt động hay quy tắc mới, domain model là phần duy nhất trong ứng
dụng cần phải được chỉnh sửa.
Mẹo: Một cách phổ biến để thực hiện tách rời domain model ra khỏi những
phần còn lại của ứng dụng ASP.NET MVC là đặt model đó trong một assembly C#
tách biệt. Bằng cách này, bạn có thể tạo ra các references cho domain model từ
những phần khác của của ứng dụng nhưng đảm bảo không còn references nào ở
miền khác. Cách xây dựng này hiệu quả đối với những project lớn


Cách ASP.NET triển khai mô hình MVC
Trong MVC, controller là một lớp C# , thường kế thừa từ lớp
System.Web.Mvc.Controller. Mỗi public method trong lớp kế thừa từ Controller là một action


method, nghĩa là liên quan đến cấu hình URL thông qua hệ thống định hướng của ASP.NET
(routing system). Khi một yêu cầu đươc gửi đến URL tương ứng với action method nào đó,
câu lệnh trong lớp Controller sẽ đươc thực thi để thực hiện các tác vụ trên domain model
sau đó lựa chọn một view để biểu diễn nội dung dữ liệu về phía Client

ASP.NET MVC Framework sử dụng view engine, nghĩa là một cấu trúc đảm nhận việc
phát sinh view nhằm tạo ra phản hồi trên Browser. Những version trước đây của MVC sử
dụng chuẩn ASP.NET view engine, nghĩa là xây dựng các trang ASPX sử dụng streamlined
version theo các cú pháp Web Form. MVC 3 giới thiệu engine Razor, sử dụng cấu trúc cú
pháp hoàn toàn khác (Sẽ được miêu tả cụ thể hơn ở chương 5). Razor đã được cải tiến ở
MVC 4 và không thay đổi ở MVC 5
Mẹo: Visual studio cung cấp công cụ IntelliSense hỗ trợ cho Razor, giúp đơn giản hóa
trong việc truyền và nhận dữ liệu đến view bởi controller
ASP.NET không áp dụng bất kỳ một ràng buộc nào trong việc thực thi domain
model.Người dùng có thể tạo một đối tượng C# bình thường và sử dụng bất kỳ một
Database nào. Bản đồ quan hệ đối tượng (ORM Framework) hoặc bất kỳ công cụ data nào
hỗ trợ bởi .NET

So sánh MVC với những mô hình khac
MVC không phải là kiến trúc xây dựng phần mềm duy nhất. Có rất nhiều kiến trúc
khác và một vài trong số đó cực kỳ phổ biến. Chúng ta đã học nhiều về MVC khi tìm hiểu
qua những định nghĩa.Ở phần tiếp theo , chúng ta sẽ bàn đến những hướng tiếp cận khác
nhau khi xây dựng một ứng dụng và những mặt đối nghịch của chúng so với MVC. Một số
mô hình có biến thể rất gần với MVC, một số khác thì hoàn toàn khác biệt
MVC không hoàn toàn là mô hình hoàn hảo cho tất cả các giải pháp phần mềm. Trong
một số trường hợp, những mô hình xây dựng phần mềm khác sẽ hiệu quả hơn MVC.
Chúng ta cần tìm hiểu và cân nhắc kỹ lưỡng trước khi lựa chọn một mô hình. Nội dung sách


sẽ chỉ xoay quanh mô hình MVC nhưng người phát triển phần mềm cần có tư duy mở và

cân nhắc để đưa ra quyết định sáng suốt nhất

Hiểu về mô hình Smart UI
Một trong những mô hình thiết kế phổ biến được biết đến với tên gọi smart user
interface (Smart UI). Hầu hết người lập trình đều đã từng tạo ít nhất 1 ứng dụng smart UI.
Nếu như bạn đã từng sử dụng Web Forms hoặc ASP.NET Web Form thì bạn cũng đã từng
sử dụng mô hình này
Để xây dựng một ứng dụng Smart UI , người lập trình cấu trúc một giao diện người
dùng, thường là công việc kéo thả các thành phần hoặc controls lên giao diện rỗng ban đầu.
controls hồi đáp những tương tác với người dùng bằng xử lý những sự kiện (events) (bấm
nút, kéo thả chuột, tổ hợp phím…).Người lập trình thêm các đoạn code để phản hồi lại
những sự kiện trong một bộ xử lý sự kiện (event handlers): đoạn code sẽ được gọi khi một
sự kiện xác định xảy ra trên components.
Mô hình này tạo ra một ứng dụng nguyên khối như trong hình. Những đoạn code xử lý
trên giao diện người dùng và các tác vụ business nằm tất cả trong một mà không phân
thành những phần nhỏ. Phần code xác định những giá trị chấp nhận cho việc nhập liệu, truy
vấn dữ liệu hoặc sửa đổi thông tin tài khoản người dùng chỉ nằm trong 1 phần nhỏ, liên kết
với nhau theo trật tự sắp xếp theo những yêu cầu của sự kiện

SmartUI là ý tưởng nhằm tạo ra 1 project đơn giản vì nó có thể đạt hiệu quả trong thời
gian ngắn (so sánh với mô hình MVC (chương 7): MVC vốn đòi hỏi sự chuẩn bị kỹ lưỡng và
sự đầu tư từ ban đầu để đạt được hiệu quả) .Smart UI cũng phù hợp với giao thức giao diện
người dùng. Công cụ thiết kế giao diện tốt dù WebForm có thể hoạt động một cách kỳ quặc
và khó đoán. Nếu bạn đang tiếp xúc với khách hàng và muốn thể hiện nhanh ý tưởng về
những yêu cầu về giao diện cũng như luồng hoạt động của giao diện, Smart UI là một công
cụ nhanh và đáp ứng việc tạo dựng và thử nghiệm những ý tưởng khác nhau.


Một khuyết điểm của Smart UI là những khó khăn trong công tác bảo trì và mở rộng.
Sự trộn lẫn giữa Model và Business logic trong giao diện người dùng dẫn đến sự trùng lặp

(duplication), khi mà cùng 1 mảng business được sao chép lại nhiều lần để hỗ trợ những
thành phần được thêm vào sau đó. Tìm ra tất cả những vị trí trùng lặp và sửa chữa gặp
nhiều khó khăn. Việc thêm tính năng mới mà không ảnh hưởng đến những tính năng đã có
gần như là không thể. Kiểm thử ứng dụng theo mô hình Smart UI cũng gặp nhiều khó khăn.
Cách duy nhất là giả lập những giao tiếp của người dùng, đi ngược lại những ý tưởng về
kiểm thử và khó khăn để thực hiện công tác kiểm thử một cách trọn vẹn
Trong thế giới MVC, Smart UI thường được xem như là một phản mô hình, cần phải
tránh bằng mọi giá. Những người tìm đến mô hình MVC vốn đã dành nhiều thời gian của
họ để bảo trì và phát triển ứng dụng Smart UI trước đó.
Mặc dù tồn tại nhiều luồng quan điểm, tuy nhiên chúng ta không nên đơn giản hóa vấn
đề cũng như không nên loại bỏ phương án SmartUI hoàn toàn . Không phải tất cả mọi thứ
đều xấu trong mô hình Smart UI, nó vẫn tồn tại rất nhiều mặt tích cực khi tiếp cận. Ứng
dụng Smart UI nhanh và dễ dàng phát triển. Những người tạo ra công cụ thiết kế
component và giao diện đang nỗ lực để đem đến trải nghiệm tốt hơn. Ngay cả những người
lập tình thiếu kinh nghiệm nhất vẫn có thể tạo ra một ứng dụng với giao diện chuyên nghiệp
và đấy đủ tính năng trong thời gian ngắn
Điểm yếu lớn nhất của các ứng dụng Smart UI đó là khả năng bảo trì, không nên tốn
công sức cho công tác bảo trì. Nếu bạn xây dựng một ứng dụng nhỏ và đơn giản, Smart UI
là giải pháp hoàn hảo khi mà những yếu tố phức tạp hơn trong mô hình MVC là không cần
thiết

Tìm hiểu kiến trúc Model-View
Business Logic là nguyên nhân dẫn đến những khó khăn về bảo trì trong SmartUI, gây
ra dài dòng trong ứng dụng gây cản trở khi chỉnh sửa hoặc thêm tính năng mới. Mô hình
Model-View đưa ra những khắc phục đối với vấn đề này bằng cách đẩy phần business logic
ra thành một phần domain model tách biệt. Bằng cách này, dữ liệu, process, và các quy tắc
(Rules) được kết nối với nhau trong ứng dụng như trong hình 3-3


Mô hình model view có thể cải thiện mô hình nguyên khối của Smart UI lấy ví dụ như

vấn đề bảo trì. Tuy nhiên có 2 vấn đề nảy sinh:
Vấn đề đầu tiên là từ khi UI và domain model đã được tích hợp chặt chẽ với nhau, nó
có thể gây khó khăn để thực hiện công tác unit testing.
Vấn đề thứ hai nảy sinh trong thực tế nhiều hơn là trong định nghĩa của mô hình.
Model thường chứa 1 lượng lớn code truy suất dữ liệu (không nhất thiết nhưng trong thực
tế vẫn thường gặp) điều này nghĩa là data model không những chỉ chứa business data,
operation và các quy tắc

Tìm hiểu kiến trúc 3 lớp cổ điển (3 tier architecture)
Để xác định vấn đề của kiến trúc model-view, mô hình ba lớp chia những đoạn code
cố định của domain model và đặt nó vào trong một thành phần mới gọi là lớp data access
(data access layer). Hình 3-4

Kiến trúc ba lớp được sử dụng phổ biến nhất trong các kiến trúc ứng dụng business.
Nó cũng không hề có ràng buộc hoạt động của UI và phân chia các yếu tố thành từng phần
mà không biến nó trở nên quá phức tạp. Và nếu quan tâm hơn, lớp DAL có thể được tạo ra
để các công tác unit testing được thực hiện một cách tương đối dễ dàng. Bạn có thể nhận
ra sự giống nhau rõ ràng giữa phần mềm theo kiến trúc ba lớp cổ điển về kiến trúc.Sự khác
biệt nằm ở việc khi lớp UI được tách thành click-and-event GUI framework (như trong
Windows Forms hoặc ASP.NET Web Forms) .Nó trở nên bất khả thi để thực hiện công việc
unit test một cách tự động. Và khi UI là một phần của kiến trúc 3 lớp , nó có thể trở nên
phức tạp, rất nhiều phần code không thể kiểm thử một cách tỉ mỉ


Trong trường hợp xấu nhất, mô hình 3 lớp sẽ thiếu đi tính chặt chẽ trong phân lớp UI,
nghĩa là nhìu ứng dụng theo đó sẽ trở nên giống như các ứng dụng theo mô hình Smart
UI(không có sự tách biệt giữa các thành phần). Điều này gây ra hậu quả tệ nhất , phần mềm
không thể kiểm thử, bảo trì và trở nên vô cùng phức tạp.

Tìm hiểu những biến thể của MVC

Chúng ta đã tìm hiểu những mô tả cơ bản về những yếu tố chính của ứng dụng MVC,
đặc biệt khi nó vận hành trên ASP.NET MVC . Một số định nghĩa về mô hình khác đã thêm
bớt, vay mượn những yếu tố của mô hình MVC để trở nên phù hợp hơn với phạm vi và nhu
cầu của các dự an. Trong phần tiếp theo , chúng ta sẽ có cái nhìn sơ lược của 2 mô hình
dựa trên nền tảng MVC. Nắm được nội dung của những biến thể của MVC là không cần
thiết khi làm việc trên ASP.NET MVC.Tuy nhiên nội dung vấn được thêm vào để hoàn thiện
kiến thức về các mô hình phát triển phần mềm

Tìm hiểu mô hình Model View Presenter
Model View Presenter là một biến thể của MVC được thiết kế để phù hợp hơn với nền
tảng GUI ổn định như Windows Form hoặc ASP.NET Web Forms. Đây là nỗ lực nhằm đạt
đến mô hình Smart UI nhưng giảm thiểu những khuyết điểm nó đem lại.
Trong mô hình MVP, lớp presenter đóng vai trò tương tự như Controller trong MVC
nhưng nó đồng thời cũng đảm nhận mối quan hệ trực tiếp đến các stateful view. Trực tiếp
điều khiển giá trị xuất ra trên các phần giao diện dựa trên nhập liệu của người dùng. Có 2
yếu tố được triển khai trong mô hình này.
Passive view, trong đó view không chứa các tác vụ logic _ nó là một nơi chứa các UI
controls được điều khiển bởi presenter
Supervising controller (controller giám sát), view có thể chịu trách nhiệm cho một số
thành phần logic trình bày như ràng buộc dữ liệu (data binding), và được cho phép tham
chiếu từ nguồn dữ liệu (data source) bên trong domain models
Sự khác biệt giữa 2 hướng tiếp cận liên quan đến sự thông minh của lớp view. Ngoài
ra presenter được tách ra từ GUI Framework, điều này khiến thành phần presenter logic
đơn giản và phù hợp hơn cho công việc unit testing


Tìm hiểu mô hình Model -View- View model
Mô hình Model view view model ( MVVM) là một trong những biến thể gần đây nhất
của MVC. Nó được xây dựng bởi Microsoft và được sử dụng trong WPF (Windows
Presentation Foundation). Trong mô hình MVVM, Model và view có cùng một vai trò như

trong MVC. Sự khác biệt trong khái niệm của MVVM nằm trong lớp view model. Đây là một
lớp trừu tượng thể hiện của giao diện người dùng. Thường là lớp C# cho thấy cả các thành
phần của dữ liệu được biểu diễn trên UI và các tác vụ trên dữ liệu có thể được gọi bởi UI.
Không như MVC controller, MVVM view model không hề tồn tại các khái niệm về view (hoặc
bất cứ định nghĩa về UI nào khác). MVVM view sử dụng chực năng WPF binding để liên kết
2 chiều với những thuộc tính điều khiển trong view (item trong các dropdown menu hoặc tác
dụng khi bấm nút) với thuộc tính được thể hiện bởi viewmodel
Mẹo: MVC cũng dùng khái niệm view model nhưng liên quan đến các class model
đơn giản và chỉ được dùng với mục đích duy nhất là truyền dữ liệu từ controller vào view,
đối nghịch với domain model vốn đại diện cho dữ liệu, hoạt động và quy tắc

Loose Coupling (Độ phụ thuộc thấp)
Một trong những tính năng quan trọng của mô hình MVC là nó cho phép chia nhỏ các
vấn đề cần quan tâm. Chúng ta muốn mỗi bộ phận trong ứng dụng của phải độc lập nhất có
thể và có ít tính phụ thuộc lẫn nhau nhất trong khả năng sắp xếp của chúng ta. Ý tưởng cho
giải pháp trên, mỗi thành phần không hề biết gì về các thành phần khác và chỉ hoạt động với
các phần khác thông qua lớp các lớp interface trừu tượng. Đây được xem như độ phụ thuộc
thấp (Loose Coupling)
Nó khiến cho công đoạn kiểm thử và chỉnh sưa trở nên dễ dàng hơn. Một ví dụ đơn
giản sẽ giúp chúng ta dễ hình dung, giả sử khi chúng ta viết một component tên
MyEmailSender làm nhiệu vụ gửi email, chúng ta sẽ thực hiện mợi lớp interface để định

nghĩa tất cả các chức năng chung cần thiết để gửi một email (tạm gọi là IEmailSender). Tất
cả những component khác của ứng dụng khi cần gửi email- giả sử chức năng trợ giúp reset
lại password sẽ gọi hàm PasswordResetHelper có thể gửi 1 email bằng cách liên hệ với hàm
chức năng (method) trong lớp interface. Không hè có sự phụ thuộc trực tiếp nào giữa
PasswordResetHelper và MyEmailSender Hinh 3-5


Bằng giới thiệu về IEmailSender, chúng ta có thể thấy không hề có sự phụ thuộc nào

giữa PasswordResetHelper và MyEmailSender. Chúng ta có thể thay thế MyEmailSender bằng
một nhà cung cấp e-mail khác hoặc xây dựng một hàm chức năng giả nhằm mục đích kiểm
thử mà không cần phải chỉnh sửa lại PasswordResetHelper (Chương 6)

Sử dụng Dependency Injection
Interface cho phép chia nhỏ các components nhưng vẫn phải đối mặt với một vấn đề:
C# không hỗ trợ công cụ built-in có thể dễ dàng tạo một đối tượng thực thi interfaces, ngoại
trừ tạo một instance cụ thể của component bằng từ khóa mới. Gỉa sử đoạn code

Điều này làm cản trở mục tiêu nhằm thay thế MyEmailSender mà không cần phải thay
đổi PasswordResetHelper và có nghĩa là chúng ta chỉ mới hoàn thành một phần của thao tác
kết nối các component riêng biệt. PasswordResetHelper class được cấu hình và gửi mail
thông qua lớp interface IEmailSender, nhưng để tạo một đối tượng thực thi lớp interface đó,
chúng ta phải tạo ra một instance của MyEmailSender. Thực tệ, chúng ta đã khiến vấn đề trở
nên xấu đi khi bởi vì hiện giờ PasswordResetHelper giờ đã bị phụ thuộc vào MyEmailSender
với lớp Interface IEmailSender Hình 3-6

Những gì chúng ta cần là một cách để lấy đối tượng thực thi lớp interface mà không
cần phải tạo đối tượng một cách trực tiếp. Giải pháp cho vấn đề này gọi là Denpendency
Injection. Hoặc có thể biết đến với tên gọi Inversion of control (IoC). DI là mô hình thiết kế có
thể hoàn thiện kết nối các thành phần rời rạc. Như đã miêu tả về DI, chúng ta có thể tự hỏi
tại sao lại cần đến nó nhưng thực sự đây là một khái niệm trung tâm quan trọng ảnh hưởng
đến quá trình phát triển ứng dụng MVC và nó có thể gây ra rất nhiều nhầm lẫn (confusion)


Phá bỏ và khai báo Dependencies
Có 2 phần trong mô hình DI . Thứ nhất là khi bỏ đi bất cứ Dependency nào của các
class cụ thể trong component. Trong trường hợp này là PasswordResetHelper. Chúng ta thực
hiện bằng cách tạo một class cấu trúc cho phép quá trình thực thi interface .


Hàm cấu trúc cho lớp PasswordResetHelper được viết để khai báo phụ thuộc vào lớp
interface IEmailSender, nghĩa là nó không thể được tạo hoặc sử dụng chừng nào nó nhận
được một đối tượng thực thi lớp interface IEmailSender. Bằng cách khai báo lệ thuộc, lớp
PasswordResetHelper không còn chứa bất nội dung nào từ MyEmailSender. Nó chỉ phụ thuộc

duy nhất vào lớp interface IEmailSender. Một cách ngắn gọn, lớp PasswordResetHelper
không cần biết hoặc quan tâm lớp interface IEmailSender vận hành như thế nào

Injecting Dependencies
Phần thứ hai của mô hình DI là chuyển đổi phụ thuộc được khai báo từ lớp
PasswordResetHelper khi tạo ra 1 instance của nó,do đó nó có khái niệm truyền sự lệ thuộc

(Depencency Injection). Tất cả điều này có nghĩa khi chúng ta cần quyết định sử dụng lớp
nào để thực thi interface IEmailHelper tạo một đối tượng từ lớp đó vào truyền đối tượng đó
như một đối số vào trong hàm cấu trúc PasswordResetHelper. Lưu ý hàm
PasswordResetHelper khai báo tính lệ thuộc trong hàm cấu trúc của nó. Công việc này được

gọi là constructor injection. Chúng ta cũng có thể khai báo tính lệ thuộc để chuyển đổi thông
qua một thuộc tính public, được định nghĩa là setter injection. Tính lệ thuộc được chuyển đổi
trong PasswordResetHelper bởi môi trường runtime. Cũng cần phải nói, một instance của
một số class thực thi interface IEmailSender sẽ được tạo và truyền vào hàm cấu trúc của lớp
PasswordResetHelper trong quá trình khởi tạo instance. Không hề có quãng thời gian biên

dịch tính lệ thuộc giữa lớp PasswordResetHelper và bất cứ lớp nào thực thi lớp interface mà
nó phụ thuộc vào.Bởi vì sự lệ thuộc được giải quyết trong quá trình runtime. Chúng ta có thể
quyết định tiến trình nào trong lớp interface sẽ được sử dụng khi chúng ta chạy ứng dụng.
Chúng ta có thể chọn giữa nhừng nhà cung cấp dịch vụ email khác nhau hoặc chuyển đổi
một số tiến trình giả nhằm mục đích kiểm thử. Dependency Injection cho phép chúng ta đạt
được mối quan hệ mà chúng ta nhắm đến như trong hình 3-5.



Sử dụng Dependency Injection Container
Sử dụng vùng chứa (container) cho các dependency injection. Chúng ta đã giải quyết
vấn đề về tính lệ thuộc nhưng làm sao chúng ta có thể nhanh chóng thực hiện tác vụ của
interface mà không cần lệ thuộc ở 1 nơi nào khác trong ứng dụng. Như đã nêu, chúng ta
vẫn còn đoạn câu lệnh như thế này nằm đâu đó trong ứng dụng :

Câu trả lời là để sử dụng vùng chứa dependency injection (còn dc gọi là vùng chứa
IoC). Đây là một components hoạt động như người chỉ đường cho một lớp như
PasswordResetHellper khai báo một lớp để phân giải sự lệ thuộc của nó, trong trường hợp

này là IEmailSender. Chung ta đăng ký một nhóm các kiểu interface hoặc abstract mà ứng
dụng sẽ sử dụng với vùng chứa DI và xác định những lớp tác vụ nào nên được sử dụng để
thỏa mãn sự lệ thuộc. Vì thế chúng ta sẽ đăng ký interface IEmailSender với vùng chứa và
chỉ ra một instance của MyEmailSender nên được khởi tạo bất kỳ khi nào một tiến trình của
IEmailSender được yêu cầu. Khi chúng ta muốn đối tượng PasswrodResetHelper trong ứng

dụng của mình, chúng ta sẽ yêu cầu vùng chứa DI để tạo một đối tượng . Nó sẽ hiểu rằng
lớp PasswordResetHelper đã khai báo lệ thuộc vào lớp interface IEmailSender và nó biết chúng
ta đã khai báo sử dụng lớp MyEmailSender nhưng là lớp thực hiện của interface đó. Vùng
chứ DI dựa vào 2 nguôn thông tin đó, tạo một đối tượng MyEmailSender và sau đó sử dụng
nó như một đối số để tạo ra đối tượng PasswordHelper để có thể dùng được trong ứng dụng

Ghi nhớ: cần lưu ý một điểm quan trọng là chúng ta không thể tự mình tạo đối tượng
trong ứng dụng sử dụng keyword “new”. Thay vào đó, chúng ta sẽ vào cùng chứa DI và yêu
cầu đối tượng cần. Điều này có thể cần thời gian để làm quan khi mới học về khái niện DI
nhưng MVC Framework sẽ cung cấp một số tính năng khiến công việc này trở nên dơn giản
hơn. Chúng ta không tự tạo DI container , có một số mã nguồn mở và miễn phí thực hiện
công việc đó.Một trong số các công cụ đó là Ninject (www.ninject.org) Chúng ta sẽ giới thiệu
về phần cài đặt và sử dụng công cụ này thông qua NuGet ở chương 6

Mẹo: Microsoft đã tạo một vùng chứa DI riêng, gọi là Unity. Để tìm hiểu thêm, có thể
vào unity.codeplex.com. Vai trò của DI container có vẻ đơn giản và không đáng kể, tuy
nhiên công cụ DI container như Unity vẫn có một số tính năng độc đáo như
Dependency chain resolution: Nếu chúng ta yêu cầu một conponent có sự phụ thuộc
trong đó (tham số trong hàm cấu trúc), vùng chứa sẽ cũng sẽ thỏa mãn những phụ thuộc


đó. Vì thế, nếu hàm cấu trúc cho lớp MyEmailSender đòi hỏi một tiến trình bên phía lớp
interface INetworkTransport, DI containner sẽ nhanh chóng thực hiện tiến trình bên phía
interface đó, chuyển nó đến hàm cấu trúc của MyEmailSender và trả về kết quả như một tác
vụ thực hiện bởi IEmailSender
Object lifecycle management: Nếu chúng ta yêu cầu một component nhừng hơn 1 lần,
liệu chúng ta có nên lấy cùng 1 instance đó mỗi lần như vậy hay tạo ra một instance mới?
Một công cụ DI container tốt sẽ cho phép chúng ta cấu hình vòng đời của component, cho
phép chúng ta lựa chọn trong nhiều lựa chọn xác định trước bao gồm singleton (sử dụng
cùng 1 instance cho mỗi lần gọi), transient(tạo mới instance mỗi lần thực hiện), instance-per
thread, instance-per HTTP request, instance-from-a-pool và nhiều lựa chọn khác
Configuration of constructor parameter values: Nếu hàm cấu trúc cho tác vụ bên lớp
interface INetworkTransport yêu cầu một đạn mã string gọi là serverName , lấy ví dụ, chúng
ta nên được phép lấy bộ các giá trị cho nó từ trong phần cấu hình DI container. Đây là một
ước cấu hình hệ thống căn bản và đơn giản sẽ loại bỏ việc truyền đoạn code trên các
connection strings, server addresses …
Tự mình xây dựng một DI container là cách tuyệt vời nhất để nắm được cách C# và
.NET xử lý kiểu và ánh xạ. Tuy nhiên chúng ta không nên đặt phần code của mình vào trong
các ứng dụng thực tế vì việc xây dựng một DI container độ tin cậy cao, mạnh mẽ và hiệu
năng tốt rất khó khăn vì thế chúng ta nên sử dụng những gói sẵn có cho công việc này.
Ngoài Ninject như đã đề cập, chúng ta vẫn có nhiều lựa chọn khác tùy thuộc vào sở thích
phát triển của người dùng.

Làm quen với kiểm thử tự động

ASP.NET MVC Framework được thiết kế giúp công việc cài đặt kiểm thử tự động đơn
giản và sử dụng các công nghệ phát triển như test-driven (TDD), chúng ta sẽ đề cập đến ở
phần sau chương này. ASP.NET cung cấp ý tưởng về nền tảng dành cho việc kiểm thử tự
động và Visual Studio có một số tính năng kiểm thử mạnh mẽ. Giữa hai phần đó, họ thực
hiện kiểm thử thiết kế và chạy thử đơn giản và dễ dàng
Trong định nghĩa rộng hơn, những nhà phát triển ứng dụng web ngày nay tập trung
vào hai loại kiểm thử tự động. Loại đầu tiên là Unit testing, là cách xác định và xác nhận một
hành động của một lớp độc lập (hoặc trong từng đơn vị code) một cách chuyên biệt so với
phần còn lại của ứng dụng. Loại thứ hai là intergration testing (kiểm thử tích hợp) là cách
xác định và xác nhận hành vi của nhiều component làm việc cùng nhau, cho đến mức ứng
bao hàm toàn bộ ứng dụng web. Cả hai loại kiểm thử đều có giá trị trong ứng dụng web.


Unit tests có thể được tạo ra đơn giản và chạy dễ dàng, hoạt động vô cùng chính xác khi
bạn cần kiểm định các thuật toán, business logic hoặc cơ sở hạ tầng phía back-end. Gía trị
của intergration testing là có có thể mô hình hóa cách user tương tác vơi giao lớp UI và có
thể bao hàm toàn bộ những tích hợp công nghệ ứng dụng của bạn sử dụng, bao gồm cả
web server và database. Kiểm thử tích hợp có xu hướng dễ dàng tìm ra những bug mới vốn
chưa xuất hiện ở các tính năng cũ, loại này gọi là regression testing(kiểm thử truy hồi)

Tìm hiểu Unit testing
Trong môi trường .NET, chúng ta tạo ra một test project tách riêng trong Visual Studio
nắm giữ các thành phần phần kiểm thử (test fixtures) . Porject này sẽ được tạo khi chúng ta
chọn add một unit test hoặc có thể tạo một cách tự động khi sử dụng template project của
MVC. Thành phần kiểm thử là một lớp C# định nghĩa một bộ các test methods: một method
cho mỗi tính năng bạn mún xác nhận. Một test project có thể bao gồm nhiều lớp kiểm thử
GETTING UNIT TEST FEVER
Khả năng vận hành unit testing là một lợi thế khi nàm việc với MVC Framework nhưng
nó không dành cho tất cả mọi người. Nếu bạn chưa từng phải làm công việc unit testing
trước đây, lời khuyên cho bạn là nên thử làm một lần và xem cách làm việc của nó như thế

nào. Một số thường thích sử dụng unit testing cho project của họ nhưng không hẳn project
nào cũng nên như thế và không nhất quán như bạn mong đợi. Chúng ta sẽ tập trung vào
việc viết các unit test cho các tính năng và hàm khó và có khả năng là nguồn gốc của các
bug khi chúng ta triển khai. Trong trường hợp này , unit testing sẽ giúp chúng ta kiến trúc lại
suy nghĩ về cách thực hiện những điều mình cần. Chỉ cần nghĩ việc kiểm thử sẽ giúp chúng
ta phát hiện ra những vấn đề tìm ẩn. Trước khi bắt đầu với những bug và lỗi trong thực tế.
Cần chú ý là unit testing là một công cụ, không bắt buộc chúng ta cần phải ứng dụng nó bất
kỳ lúc nào. Nếu bạn nghĩa unit testing không giúp ích gì hoặc có một công nghệ nào đó tốt
hơn phù hợp với nhu cầu bản thân thì bạn không cần phải thực hiện công việc này.(Dù thực
tế không có cách làm nào tốt hơn tự mình kiểm thử, bạn có thể để user là những người tìm
ra bug và bạn trở thành người phát triển xấu trong mắt người dùng. Chúng ta không nhất
thiết phải thực hiện unit test nhưng vẫn nên thực hành nó trên một số ví dụ)
Ghi chú: Chúng ta sẽ học cách tạo một test project và dùng đó làm unit testing trong
chương 6. Mục đích của chương này chỉ nhằm giới thiệu khái niệm của unit testing và cho
người đọc ý tưởng về một thành phần kiểm thử (text fixture) như thế nào và được dùng như
thế nào.
Để bắt đầu, chúng ta tạo một class trong một ứng dụng tưởng tượng như trong danh
sách 3-1. Lớp này gọi là AdminController và nó định nghĩa method ChangeLoginName cho
phép người dùng thay đổi password của họ


Mẹo: Lớp được tạo trong một project Visual Studio có tên là TestingDemo. Người đọc
không nhất thiết phải tạo lại ví dụ này để theo dõi. Ví dụ này có thể tìm thấy trên trang
Apress.com. Controller dựa vào một số lớp model và một interface trong list 3-2.
Đây không phải trong một project thực tế, ví dụ trong sách đã được đôn giản hóa để
miêu tả quá trình kiểm thử dễ dàng hơn. Không khuyến khích người đọc tạo thêm 1 lớp user
chỉ có một thành phần kiểu string


Lớp user đại diện cho user trong ứng dụng. user được khởi tạo, quản lý và lưu trữ

trong kho chứa, các tính năng đó được định nghĩa bởi lớp interface IUserRepository và có
một tính năng hoàn chỉnh nằm tách rời của lớp interface này nằm trong lớp
DefaultUserRepository. Mục địch của chúng ta trong phần này là viết một unit test cho tính

năng cung cấp bởi method ChangeLoginName được định nghĩa trong AdminController.(miêu tả
như trong listing 3-3).


Thành phần kiểm thử trong đoạn code trên là method CanChangeLoginName. Để ý rằng
method này được đặt thuộc tính TestMethod và lớp này thuộc sở hữu của lớp
AdminControllerTest (được đặt cho thuộc tính TestClass). Đây là cách Visual Studio tìm ra

các thành phần kiểm thử. Method CanChangeLoginName dựa trên mô hình
AAA(arrange/act/assert). Arrange (sắp xếp)liên quan đến việc thiết lập điều kiện cho bài
kiểm thử, act là việc thực hiện vài test và assert (xác nhận) xác nhận xác kết quả nhận được
chính là kết quả cần đạt được. Thống nhất về kiến trúc của các method unit test khiến
chúng dễ đọc hơn. Đôi khi chúng ta cảm nhận được project sẽ trở nên tốt hơn khi nó có đến
hàng trăm unit test. Thành phần kiểm thử làm giả các tác vụ trong lớp interface


IUserRepository để giả lập tình huống cho trước. Trong trường hợp này, khi có một thành

viên (Bob) trong kho chứa. Nó khởi tạo một kho chứa giả và User giả đây là phần Arrange
trong quá trình kiểm thử. Tiếp theo đó, method AdminController.ChangeLoginName được gọi
trong quá trình kiểm thử. Đây là phần Act. Cuối cùng khi kiếm trả kết quả sửa dụng những
lời gọi Assert. Assert method được cung cấp bởi Visual Studio và cho phép người dùng
kiểm tra các kết quả đầu ra. Chúng ta chạy bài test trên Visual Studio test meno và nhận
được các kết quả phản hồi khi thực hiện quá trình kiểm thử như trong hình 3-7

Nếu bài kiểm thử chạy mà không xảy ra bất kỳ những exception chưa đươc xử lý và

tất cả các câu lệnh Assert đều hoạt động mà không phát sinh lỗi, cửa sổ Test Explorer sẽ
hiện màu xanh, ngược lại sẽ là màu đỏ kèm theo nội dung lỗi phát sinh.
Lưu ý: chúng ta có thể chú ý cách mô hình DI giúp ích trong quá trình thực hiện unit
testing. Chúng ta có thể tạo một tác vụ giả trong kho chứa và ánh xạ nó qua lớp controller
để tạo ra một trường hợp cụ thể. Thoạt nhìn có vẻ cần rất nhiều nỗ lực khi thực hiện một bài
kiểm thử trên một method đơn giản, tuy nhiên khi kiểm thử những tính năng phức tạp hơn,
nó lại không đòi hỏi thêm quá nhiều các dòng code.Nếu bạn cảm thấy muốn bỏ qua những
bài kiểm thử nhỏ như bài trên, hãy nhớ rằng các test fixtures (thành phần test) cho phép tìm
ra những bug trong những tính năng phức tạp hơn.Chúng ta có thể cải thiện bài kiểm thử
vừa rồi bằng cách loại bỏ lớp giả được tạo ra trong quá trình kiểm thử
(FakeMemberRepository) bằng cách sử dụng công cụ (sẽ đề cập ở chương 6)


Sử dụng TDD và Red-Green-Refactor Workflow
Với TDD (Test-driven developement), chúng ta sử dụng unit test để thiết kế các đoạn
code. Đây là một khái niệm rất lạ nếu bạn đã quen mới việc testing sau khi hoàn tất quá
trình coding, tuy nhiên hướng tiếp cận này lại rất hay. Nội dung chính của khái niệm này là
xây dụng một workflow gọi là red-green-refactor. Nó sẽ hoạt động như sau
 Xác định những gì bạn cần để thêm các tính năng hoặc method mới vào ứng dụng
 Viết bài kiểm thử xác nhận hành vi của tính năng mới khi nó được tạo ra
 Chạy bài kiểm thử và nhận được kết quả đèn đỏ (red light)
 Viết phần code thực hiện tính năng mới
 Chạy lại bài test và chỉnh sửa phần code cho đến khi nhận được đèn xanh
(greenligth)
 Chỉnh sửa lại đoạn code của bạn nếu thấy cần thiết (thiết kế lại các câu lệnh,đổi tên
biến …)
 Chạy lại bài test để xác nhận những thay đổi này không ảnh hưởng đến hoạt động
của tính năng bạn vừa thêm vào
Hướng làm việc này được lặp lại mỗi khi chúng ta thêm mới một tính năng. TDD là
hướng phát trển cổ điển. Chúng ta bắt đầu bằng cách viết bài test cho tính năng mới hoạt

động hoàn chỉnh, chắc chắn rằng bài test sẽ không đạt yêu cầu. Sau đó thực hiện tính năng
đó, xây dựng từng thành phần của tính năng để nó vượt qua một hoặc nhiều bài test.
Vòng tròn này là bản chất cơ bản của TDD. Nó được rất nhiều ý kiến tích cực nên
được áp dụng như phong cách lập trình, nó khiến người phái triển xác định đươc sự thay
đổi hoặc những cải tiến cho ứng dụng trước khi bắt đầu giai đoạn coding. Chúng ta luôn có
một đích đến rõ ràng cũng như việc kiểm tra liệu chúng ta đã đến được đích đến ấy chưa.
Nếu bạn dự đính ứng dụng unit test trong ứng dụng của mình, bạn có thể chắc chắn những
yếu tố thêm vào sẽ không thay đổi hoạt động ở các phần khác
TDD có thể khá lạ khi mới làm quen với nó, tuy nhiên đây là một hướng tiếp cận rất
mạnh mẽ. Khi viết bài test trước đảm bảo rằng chúng ta sẽ hình dung được tính năng sẽ
hoạt động một cách hoàn chỉnh ra sao giúp chúng ta chọn đúng hướng đi trong quá trình
coding
Một điểm trừ đối với TDD là nó đòi hỏi tính kỷ luật. Khi hạn chót đến gần, đôi khi
chúng ta muốn bỏ đi phần TDD này và đi ngay vào phần coding. Trong những project thực
tế, đã có nhiều người lén lút bỏ đi phần kiểm thử nhằm khiến cho phần code có vẻ tốt đẹp
hơn bản chất thực của nó. Vì lí do đó, TDD nên được sử dụng trong môi trường làm việc
nghiêm túc, cần kỷ luật và kỹ năng cao trong đội ngũ phát triển giúp việc thực hiện vẫn được
tiến hành dưới áp lực của thời gian.


Tìm hiểu kiểm thử tích hợp (Intergration Testing)
Trong ứng dụng web, cách tiếp cận phổ biến nhất trong kiểm thử tích hợp là UI
automation. Nghĩa là giả lập hoặc tự động hóa tạo ra các thao tác người dùng (nhất nút,
chọn follow hoặc xác nhận mẫu đơn …) trên một trình duyệt web để thực hành ứng dụng
trên môi trường công nghệ. Hai công cụ mã nguồn mở phục phụ tự động hóa hỗ trợ phát
triển .NET bao gồm:
 Selenium RC ( bao gồm ứng dụng java server có thể tự động
gửi câu lệnh đến IE, Firefox, Safari hoặc Opera, thêm người dùng cho .NET, Python, Ruby
và nhiều framework khác, bạn có thể chọn một ngôn ngữ theo sở thích. Selenimum là một
công cụ rất mạnh và chuyên dụng, điểm trừ duy nhất là nó hoạt động trên Java server

 WatiN (), thư viện .NET có thể gửi các yêu cầu tự động đến IE hoặc
Firefox. API của công cụ này không mạnh bằng Selenium nhưng nó có thể giải quyết hầu
hết mọi ngữ cảnh và dễ dàng cài đặt. Bạn chỉ cần một file DLL
Kiểm thử tích hợp là ý tưởng bổ sung cho unit testing. Mặc dù unit testing phù hợp để
kiểm thử các hành động hoặc một components riêng biệt trên server. Kiểm thử tích hợp cho
phép bạn tạo những bài kiểm thử tập trung vào phía client, xây dựng những hành động như
một user. Kết quả đạt được có thể phát hiện ra vấn đề trong quá trình tương tác giữa các
component, đây cũng là ý nghĩa của khái niệm kiểm thử tích hợp.Vì kiểm thử tích hợp được
thực hiện trên web browser , chúng ta có thể kiểm tra các câu lệnh Javascript có chạy đúng
như yêu cầu, đôi khi việc này gặp nhìu khó khăn đối với unit testing. Có một số khuyết điểm.
kiểm thử tích hợp cần nhiều thời gian hơn, cần nhiều thời gian để xây dựng các bài test và
thời gian để thực hiện chúng. Kiểm thử tích hợp có thể không chắc chăn. Lấy ví dụ Nếu bạn
thay đổi thuộc tính id của một elements được kiểm tra trong bài test, bài test thường sẽ
không đạt yêu cầu .Kết quả đạt được khi bỏ ra nhiều thời gian và công sức hơn, kiểm thử
tích hợp thường được hoàn thành tại một cột mốc quan trọng trong quá trình phát triển có
thể là kiểm tra phần code hoàn thành mỗi tuần hay hoàn thành một tính năng lốn nào đó.
Kiểm thử tích hợp cũng hiệu quả tương đương như Unit testing và nó có thể phát hiện
những lỗi mà unit testing không phát hiện được. kiểm thử tích hợp là quá trình nên được
đầu tư thời gian để cài đặt và thực hiện.Chung ta nên thêm nó vào trong quá trình phát triển
truy nhiên trong sách sẽ không đề cập đến kiểm thử tích hợp. Nó nằm ngoài nội dung MVC
framwork. Mọi web app đều có thể thực hiện kiểm thử tích hợp và thu về kết quả tuy nhiên
không có tính năng nào của MVC Framework hỗ trợ hoạt động này. Kiểm thử tích hợp là
một phần tách biệt, những gì đúng khi thực hiện kiểm thử tích hợp trên ứng dụng web cũng
đúng trên mô hình MVC


Tổng hợp
Trong chương này chúng ta đã học về mô hình kiến trúc MVC và so sánh nó với
những mô hình kiến trúc khác chúng ta có thể đã thấy hoặc nghe qua. Chúng ta đã bàn về ý
nghĩa của domain model và giới thiệu về dependency injection, tính năng cho phép chúng ta

tách các components để phân chia rõ ràng các thành phần trong ứng dụng. Mô tả công việc
kiểm thử unit testing ở mức độ cơ bản, cách phân chia các components và dependency
injection giúp công việc kiểm thử unit testing trở nên dễ dàng. Trong chương tiếp theo
chúng ta sẽ bàn về những tính năng cần thiết của C# sử dụng trong ứng dụng MVC
Framework



×