1. Trang chủ
  2. » Giáo án - Bài giảng

vài năm về trước tôi đã có đủ khả năng tài chính để chuyển hướng nghề nghiệp bản quyền quyển sách foundation of programming được đăng kí bản quyền với tên gọi attribution noncommercial noderivs 3 0 un

68 9 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 68
Dung lượng 159,17 KB

Nội dung

Tôi cho rằng phần lớn sự đối lập giữa hai trường phái trên là do Microsoft thiên về thiết kế hướng dữ liệu mặc dù trên thực tế nó không tương thích tốt với những gì mà những nhà phát tri[r]

(1)

BẢN QUYỀN

Quyển sách Foundation of Programming đăng kí quyền với tên gọi Attribution-NonCommercial-NoDerivs 3.0 Unported

Độc giả phép chép, phân phối sử dụng sách Tuy nhiên, bạn cần ghi tên tác giả Karl Seguin không sử dụng sách với mục đích thương mại không thay đổi nội dung sách

Bản quyền đầy đủ sách thể tại:

http://creativecommons.org/licences/by-nc-nd/3.0/legalcode

SỰ GHI NHẬN

Có nhiều người xứng đáng nhận lời cảm ơn từ tác giả Quyển sách đóng góp nhỏ nhoi cho phát triển kho kiến thức khổng lồ cộng đồng phần mềm Nếu khơng có sách chất lượng diễn đàn, newsgroup, blog, thư viện dự án mã nguồn mở, tơi cịn phải tìm hiểu đoạn mã ASP bị timing-out thực vòng lặp tập hợp ghi/đối tượng - recordset(sự phiền toái MoveNext)

Không ngạc nhiên cộng đồng phần mềm sử dụng tính đại chúng Internet nhiều ngành nghề khác để phát triển Điều đáng ngạc nhiên cách mà điều xảy mà không gây nhiều ý Rất tốt!

Dĩ nhiên, có ngừơi đặc biệt mà khơng có khơng thể có sách Gửi Wendy,

Mọi người bảo anh may mắn người thơng minh xinh đẹp em Tuy nhiên họ hết em Em không xinh đẹp thông minh, em cho phép anh dành nhiều thời gian bên máy tính, cho dù để làm việc, học tập, viết code chơi game Em sẵn lòng đọc lại thảo anh lắng nghe anh chuyện trị điều vơ nghĩa Anh cần phải biết ơn em nhiều

Mục lục

BẢN QUYỀN 1

SỰ GHI NHẬN 1

VỀ TÁC GIẢ 3

CHƯƠNG ALT.NET 4

Mục tiêu 4

Sự đơn giản 5

YAGNI 5

Thời điểm hồi đáp cuối cùng(Last Responsible Moment) 6

Tránh lặp lại (DRY) 6

Rõ ràng gắn kết 6

Cặp đôi 6

(2)

Trong chương 6

Chương THIẾT KẾ HƯỚNG LĨNH VỰC 8

Thiết kế hướng liệu/ lĩnh vực 8

Ngừơi dùng, khách hàng Các bên liên quan 9

Hướng lĩnh vực (The domain object) 9

Giao diện người dùng (UI) 12

Bí thủ thuật 13

Factory Pattern 13

Chỉ định truy cập – Access Modifier 13

Interface 13

Che dấu thơng tin tính đóng gói 14

Tổng kết chương 15

CHƯƠNG Persistence 16

Gap 16

DataMapper 16

Vấn đề 19

Các hạn chế 20

Tổng kết chương 21

Dependency Injection 22

Sneak Peak Unit Testing 23

Don’t avo id Coupling lik e the Plag ue 24

Dependency Injection 24

Constructor Injection 24

Framework 26

Sự cải tiến cuối 28

Tổng kết chương 29

CHƯƠNG Unit Testing 30

Tại không dùng Unit Test năm trước? 30

The Tools 31

nUnit 32

Unit Test gì? 33

Mocking 33

More on nUnit and RhinoMocks 36

UI and Database Testing 37

Trong chương 37

CHƯƠNG Object Relational Mappers 38

Infamous Inline SQL vs Stored Procedure Debate 38

Stored Procedures are More Secure 38

Thủ tục lưu trữ nhanh 39

NHibernate 40

Configuration 40

Relationships 42

Querying 43

Lazy Loading 45

Download 46

Trong chương 46

CHƯƠNG Trở lại bản: Bộ nhớ 47

(3)

Stack 47

Heap 47

Con trỏ 48

Mơ hình nhớ thực tế 50

Đóng hộp (Boxing) 50

ByRef 51

Quản lý thất thoát nhớ 53

Phân mảnh 53

Kết dính nhớ (Pinning) 54

Xác lập giá trị null 54

Kết thúc tiền định 55

Trong chương 55

CHƯƠNG Trở lại bản: Exception 56

Xử lý exception 56

Logging 57

Cleaning (làm sạch) 57

Ném ngoại lệ 58

Cơ chế ném ngoại lệ 58

Khi phải ném ngoại lệ 59

Tạo ngoại lệ riêng 60

Trong chương 62

CHƯƠNG Basic : Proxy This and Proxy That 63

Proxy Domain Pattern 63

Interception 64

In This Chapter 66

VỀ TÁC GIẢ

Karl Seguin nhà phát triển phần mềm Fuel Industries, MVP Microsoft, thành viên có nhiều ảnh hưởng cộng đồng CodeBetter.com biên tập viên DotNetSlackers Ông viết nhiều báo thành viên tích cực nhiều cộng đồng thơng tin Microsoft Ơng sống Ottawa, Ontario Canada

Trang web cá nhân Karl Seguin http://www.openmymind.net/

Blog tác giả, với blog nhiều chuyên gia tiếng khác, địa chỉ:

(4)

CHƯƠNG ALT.NET

NẾU CĨ SỰ KhƠNG HÀI LỊNG VỚI HIỆN TẠI, TỐT THƠI NẾU CĨ SỰ KHUẤY ĐỘNG, CÀNG TỐT HƠN NỮA NẾU CĨ SỰ KIÊN TRÌ KHƠNG NGỪNG NGHỈ, TƠI HÀI LỊNG VÀ TIẾP ĐĨ HÃY ĐỂ NHỮNG Ý TƯỞNG, NHỮNG SUY NGHĨ, VIỆC LÀM TÍCH CỰC ĐƯỢC NẢY NỞ NẾU MỘT NGƯỜI CẢM THẤY NHỎ BÉ, HÃY ĐỂ ANH TA TỰ LÀM MÌNH LỚN HƠN - HUBERT H HUMPHREY

Vài năm trước tơi có may mắn để chuyển hướng nghề lập trình Một hội làm hướng dẫn xuất tơi tận dụng cách tối đa Trong vịng vài tháng, kĩ lập trình tơi tăng theo hàm mũ, vài năm gần đây, tiếp tục tinh lọc kĩ Khơng nghi ngờ tơi cịn nhiều điều cần học, năm sau, tơi xấu hổ nhìn lại đoạn code viết lúc Tơi tự tin khả lập trình mình, Nhưng tơi chấp nhận biết ít, thường vậy, bắt đầu thật hiểu

Cuốn sách sưu tập viết mà mục đích để giúp cho lập trình viên nhiệt huyết tự luyện tập Xuyên suốt sách này, đề cập vấn đề thường trình bày chuyên sâu với người, ngoại trừ người tiếp cận chúng từ trước Tơi ln thấy có phần trội giới Net Một ủng hộ mạnh mẽ Microsoft phát triển tự nhiên VB6 ASP cổ điển( thường biết tới MSDN Way) Cái lại ủng hộ xu hướng hướng đối tượng bị ảnh hưởng bời khái niệm/ dự án lớn Java (được biết đến ALT.NET)

Trong thực tế, hai phần so sánh với MSDN Way hướng người sử dụng vào phương thức đặc biệt để chia nhỏ chương trình thành câu lệnh riêng biệt (sau cùng, ghi tham khảo API lí mà phải ghé thăm MSDN?) Trong đó, ALT.NET trọng vào chủ đề mang tính trừu tượng đồng thời cung cấp thực cụ thể Jeremy Miller cho rằng: Cộng đồng Net trọng nhiều vào API framework, không đủ nhấn mạnh vào tảng thiết kế lập trình Để đưa ví dụ liên quan cụ thể, MSDN Way ủng hộ mạnh mẽ việc sử dụng Datasets va DataTables cho việc liên lạc/kết nối sở liệu ALT.NET, nhiên, ý vào bàn luận mẫu thiết kế ổn định, vấn đề khơng tương thích trước mắt đối tượng cách thực đặc biệt, NHibernate (O/R Mapping), MonoRail(ActiveRecord) DataSets and DataTables Nói cách khác, khác với điều nhiều người nghĩ, ALT.NET thay cho MSDN Way, người phát triền phần mềm cần phải biết hiểu phương án thay cho giải pháp cách tiếp cận mà MSDN Way phần

Dĩ nhiên, từ miêu tả trên, ta thấy để theo đường ALT.NET cần phải có niềm đam mê lớn cần tảng kiến thức sâu rộng Con đường học vấn nhiều trắc trở nguồn tài liệu bổ ích bắt đầu tập hợp lại (đây lí tơi viết sách này) Tuy nhiên, nhận xứng đáng Với tơi, thành cơng nghề nghiệp đem đến hạnh phúc to lớn đời sống cá nhân

Mục tiêu

(5)

và kinh nghiệm thực tế cho thấy rằng, hệ thống dụng phần lớn thời gian (trên 50%) trạng thái bảo trì – thay đổi, sửa lỗi, nâng cấp Thứ hai là, gia tăng cách phát triển lặp (iterative) làm cho thay đổi tính chất liên tục tiến hành đoạn mã có sẵn (thậm chí bạn chưa trải nghiệm qua q trình phát triển lặp, ví dụ Agile, khách hàng bạn có nhiều khả yêu cầu bạn đủ kiểu thay đổi) Tóm lại, giải pháp bảo trì khơng hạ thấp chi phí mà cịn làm tăng chất lượng số lượng thành phẩm mà bạn bán

Thậm chí bạn cịn xa lạ với lập trình, bạn định hình suy nghĩ việc bảo trì khơng thể, dựa kinh nghiệm làm việc nhóm, sử dụng ứng dụng người khác, hay chí sửa lại mà bạn code vài tháng trước Một số điều quan trọng mà bạn làm ghi lại tất khơng google (hay MSN được) giải pháp tốt xung quanh Ví dụ, lập trình viên bỏ nhiều năm làm việc với ASP cổ điển biết tương thích chặt chẽ code HTML chưa thật lý tưởng

Viết đoạn mã bảo trì khơng phải điều dễ dàng Khi bạn bắt đầu, bạn phải siêng việc vào quỹ đạo Bạn nghi ngờ, người đặt suy nghĩ vào việc tạo chương trình bảo trì Cuối cùng, có số ý niệm bạn nên quen dần Khi lướt qua điều gí đó, bỏ thời gian nghiên cứu chúng thật kĩ tìm kiếm thêm thơng tin để có nhìn sâu sắc tảng vững vàng Và, quan trọng hết, cố tìm hiểu xem chúng ứng dụng vào dự án mà bạn thực

Sự đơn giản

Cơng cụ tốt để làm cho chương trình bạn bảo trì làm cho đơn giản Theo quan điểm thơng thường để bảo trì được, trước hệ thống phải xây dựng để đáp ứng cho tất yêu cầu thay đổi Tôi thấy hệ thống xây dựng dựa metarepositories (những bảng với cột khóa – key cột giá trị -value) cấu hình XML phức tạp, nhằm mục đích giải thay đổi mà khách hàng yêu cầu Những hệ thống xu hướng vấp phải giới hạn kĩ thuật nghiêm trọng (thể hệ thống chậm cồng kềnh) mà chúng ln khơng đạt mục đích (chúng ta đề cập vấn đề kĩ nói YAGNI) Theo kinh nghiệm thân, cách đắn để đặt mềm dẻo giữ cho hệ thống đơn giản tốt, bạn, lập trình viên khác đọc hiểu đoạn code cách dễ dàng thực số thay đổi cần thiết Vì phải xây dựng tập hợp luật cấu hình ta cần kiểm tra chiều dài tên người dùng hệ thống đạt độ dài cần thiết hay khơng? Trong chương sau, ta xem mơ hình “Phát triển định hướng testcase” - Test Driven Development - giúp đạt tới mức độ cao đơn giản cách đảm bảo ta tập trung vào ma khách hàng yêu cầu

YAGNI

Bạn cần - you aren’t going to need it - quan điểm lập trình cực đoan Theo đó, bạn khơng nên xây dựng chương trình bạn khơng cần tới tương lai Kinh nghiệm cho thấy bạn khơng thực cần đến cần phải có đơi chút thay đổi Bạn cá tháng để xây dựng hệ thống linh động cách đáng kinh ngạc cần dòng email đơn giản khách hàng để làm cho hệ thống trở nên hồn tồn vơ dụng Có lần, tơi bắt tay vào thiết kế máy báo cáo không giới hạn thời gian (open-ended reporting engine), tơi nhận hiểu sai ý khách hàng Những họ thật muốn có thiết bị báo cáo ngày (single daily report), thực cần 15 phút để làm điều

(6)

Ý tưởng đằng sau thời điểm hồi đáp cuối bạn cần trì hỗn việc xây dựng điều bạn buộc phải làm Thực là, số trường hợp, thời điểm xuất sớm q trình phát triển phần mềm Ý tưởng gần gũi với YAGNI, với ý nghĩa cho dù bạn thật cần nó, bạn nên trì hỗn việc xây dựng bạn khơng thể chờ Điều giúp bạn, khách hàng bạn, có thời gian để chắn bạn thật cần nó, đồng thời giúp bạn giảm thiểu số thay đổi mà bạn cần làm sau phát triển sản phẩm

Tránh lặp lại (DRY)

Sự lặp lại code gây đau đầu cho nhà phát triển phần mềm Nó khơng gây khó khăn cần thay đổi code (bởi bạn phải tìm tất vị trí có chức năng), mà cịn tạo nguy xuất lỗi nghiêm trọng làm cho chương trình trở nên khó khăn cách không cần thiết cho nhà phát triển tham gia Bằng việc thực hiên nguyên tắc không lặp lại xuyên suốt trình tạo sản phẩm (nắm bắt yêu cầu, thiết kế, thực, kiểm thử tài liệu hóa), bạn hồn thành với code rõ ràng dễ bảo trì Cần phải nhớ ý tưởng vượt xa việc copy paste cịn hướng đến việc khơng lặp lại code hình thức Tính đóng gói đối tượng tính liền lạc đoạn code giúp giảm bớt việc lặp lại Rõ ràng gắn kết

Điều hiển nhiên, điều quan trọng code bạn phải làm yêu cầu Điều có nghĩa hàm biến cần phải đặt tên cách phù hợp, sử dụng chuẩn chữ hoa/chữ thường, cần, phải tài liệu hóa đầy đủ Một class Producer phải thực xác điều mà bạn, nhà phát triển khác khách hàng bạn yêu cầu Hơn nữa, class câu lệnh bạn cần phải gắn kết chặt chẽ, có nghĩa chúng phải thống mục đích Nếu bạn thấy bạn viết class Khách hàng có chứa phần quản lí số liệu đơn hàng, có nhiều khả bạn cần tạo class tên Đơn hàng Các class chịu trách nhiệm cho nhiều phần riêng biệt nhanh chóng trở nên khó quản lí Trong chương tiếp theo, thấy khả lập trình hướng đối tượng việc viết code rõ ràng gắn kết

Cặp đôi

Cặp đơi xảy có class phụ thuộc lẫn Bất có thể, bạn cần phải giảm thiểu cặp đôi để giảm thiểu ảnh hưởng thay đổi gia tăng khả kiểm tra code Giảm thiểu chí loại bỏ cặp đôi thực dễ dàng nhiều người nghĩ; có thủ thuật cơng cụ giúp bạn hồn thành việc Cái khó phát cặp đôi không mong muốn Chúng ta bàn đến cặp đôi chi tiết ỡ chương sau

Unit Tests Continuous Integration

Kiểm tra đơn vị tích hợp liên tục, Unit Tests Continuous Integration, thường biết đến CI, đề tài đề cập sau Có hai điều bạn cần phải nắm trước Đầu tiên, hai hiệu việc giúp ta có code có tính bảo trì cao Unit tests giúp cho người phát triển có tự tin lớn lao Số lượng thay đổi việc tái phân rã – refactoring -và tính mà bạn sẵn sàng tạo vơ lớn bạn có lưới an toàn hàng trăm, hàng ngàn test tự động để bạn chưa phá vỡ chương trình Thứ hai là, bạn khơng muốn chấp nhận, thử, việc tiến hành kiểm tra đơn vị, bạn lãng phí thời gian đọc điều Phần lớn đề cập trọng nâng cao khả test code

Trong chương này

(7)(8)

Chương THIẾT KẾ HƯỚNG LĨNH VỰC

Thiết kế gì? Đó nơi mà bạn đứng hai giới – giới công nghệ giới người mục đích họ - bạn cố gắng mang hai giới lại cạnh – Mitchell Kapor

Có thể đốn bắt đầu Domain driven design (thiết kế định hướng theo lĩnh vực) lập trình hướng đối tượng (OOP) Ban đầu nghĩ nên bỏ qua số phần chủ đề này, điều gây khó khăn cho tơi bạn Có số hữu hạn cách thiết thực để thiết kế phần cốt lõi chương trình Cách tiếp cận phổ biến với NET nhà phát triền dùng mơ hình liệu tập trung(data-centric model) Có nhiều khả bạn chuyên gia cách tiếp cận – thành thạo sử dụng repeater lồng nhau, kiểu event hữu dụng ItemDataBound điều chỉnh thục DataRelations Một giải pháp khác, vốn thông dụng với nhà phát triển Java nhanh chóng phổ biến cộng đồng NET, thiên cách tiếp cận tập trung lĩnh vực

Thiết kế hướng liệu/ lĩnh vực

Cách tiếp cận tập trung vào liệu hướng lĩnh vực gì? Tập trung liệu nói chung có nghĩa bạn xây dựng hệ thống dựa hiểu biết bạn liệu mà bạn phải tương tác Cách tiếp cận tiêu biểu trước hết bạn xây dựng mẫu sở liệu cách tạo bảng quan hệ, cột quan hệ khóa ngoại; sau mơ ngơn ngữ C# VB.NET Lí phương pháp trở nên thông dụng giới phát triền NET Microsoft tốn nhiều thời gian để tự động hóa q trình dịch sang ngơn ngữ lập trình với lớp DataAdapters, DataSets DataTables Chúng ta biết với bảng với liệu, có website ứng dụng windows hoạt động chưa đầy năm phút với vài dòng code.Điểm mấu chốt tập trung vào liệu – ý kiến tốt hầu hết trường hợp Cách tiếp cận gọi “phát triển hướng liệu”

Domain-centric, hay thường gọi thiết kế hướng lĩnh vực (DDD), tập trung chủ yếu vấn đề lĩnh vực Vấn đề khơng bao gồm liệu mà cịn có cách hoạt động Vì thế, ví dụ, ta không trọng TênRiêng nhân viên mà phải ý đến khả TăngLương Vấn đề lĩnh vực cách nói tương trưng cho nghiệp vụ mà bạn xây dựng hệ thống để quản lí Cơng cụ sử dụng lập trình hướng đối tượng (OOP), việc bạn biết sử dụng ngôn ngữ hướng đối tượng C# hay VB.NET khơng có nghĩa bạn lập trình hướng đối tượng

(9)

rằng hướng liệu thực sự lựa chọn đắn số trường hợp Tôi cho phần lớn đối lập hai trường phái Microsoft thiên thiết kế hướng liệu thực tế khơng tương thích tốt với mà nhà phát triển NET làm (phát triển ứng dụng doanh nghiệp), không sử dụng cách, kết tạo dịng code khó bảo trì Nhiều lập trình viên, cộng đồng NET gãi đầu suy nghĩ Microsoft kiên định làm ngược lại với điều thông thường phải luôn cố gắng bắt kịp cách vụng )

Ngừơi dùng, khách hàng Các bên liên quan

“ Trong q khứ, tơi thường xun bực với khách hàng Họ khó chịu, khơng biết họ cần ln ln có định sai lầm Nhưng tơi thật nghĩ lại điều đó, tơi nhận không thông minh nghĩ Khách hàng biết rõ công việc họ tơi nhiều Khơng thế, mà cịn tiền họ công việc giúp họ có nhiều lợi nhuận từ đồng vốn Và mối quan hệ với khách hàng trở nên tích cực có mối cộng tác tốt, không kết công việc tăng lên đáng kể mà việc lập trình trở nên thú vị trở lại.”

Vài điều rút cách nghiêm túc từ phát triển Agile ảnh hưởng lẫn đội phát triển phần mềm, người dùng khách hàng Trên thực tế, có thể, tơi khơng phân biệt nhà phát triển hay khách hàng, tất thực thể thống : đội phát triển Bất kể bạn có đủ may mắn hay khơng tình (đơi bạn dính đến luật pháp đơi khách hàng không sẵn sàng hợp tác chặt chẽ), hiểu chức người quan trọng Khách hàng người trả tiền người đưa định cuối vấn đề chủ chốt độ ưu tiên Người dùng người sử dụng hệ thống Khách hàng thường người dùng người dùng Ví dụ website có người dùng ẩn danh, người dùng thức, người điều hành người quản lý Cuối cùng, bên liên quan có liên quan đến hệ thống Một trang web có trang “cha” trang “anh chị” , nhà quảng cáo, PR chuyên gia lĩnh vực

Khách hàng có cơng việc khó khăn Họ phải dành ưu tiên cách khách quan cho điều mà người muốn, bao gồm họ, thu xếp với ngân quỹ có hạn Rõ ràng, họ phạm sai lầm, họ khơng hiểu hồn tồn nhu cầu người dùng, họ hiểu lầm thơng tin mà bạn cung cấp, họ dành ưu tiên cho nhu cầu họ người khác Là nhà phát triển phần mềm, công việc bạn giúp họ tránh sai lầm nhiều tốt đồng thời đáp ứng nhu cầu họ

Bất kể bạn có xây dựng hệ thống mục đích thương mại hay khơng, tiêu chí đánh giá cuối cho thành cơng hệ thống cách mà người dùng cảm nhận Vì thế, lúc bạn khách hàng làm việc với nhau, mong hai hướng tới nhu cầu người dùng Nếu bạn khách hàng bạn xem việc xây dựng hệ thống cho người dùng quan trọng, thực khuyên bạn nên đọc thật kĩ User Stories – nơi tốt để bắt đầu Mike Cohn’s excellent User Stories Applied1

Cuối cùng, lí cho có mặt chương chuyên gia lĩnh vực Chuyên gia lĩnh vực người biết rõ đầu vào đầu giới mà hệ thống bạn tồn Cách không lâu, thành viên dự án phát triển lớn tổ hợp tài chính, thật có hàng trăm chuyên viên lĩnh vựcmà phần lớn số nhà kinh tế nhà kế toán Họ người nhiệt tình với cơng việc giống bạn say mê lập trình Bất trở thành chuyên gia lĩnh vực – khách hàng, người dùng, cổ đông chí bạn Sự phụ thuộc bạn vào chuyên gia lĩnh vực tăng với phức tạp hệ thống

Hướng lĩnh vực (The domain object)

(10)

để bắt đầu – nhiều người biết hết đề cập Chúng ta khơng đề cập tính bền vững - persistence - (có thể xem phần sở liệu) Nếu bạn xa lạ với cách thiết kế này, bạn ln cảm thấy thắc mắc sở liệu đoạn code truy xuất liệu Hãy cố đừng lo lắng Trong chương ta đề cập tới phần tính bền vững, chương nữa, ta nghiên cứu sâu

Ý tưởng đằng sau thiết kế hướng lĩnh vực xây dựng hệ thống phản ánh lại lĩnh vực vấn đề mà bạn cố giải Đây nơi mà chuyên gia lĩnh vực nhập – họ giúp bạn hiểu hệ thống hoạt động (thậm chí q trình thực tay giấy) hệ thống phải hoạt động Ban đầu bạn bị ngợp hiểu biết họ - họ nói thứ mà bạn chưa biết đến họ phải ngạc nhiên trước ánh nhìn ngơ ngác bạn Họ dùng từ viết tắt, từ đặc biệt điều làm cho bạn phải tự hỏi liệu đảm đương cơng việc hay khơng Cuối cùng, mục đích thực nhà phát triển ứng dụng doanh nghiệp - hiểu lĩnh vực vấn đề Bạn biết lập trình nào, bạn có biết lập trình hệ thống kiểm kê cụ thể mà bạn yêu cầu? Một phải hiểu ngơn ngữ người khác, chuyên gia lĩnh vực học cách lập trình khơng cịn việc làm

Với tất đọc qua phần hiểu học lĩnh vực phần phức tạp công việc lập trình Vì thế, làm cho code bạn tương đồng nhiều với lĩnh vực có ích Về bản, điều tơi nói đến giao tiếp Nếu khách hàng bạn nói Mục đích chiến lược, điều mà tháng trước chẳng có ý nghĩa với bạn, code bạn MụcĐíchChiếnLược số điểm mơ hồ, nguy hiểu lầm dẹp bỏ Nhiều người, kể tôi, cho nơi tốt để bắt đầu công việc học cụm danh từ mà chuyên gia lĩnh vực người dùng hay sử dụng Ví dụ bạn xây dựng hệ thống cho công ty mua bán xe bạn nói chuyện với người bán hàng (người vừa người dùng vừa chuyên gia ngành), anh chắn nói Khách hàng, Xe (car), Mo-del (moMo-del), Kiện hàng (package) Nâng Cấp (upgrade), Tiền toán (payment), v v Những điều nồng cốt công việc anh ấy, nên tất nhiên phần hệ thống bạn Ngồi cụm danh từ điều cần lưu ý giao hội tụ ngôn ngữ kinh doanh, vốn biết tới ngôn ngữ thống Ý tưởng dùng ngôn ngữ cho người dùng hệ thống giúp cho việc bảo trì dễ dàng giảm thiểu sai lầm

Chính xác bắt đầu cơng việc hoàn toàn phụ thuộc vào bạn Thiết kế hướng lĩnh vực không thiết phải bắt đầu việc mơ phong lĩnh vực (mặc dù ý kiến hay!), mà có nghĩa bạn nên tập trung vào lĩnh vực để điều khiển định bạn Đầu tiên, bạn hồn tồn bắt đầu với mơ hình liệu bạn, khám phá mô hình phát triển định hướng testcase sử dụng cách tiếp cận khác thích hợp với DDD Tuy nhiên, giả sử nói chuyện với khách hàng vài người bán hàng, nhận điều khó khăn chủ yếu kiểm soát phụ thuộc lẫn khả nâng cấp hệ thống Công việc mà làm thiết kế bốn class:

public class Car{} public class Model{} public class Package{} public class Upgrade{}

Tiếp theo thêm vào code dựa vài giả thiết tin cậy:

public class Car {

private Model _model;

(11)

public void Add(Upgrade upgrade){ //todo } }

public class Model {

private int _id; private int _year; private string _name;

public ReadOnlyCollection<Upgrade> GetAvailableUpgrades() {

return null; //todo }

}

public class Upgrade {

private int _id; private string _name;

public ReadOnlyCollection<Upgrade> RequiredUpgrades {

get {

return null; //todo }

} }

Cơng việc hồn tồn đơn giản Chúng ta thêm vào vài thông tin truyền thống ( id, tên), vài tham chiếu (cả Cars Models có Upgrades), hàm Add vào class Car Bây ta thay đối bắt đầu viết hoạt động thực tế

public class Car {

private Model _model;

//todo where to initialize this? private List<Upgrade> _upgrades; public void Add(Upgrade upgrade) {

_upgrades.Add(upgrade); }

public ReadOnlyCollection<Upgrade> MissingUpgradeDependencies()

{

List<Upgrade> missingUpgrades = new List<Upgrade>(); foreach (Upgrade upgrade in _upgrades)

{

foreach (Upgrade dependentUpgrade in upgrade.RequiredUpgrades) {

if (!_upgrades.Contains(dependentUpgrade) && !

missingUpgrades.Contains(dependentUpgrade)) {

missingUpgrades.Add(dependentUpgrade); }

} }

return missingUpgrades.AsReadOnly(); }

}

(12)

theo tìm xem nâng cấp gây việc làm nâng cấp khác, nói cách khac, bạn phải lựa chọn WheelDrive với Traction Control bạn ; nhiên, dừng lại Mục đích phần đưa cách bắt đầu phần mở đầu

Giao diện người dùng (UI)

Bạn thấy chưa nhắc tới UIs Bởi lĩnh vực ta độc lập với lớp presentation – dùng để làm mạnh website, ứng dụng windows, dịch vụ windows Việc cuối mà bạn muốn làm trộn lẫn lớp presentation bạn logic lĩnh vực Làm tạo đoạn code khó đọc khó kiểm tra mà cịn làm cho ta tái sử dụng logic UI (có thể khơng phải vấn đề quan trọg, tính dễ đọc dễ bảo trì khác) Tiếc thay, nhiều nhà phát triển ASP.NET lại trộn lẫn UI lớp lĩnh vực Thậm chí tơi nói rằng, thường thấy hoạt động trơng suốt q trình hàm xử lý kiện ấn nút hay kiện tải trang – page load – ASP.NET Những khung trang ASP.NET điều khiển ASP.NET UI – thực hoạt động Nút Save không nên thông qua quy tắc kinh doanh rắc rối (hoặc tệ truy cập sỡ liệu cách trực tiếp), mà mục đích sửa lại trang ASP.NET dựa kết lớp lĩnh vực – nên thơng qua trang khác, đưa vài tin báo lỗi yêu cầu thêm thông tin Hãy nhớ bạn muốn viết đoạn code liền lạc với Logic ứng dụng ASP.NET bạn nên tập trung vào việc làm việc cho tốt – Tơi tin không phản đối đối tượng cần phải quản lí page, điều có nghĩa khơng thể thực thi chức liên quan tới lĩnh vực.Thêm vào đó, đặt logic trongnhững file code behind thường vi phạm nguyên tắc “Không tự lặp lại”, đơn giản việc tái sử dụng code file aspx.cs khó

Chính điều trên, bạn chờ lâu để bắt đầu UI Trước hết, ta nên lấy phản hồi khách hàng sớm thường xuyên tốt Tôi không cho họ ấn tượng ta gửi cho họ loạt file cs/vb với class Thứ hai, đưa lớp lĩnh vực thực tế giúp ta thấy vài chỗ hỏng vài điều khơng hợp lí Ví dụ, ngưng kết nối tự nhiên trang web nghĩa ta phải chỉnh sửa đôi chút giới hướng đối tượng khiết để có trải nghiệm tốt người dùng Theo kinh nghiệm unit test cịn q hẹp để thấy khuyết tật

Bạn hài lòng biết ASP.NET WinForms xử lý đoạn mã tập trung lĩnh vực tốt lớp tập trung liệu Bạn ràng buộc liệu – data binding với tập hợp NET sử dụng phiên - session kỹ thuật đệm - cache bạn thường làm, làm bạn hay làm Trên thực tế, ngồi ra, ảnh hưởng UI nghiêm trọng Tất nhiên bạn không nên ngạc nhiên biết lập trình viên ALT.NET cho bạn nên mở rộng đầu óc làm việc với cơng cụ hỗ trợ trình bày ASP.NET Page Framework không thiết phải công cụ tốt cho công việc bạn – phần đông cho phức tạp cách khơng cần thiết không bền vững Chúng ta đề cập vấn đề kĩ chương sau, bạn muốn tìm hiểu rõ hơn, tơi khun bạn nên đọc MonoRails ( khung cho NET) khung giá MVC Microsoft tung Việc cuối muốn dành cho người chán nản với rộng lớn thay đổi, trở lại đề tái

Bí thủ thuật

Ta kết thúc chương việc lướt qua vài điều có ích mà ta làm với class Ta bước bề mặt tảng băng, hi vọng thông tin giúp bạn bước cách

(13)

Chúng ta làm Khách Hàng mua Xe mới? Rõ ràng ta cần tạo đối tượng kiều Xe rõ mo-del Một cách truyền thống để làm việc sử dụng hàm tạo – constructor đơn giản tạo đối tượng với từ khóa new Một cách tiếp cận khác sử dụng cách tiếp cận

factory pattern để tạo đối tượng:

public class Xe {

private Model _model;

private List<Upgrade> _upgrades; private Xe()

{

_upgrades = new List<Upgrade>(); }

public static Xe CreateCar(Model model)

{

Xe xe = new xe(); xe._model = model; return xe;

} }

Có hai ưu điểm cho cách tiếp cận Thứ nhất, ta trả đối tượng rỗng, điều mà ta làm với cách dùng constructor – điều khơng có ích cho bạn vài trường hợp riêng Thứ hai, có nhiều cách để tạo đối tượng, bạn có nhiều hội đề tạo tên hàm có ý nghĩa Ví dụ xuất đầu bạn muốn tạo đối tượng class NgườiDùng, bạn có NgườiDùng.XácĐịnhBởiThơngTin (string tên, string mật-mã), NgườiDùng XácĐịnhBởiId ( int id) Người Dùng XácĐịnhBởiVịTrí (string vị-trí) Bạn tạo hàm giống với cách nạp chồng constructor, tạo rõ ràng Thành thật mà nói, tơi ln phải khó khăn để chọn lựa để sử dụng, vấn đề lựa chọn sở thích riêng

Chỉ định truy cập – Access Modifier

Khi bạn tập trung viết class đóng gói hoạt động nghiệp vụ, API phong phú tạo để UI bạn sử dụng Giữ cho API sáng rõ ràng ý tưởng tốt Một cách đơn giản làm cho API nhỏ lại cách giấu tất trừ phương thức cần thiết Vài phương thức rõ ràng phải public vài khác phải private, bạn không chắn, để quyền truy cập hạn chế thay đổi cần thiết Tôi ưa chuộng phương thức internal phần lớn phương thức thuộc tính Thành phần internal thấy thành viên khối hợp ngữ - bạn thực phân chia lớp bạn nhiều khối hợp ngữ (vốn thường ý tưởng hay), bạn thu nhỏ API đáng kể

Interface

Giao diện giữ vai trò quan trọng việc giúp ta tạo đoạn code dễ bảo trì Ta sử dụng để tách rời code tạo class giả để dùng cho kiểm tra đơn vị - unit testing Một giao diện giao kèo trước mà class thực thi phải tuân thủ Giả sử muốn tóm gọn tất liệu class tên SqlServerDataAccess:

internal class SqlServerDataAccess {

internal List<Upgrade> RetrieveAllUpgrades() {

(14)

}

public void ASampleMethod() {

SqlServerDataAccess da = new SqlServerDataAccess(); List<Upgrade> upgrades = da.RetrieveAllUpgrades(); }

Bạn thấy phần code mẫu cuối ví dụ có tham chiếu trực tiếp tới SqlServerDataAccess – giống với phương thức khác cần phải liên hệ với sở liệu Những đoạn code “cặp đơi” chặt chẽ khó thay đổi khó kiểm tra (ta khơng thể kiểm tra ASampleMethod mà đầy đủ phương thức RetrieveAllUpgrades) Ta làm giảm bớt liên hệ chặt chẽ cách lập trình giao diện thay thế:

internal interface IDataAccess {

List<Upgrade> RetrieveAllUpgrades(); }

internal class DataAccess {

internal static IDataAccess CreateInstance() {

return new SqlServerDataAccess(); }

}

internal class SqlServerDataAccess : IDataAccess {

public List<Upgrade> RetrieveAllUpgrades() {

return null; //todo implement }

}

public void ASampleMethod() {

IDataAccess da = DataAccess.CreateInstance(); List<Upgrade> upgrades = da.RetrieveAllUpgrades(); }

Ta giới thiệu giao diện với lớp trợ giúp để đưa ví dụ cho giao diện Nếu ta muốn thay đổi phần thực thi, ví dụ OracleDataAccess, đơn giản ta cần tạo class Oracle mới, phải thực giao diện, thay đổilớp trợ giúpđể trả Thay phải thay đổi hàng loạt (có thể lên tới hàng trăm chỗ), ta đơn giản cần thay đổi chỗ

Đây ví dụ đơn giản cho việc sử dụng giao diện để hỗ trợ Chúng ta làm cải thiện đoạn code cách tạo ví dụ class thơng qua liệu cấu hình tạo khung dành riêng cho cơng việc (đó xác điều chuẩn bị làm) Chúng ta thường xuyên thiên việc lập trình với giao diện với lớp thực sự, bạn chưa quen với điều cố tìm đọc thêm

Che dấu thơng tin tính đóng gói

(15)

Tổng kết chương

(16)

CHƯƠNG 3 Persistence

CODD'S AIM WAS TO FREE PROGRAMMERS FROM HAVING TO KNOW THE PHYSICAL STRUCTURE OF DATA OUR AIM IS TO FREE THEM IN ADDITION FROM HAVING TO

KNOW ITS LOGICAL STRUCTURE – LAZY SOFTWARE

Trong chương trước, thảo luận DDD mà khơng nói nhiều đến sở liệu Nếu bạn thường lập trình với DataSet, bạn chắn có nhiều câu hỏi việc làm việc DataSet lớn nên bạn cần phải cẩn thận Trong chương này, bắt đầu thảo thuận quanh việc làm để giải persistence sử dụng DDD Chúng ta viết code để bắt cầu đối tượng C# bảng SQL Trong phần sau, xem xét vấn đề cao (hai hướng mapping O/R khác nhau), DataSet- giúp làm nhiều việc phức tạp Chương đóng lại vấn đề thảo luận từ trước mở vấn đề với nhiều mẫu persistence Gap

Như biết, chương trình chạy nhớ yêu cầu không gian để lưu trữ hay persist thông tin Ngày nay, giải pháp lựa chọn sở liệu quan Persistence thực chủ đề lớn lĩnh vực phá triển phần mềm vì, khơng có giúp đỡ pattern cơng cụ khơng phải cách đơn giản để thực thành công Đối với lập trình hướng đối tượng, thách thức đưa với tên: Object-Relational Impedance Mismatch Khá nhiều nghĩa cho liệu quan hệ (relational data) không ánh xạ hoàn toàn với đối tượng đối tượng khơng ánh xạ hồn tồn Microsoft cố gắng lờ vấn đề tạo đơn giản biểu diễn quan hệ (relational representation) với code hướng đối tượng- cách tiếp cận khéo léo, khơng phải khơng có sai sót hiệu suất kém, lỗ hổng, khả kiểm tra kém, rắc rối, bảo trì (Cơ sở liệu hướng đối tượng, tốt theo kiến thức tôi, làm giảm.)

Tốt cách lờ vấn đề này, nên đối mặt với Chúng ta đối mặt với để tận chụng tốt hai world – quy tắc business phức tạp thực OOP lưu trữ liệu lấy thong qua sở liệu quan hệ Dĩ nhiên, chúng bắt cầu cho vượt qua gap Nhưng gap thực gì? Impedance Mismatch gì? Bạn nghĩa khơng thể khó để việc đẩy liệu quan hệ đối tượng đưa chúng trở lại bảng (Hầu hết trường hợp nào… giả định ln q trình đơn giản)

DataMapper

(17)

public class Upgrade {

private int _id; private string _name;

private string _description; private decimal _price;

private List<Upgrade> _requiredUpgrades; public int Id

{

get { return _id; }

internal set { _id = value; } }

public string Name {

get { return _name; } set { _name = value; } }

public string Description {

get { return _description; } set { _description = value; } }

public decimal Price {

get { return _price; } set { _price = value; } }

public List<Upgrade> RequiredUpgrades {

get { return _requiredUpgrades; } }

}

Chúng ta vừa thêm vào trường bạn bạn muốn có class Tiếp theo, tạo bảng mà nắm, persist hay nâng cấp thơng tin

CREATE TABLE Upgrades (

Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY, [Name] VARCHAR(64) NOT NULL,

Description VARCHAR(512) NOT NULL, Price MONEY NOT NULL,

)

(18)

internal class SqlServerDataAccess {

private readonly static string _connectionString = "FROM_CONFIG" internal List<Upgrade> RetrieveAllUpgrades()

{

//use a sproc if you prefer

string sql = "SELECT Id, Name, Description, Price FROM Upgrades"; using (SqlCommand command = new SqlCommand(sql))

using (SqlDataReader dataReader = ExecuteReader(command)) {

List<Upgrade> upgrades = new List<Upgrade>(); while (dataReader.Read())

{

upgrades.Add(DataMapper.CreateUpgrade(dataReader)); }

return upgrades; }

}

private SqlDataReader ExecuteReader(SqlCommand command) {

SqlConnection connection = new SqlConnection(_connectionString); command.Connection = connection;

connection.Open();

return command.ExecuteReader(CommandBehavior.CloseConnection) }

}

ExecuteReader phương thức giúp giảm nhẹ đoạn mã thừa RetrieveAllUpgrades chọn tất upgrade lưu chúng vào danh sách thông qua

DataMapper Hàm CreateUpgrade (xem dưới) đoạn code tái sử dụng mà dùng để ánh xạ (map) thông tin upgrade vào sở liệu domain Nó khơng phức tạp mơ hình Domain mơ hình liệu tương tự

internal static class DataMapper {

internal static Upgrade CreateUpgrade(IDataReader dataReader) {

Upgrade upgrade = new Upgrade();

upgrade.Id = Convert.ToInt32(dataReader["Id"]); upgrade.Name = Convert.ToString(dataReader["Name"]); upgrade.Description = Convert.ToString(dataReader["Description"]); upgrade.Price = Convert.ToDecimal(dataReader["Price"]);

return upgrade; }

(19)

Nếu cần làm, tái sử dụng hàm CreateUpgrade lúc cần thiết Ví dụ cần lấy thơng tin upgrade id hay price– hai phương thức lớp

SqlServerDataAccess

Rõ ràng ứng dụng logic giống muốn lưu trữ Upgrade objects ngược trở lại

Đây giải pháp :

internal static SqlParameter[] ConvertUpgradeToParameters(Upgrade upgrade) {

SqlParameter[] parameters = new SqlParameter[4];

parameters[0] = new SqlParameter("Id", SqlDbType.Int); parameters[0].Value = upgrade.Id;

parameters[1] = new SqlParameter("Name", SqlDbType.VarChar, 64); parameters[1].Value = upgrade.Name;

parameters[2] = new SqlParameter("Description", SqlDbType.VarChar, 512); parameters[2].Value = upgrade.Description;

parameters[3] = new SqlParameter("Price", SqlDbType.Money); parameters[3].Value = upgrade.Price;

return parameters; }

Vấn đề

Mặc dù thực tế lấy ví dụ đơn giản phổ biến, vấn chạy dreaded impedance mismatch Chú ý cách tầng Data Access ( SqlServerDataAccess hay DataMapper) không nắm nhiều tập hợp RequiredUpgrades cần thiết Bời điều nắm giữ (handle) phức tạp quan hệ Trong domain world tham khảo ( hay tập tham khảo) đến đối tượng khác; nơi mà relational world sử dụng khóa ngoại Sự khác trở ngại bất biến Việc sửa đổi khó Đầu tiên, thêm bảng join many-to-many mà liên kết tới upgrade với upgrade khác mà chúng cần ( 0,1 hay nhiều nữa)

CREATE TABLE UpgradeDepencies (

UpgradeId INT NOT NULL,

RequiredUpgradeId INT NOT NULL, )

(20)

internal List<Upgrade> RetrieveAllUpgrades() {

string sql = @"SELECT Id, Name, Description, Price FROM Upgrades;

SELECT UpgradeId, RequiredUpgradeId FROM UpgradeDepencies"; using (SqlCommand command = new SqlCommand(sql))

using (SqlDataReader dataReader = ExecuteReader(command)) {

List<Upgrade> upgrades = new List<Upgrade>();

Dictionary<int, Upgrade> localCache = new Dictionary<int, Upgrade>(); while (dataReader.Read())

{

Upgrade upgrade = DataMapper.CreateUpgrade(dataReader); upgrades.Add(upgrade);

localCache.Add(upgrade.Id, upgrade); }

dataReader.NextResult(); while (dataReader.Read()) {

int upgradeId = dataReader.GetInt32(0);

int requiredUpgradeId = dataReader.GetInt32(1); Upgrade upgrade;

Upgrade required;

if (!localCache.TryGetValue(upgradeId, out upgrade)

|| !localCache.TryGetValue(requiredUpgradeId, out required)) {

//probably should throw an exception //since our db is in a weird state continue;

}

upgrade.RequiredUpgrades.Add(requiredUpgrade); }

return upgrades; }

}

Chúng ta kéo thông tin extra join table với câu query ban đầu tạo từ điển tìm kiếm để truy cập nhanh chóng upgrade id chúng Tiếp thep lặp thông qua join table, lấy upgrade thích hợp từ từ điển tìm kiếm thêm chúng vào tập hợp

Đó khơng phải giải pháp tốt làm việc tốt Chúng ta refactor hàm để làm dễ đọc hơn, trường hợp đơn giản này, the job

Các hạn chế

Mặc dù bắt đầu tìm kiếm việc ánh xạ (mapping), đáng để tìm hạn chế mà tự đặt cho Bạn lần xuống cịn đường tự viết loại code mà khỏi kiểm sốt nhanh chóng Nếu muốn thêm phương thức xếp hay lọc phải viết SQL động phải viết nhiều phương thức Chúng ta kết thúc việc viết nhóm phương thức RetrieveUpgradeByX mà tương tự

(21)

Vấn đề quan trọng phải làm việc với id Nếu gọi RetrieveAllUpgrades hai lần, nhận phiên riêng biệt upgrade Điều cho kết mâu thuẫn:

SqlServerDataAccess da = new SqlServerDataAccess(); Upgrade upgrade1a = da.RetrieveAllUpgrades()[0]; Upgrade upgrade1b = da.RetrieveAllUpgrades()[0]; upgrade1b.Price = 2000;

upgrade1b.Save();

Giá thay đổi để việc upgrade không bị phản chiếu đến thể trỏ tới upgrade1a Trong số trường hợp vấn đề Tuy nhiên, nhiều trường hợp, bạn muốn tầng Data Acess theo dõi id thể (instance) mà tạo thực số điều khiển (bạn đọc thêm cách tìm kiếm mẫu Identify Map)

Chắc chắn có thêm số hạn chế, điều cuối nói việc phải làm việc với Unit of work (một lần bạn đọc thêm cách tra Google với cụm Unit of Work) Khi bạn viết code tầng Data Access, bạn cần chắn bạn persist đối tượng, bạn persist tất đối tượng tham khảo cập nhật, cần thiết Nếu bạn làm việc phần admin hệ thống bán xe hơi, bạn tạo Model tạo Upgrade Nếu bạn gọi Save Model bạn, bạn cần chắn Updare bạn lưu Giải pháp đơn giản gọi Save thường xuyên với hành động - cảThe simplest solution is to call save often for each individual action – điều vừa khó (các quan hệ sâu vài cấp) không hiệu Một cách tương tự bạn thay đổi thuộc tính sau phải định việc lưu lại tất trường hay theo dõi thay đổi thuộc tính cập nhật chúng Một lần nữa, hệ thống nhỏ, vấn đề lớn Đối với hệ thống lớn hơn, gần bất khả thi để thực tay (ngoài ra, tốt việc tốn thời gian xây dựng Unit of work bạn, bạn nên viết chức yêu cầu client)

Tổng kết chương

(22)

Dependency Injection

I WOULD SAY THAT MODERN SOFTWARE ENGINEERING IS THE ONGOING REF INEMENT OF THE EVER-INCREASING DEGREES OF DECOUPLING YET, WHILE THE HISTORY OF SOFTWARE SHOWS THAT COUPLING IS BAD, IT ALSO SUGGESTS THAT COUPLING IS UNAVOIDABLE A N ABSOLUTELY DECOUPLED APPLICATION IS US ELESS BECAUSE IT ADDS NO VALUE DEVELOPERS CAN ONLY ADD VALUE BY COUPLING THINGS TOGETHER THE VERY ACT OF WRITING CODE IS COUPLING ONE THING TO ANOTHER THE REAL QUESTION IS HOW TO WISELY CHOOSE WHAT TO BE COUPLED TO - JUVAL LÖWY

t’s common to hear developers promote layering as a means to provide extensibility The most common example, and one I used in Chapter when we looked at interfaces, is the ability to switch out your data access layer in order to connect to a different database If your projects are anything

like mine, you know upfront what database you’re going to use and you know you aren’t going to have to change it Sure, you could build that flexibility upfront - just in case - but what about keeping things simple and You Aren’t Going To Need IT (YAGNI)?

I used to write about the importance of domain layers in order to have re-use across multiple presentation layers: website, windows applications and web services Ironically, I’ve rarely had to write multiple front-ends for a given domain layer I still think layering is important, but my reasoning has changed I now see layering as a natural by-product of highly cohesive code with at least some thought put into coupling That is, if you build things right, it should automatically come out layered

The real reason we’re spending a whole chapter on decoupling (which layering is a high-level implementation of) is because it’s a key ingredient in writing testable code It w asn’t until I started unit testing that I realized how tangled and fragile my code was I quickly became frustrated because method X relied on a function in class Y which needed a database up and running In order to avoid the headaches I went through, we’ll first cover coupling and then look at unit testing in the next chapter

(A point about YAGNI While many developers consider it a hard rule, I rather think of it as a general guideline There are good reasons why you want to ignore YAGNI, the most obvi ous is your own experience If you know that something will be hard to implement later, it might be a good idea to build it now, or at least put hooks in place This is something I frequently with caching, building an ICacheProvider and a NullCacheProvider implementation that does nothing, except provide the necessary hooks for a real implementation later on That said, of the numerous guidelines out there, AY G NI , RD Y a nd S u s t a i n a b l e P a c e a re easily the three I consider the most important.)

(23)

Sneak Peak Unit Testing

Nói việc bắt cặp có liên quan đến kiểm tra đơn vị (unit testing) tương tự vấn đề gà trứng – có trước Tôi nghĩ tốt nên tiếp tục với kiểm tra đơn vị Điều quan trọng kiểm tra đơn vị bao gồm đơn vị Bạn không nên tập trung kiểm tra từ đầu đến cuối, nên tập trung vào thuộc tính độc lập Ý tưởng bạn kiểm tra thuộc tính qua phương pháp kiểm tra tính tương tác chúng với khác, toàn hệ thống bạn cứng nhắc Phương pháp mà bạn muốn dùng để kiểm tra đơn vị có độc lập với lớp khác, mà dễ dàng thực thi vòng nội dung kiểm tra (ví dụ sở liệu, hay thành phần trình duyệt web) Vì lý này, kiểm tra đơn vị sử dụng lớp giả (mock) – hay lớp tưởng tượng

Xem ví dụ sau, lưu trạng thái xe hơi:

public class Car {

private int _id; public void Save() {

if (!IsValid()) {

//todo: come up with a better exception

throw new InvalidOperationException("The car must be in a valid state");

}

if (_id == 0) {

_id = DataAccess.CreateInstance().Save(this); }

else {

DataAccess.CreateInstance().Update(this); }

}

private bool IsValid() {

//todo: make sure the object is in a valid state return true;

} }

Để kiểm tra hiệu phương thức Save, có điều phải làm:

1 Chắc chắn exception ném cố lưu xe trạng thái hợp lệ

2 Chắc chắn phương thức Save tầng Data Access gọi có xe Chắc chắn Updatemethod đuợc gọi có xe tồn

(24)

khái niệm Save Update, chắn argument xác phải kiểm tra đạt, trả giá trị mong muốn Mocking framework thú vị hữu ích đoạn code bạn chặt chẽ theo cặp

Don’t avo id Coupling lik e the Plag ue

Trong trường hợp bạn qn nói chương 1, bắt cặp (coupling) đơn giản những ta gọi lớp đòi hỏi lớp khác để hoạt động Điều quan trọng đoạn code độc lập với lớp khác Nếu bạn viết string site = “CodeBetter”, bạn thực việc bắt cặp với lớp System.String – thay đổi, đoạn code bạn có khả break cao Dĩ nhiên, điều bạn nên biết trường hợp đa số, ví dụ ví dụ với chuỗi, bắt cặp khơng phải điều không tốt

Chúng ta không muốn tạo interface provider cho lớp Có thể chấp nhận cho lớp Car giữ tham khảo trực tiếp lớp Upgrade Điều bắt cặp tới thành phần bên ( liệu, state server, cache server, dịch vụ web), đoạn code đòi hỏi thiết lập mở rộng ( khung liệu) và, nói project trước, đoạn code phát triển output ngẫu nhiên (password, key) Điều miên tả gây nhầm lẫn, sau chapter chapter sau,l bạn sử dụng unit testing, bạn cảm thấy điều nên làm điều nên tránh

Sẽ ý kiến hay bạn tách cặp liệu từ domain, có ví dụ chương

Dependency Injection

Trong chương 2, thấy giao diện giúp cho trườngause – nhiên, đoạn code cúng cấp không cho phép cung cấp động thực giả IdataAccess cho lớp DataAccessfactory để trả về.Nói cách khác để giải điều này, dựa vào pattern gọi Dependency Injection (DI) DI giải pháp cụ thể cho tình này, vì, tên gọi, pattern chuyển

dependency code cứng thành đưau vào lúc chạy Chúng ta xem xét mẫu DI, tự làm, dựa vào thư viện bên thứ ba third party library

Constructor Injection

(25)

internal interface IDataAccess {

int Save(Car car); void Update(Car car); }

internal class MockDataAccess : IDataAccess {

private readonly List<Car> _cars = new List<Car>(); public int Save(Car car)

{

_cars.Add(car); return _cars.Count; }

public void Update(Car car) {

_cars[_cars.IndexOf(car)] = car; }

}

Mặc dù chức upgrade mock chúng cải thiện, làm sau Chỉ có thay đổi nhỏ so với lớp Car nguyên bản:

public class Car {

private int _id;

private IDataAccess _dataProvider;

public Car() : this(new SqlServerDataAccess()) {

}

internal Car(IDataAccess dataProvider) {

_dataProvider = dataProvider; }

public void Save() {

if (!IsValid()) {

//todo: come up with a better exception

throw new InvalidOperationException("The car must be in a valid state");

}

(26)

_id = _dataProvider.Save(this); }

else {

_dataProvider.Update(this); }

} }

Hãy nhìn vào đoạn code bên Chú ý việc sử dụng trỏ có nghĩa DI khơng có ảnh hưởng đến đoạn code hành – Nếu bạn chọn không thêm vào IdataAccess Nếu bạn muốn thêm vào thuộc tính, ví dụ MockDataAccess, ta có thể:

public void AlmostATest() {

Car car = new Car(new MockDataAccess()); car.Save();

if (car.Id != 1) {

//something went wrong }

}

Có khác nho nhỏ, thêm vào IdataAccess trực tiếp từ phương pháp SAVE hay thiết lập private _dataAccess thơng qua thuộc tính nội

Framework

Doing DI manually works great in simple cases, but can become unruly in more complex situations A recent project I worked on had a number of core components that needed to be injected – one for caching, one for logging, one for a database acce ss and another for a web service Classes got polluted with multiple constructor overloads and too much thought had to go into setting up classes for unit testing Since DI is so critical to unit testing, and most unit testers love their open-source tools, it should come as no surprise that a number of frameworks exist to help automate DI The rest of this chapter will focus on StructureMap, an open source Dependency Injection framework created by fellow CodeBetter blogger Jeremy Miller ( h t t p : / / s t r u c tu r e m a p s ou r c e f o r g e n e t / )

Trước sử dụng StructureMap bạn phải cấu hình sử dụng file XML (được gọi

(27)

<StructureMap> <DefaultInstance

PluginType="CodeBetter.Foundations.IDataAccess, CodeBetter.Foundations" PluggedType="CodeBetter.Foundations.SqlDataAccess, CodeBetter.Foundations"/> </StructureMap>

Trong không muốn dành nhiều thời gian để nói configuration, có điều quan trọng cần lưu ý XML file phải sử dụng thư mục bin ứng dụng bạn Bạn tự động để VS.NET làm việc cách chọn file, mục properties thiết lập Copy to Output Directory thuộc tính Copy Always ( Có loạt option Advanced configuraion Nếu bạn thấy hứng thú học hỏi hơn, đề nghị bạn vào StructureMap website)

Một configurate, bạn undo tất thay đổi thực lớp Car phép việc thêm vào trỏ (gỡ bỏ the_dataProvider tất trỏ) Để có việc thêm vào IDataAccess xác, cần yêu cầu StrutureMap, phương pháp Save trông này::

public class Car {

private int _id; public void Save() {

if (!IsValid()) {

//todo: come up with a better exception

throw new InvalidOperationException("The car must be in a valid state");

}

IDataAccess dataAccess = ObjectFactory.GetInstance<IDataAccess>(); if (_id == 0)

{

_id = dataAccess.Save(this); }

else {

dataAccess.Update(this); }

(28)

Để sử dụng mock việc thêm vào chuẩn, cần thêm mock vào StructureMap:

public void AlmostATest() {

ObjectFactory.InjectStub(typeof(IDataAccess), new MockDataAccess()); Car car = new Car();

car.Save(); if (car.Id != 1) {

//something went wrong }

ObjectFactory.ResetDefaults(); }

Chúng ta sử dụng InjectStub lệnh gọi sau tới GetInstance trả mock, chắn reset tất thứ trở bình thường thơng qua ResetDefaults

DI frameworks StructureMap dễ sử dụng hữu ích Với cặp lệnh configuration vài thay đổi nho nhỏ đoạn code chúng ta, giảm đáng kể việc bắt cặp tăng khả kiểm tra Trước đây, giới thiệu StructureMap với luợng lớn codebase, nhiên tầm ảnh hưởng khơng đáng kể

Sự cải tiến cuối cùng

Bằng việc giới thiệu lớp IDataAccess việc sử dụng DI Framework, giải việc gỡ bỏ nhiều việc bắt cặp dở đoạn code ví dụ Chúng ta thực tiếp vài bước nữa, chí tới điểm mà mang lại hại nhiều lợi Tuy nhiên, có phụ thuộc mà tơi muốn tránh - object bussiness thường khơng biết việc thêm vào DI Thay gọi StructureMap's ObjectFactory trực tiếp, thêm vào nhiều cấp độ gián tiếp:

public static class DataFactory {

public static IDataAccess CreateInstance {

get { } }

(29)

return ObjectFactory.GetInstance<IDataAccess>();

(30)

Tổng kết chương

(31)

CHƯƠNG 5 Unit Testing

Chúng ta không chọn unit testing khơng cho lợi ích

- (RANDOM CONSULTANCY COMPANY)

Trong suốt sách này, nói tầm quan trọng khả kiểm tra(testability ) xem qua nhiều kĩ thuật để làm đơn giản hóa việc kiểm tra hệ thống Khơng cần phải nói lợi ích lớn việc viết test cho hệ thống khả mang tới sản phẩm tốt cho khách hàng điều cho unit testing, lý mà viết unit test khơng có tới gần tới việc cải thiện khả quản lý hệ thống nhiều việc phét triển suite cho unit test, Bạn thường nghe unit testing hỗ trợ speak unit test - thực bao gồm Dựa dự án mà làm, tiếp tục thực thay đổi đẻ cải thiện hệ thống ( thay đổi chức năng, performance, refactoring) Nếu hệ thống lớn, yêu cầu tạo thay đổi làm phát hoảng Liệu làm được? Liệu tạo hiệu ứng dội? Những lỗi đưa ra? Nếu khơng có unit test, từ chối tạo thay đổi cao Nhưng biết rằng, khách hàng biết, thay đổi với nguy cao mang lại thành cơng lớn Nếu có 100 unit test chạy vịng vài phút, tách rời component, xếp code xây dựng tính mà không nghĩ đến năm trước, khơng cần phải lo lắng gí Bởi tự tin với hoàn chỉnh unit test, biết chúng khơng đưa lỗi

Kiểm tra đơn vị không làm nhẹ bớt thay dổi rủi ro cao Trong đời lập trình, tơi nhiều lần xử lý lỗi quan trọng gây nên thay đổi mang rủi ro thấp Một điểm cần tạo thay đổi cho hệ thống chúng ta, click chuột phải lên solution, chọn “Run Test” phút biết đâu Tôi đơn giản nhấn mạnh kiểm tra đơn vị quan trọng Chắc chắn chúng hữu ích để tìm kiếm lỗi kiểm tra code tơi có nên hay không, quan trọng khả ma thuật để phát lỗi nghiêm trọng thiết kế hệ thống Tơi phấn khích lướt ngang phương thức hay hành vi mà khó để kiểm tra Nghĩa tơi tìm thấy thiếu sót phần hệ thống Một cách tương tự, đặt test vào cặp giây cho điều mà tơi chắn khó, tơi biết số người team viết code có lẽ sử dụng lại từ dự án khác

Tại không dùng Unit Test năm trước?

Với người tìm thấy tiện lợi unit testing, thật khó hiểu người lại khơng sử dụng Với người chưa chấp nhận nó, bạn chắn muốn chấm dứt nói điều Trong nhiều năm tơi đọc blog nói chuyện với đồng nghiệp làm việc unit test, chưa tự tay thực hành lần

(32)

1 Tơi có quan niệm sai mục đích unit testing Như tơi nói, unit testing cải thiện chất lượng hệ thống, thật làm dễ dàng để chuyển bảo trì hệ thống sau Hơn nữa, bạn tiến đến bước adopt Test Driven Development (TDD), unit testing thật trở thành phần thiết kế Theo diễn giải Scott Bellware, TDD testing vè bạn suy nghĩ tester thực TDD mà bạn suy nghĩ designer

2 Giống nhiều người, thường nghĩ người phát triển phần mềm khơng nên viết phần test Tôi câu chuyện đằng sau tin tưởng này, nghĩ cớ lập trình viên Testing trình tìm lỗi hệ thống xác thực hệ thống làm việc mong đợi Có lẽ nhà phát triển phần mềm không đươc giỏi việc tìm lỗi code họ, họ người thích hợp để chắn hệ thống làm việc theo hướng mà họ mong muốn người sử dụng người thích hợp để kiểm tra việc nên làm.Thậm chí unit testing khơng thật testing nữa, người phát triển phần mềm mà không tin họ nên test code thật người khơng có trách nhiệm

3 Testing chẳng có vui vẻ Ngồi trước hình, nhập liệu chắn thứ ok Nhưng unit testing lại coding, có nghĩa có nhiều trở ngại để thành công Đôi khi, coding, testing bình thường, nhìn chung chẳng khác việc lập trình bạn làm ngày

4 Nó tốn thời gian Vài người nói unit testing khơng tốn thời gian, mà tiết kiệm thời gian cho bạn Đó thật thời gian bạn bỏ để viết unit test phần nhỏ so với thời gian bạn giành để đổi yêu cầu chỉnh lỗi Thành thật mà nói, unit testing tốn nhiều thời gian (đặc biệt bạn bắt đầu) Bạn khơng có đủ thời gian cho unit test khách hang bạn cảm thấy khơng thoả mãn Trong trường hợp này, khuyên bạn xác định phần code then chốt test nó, chí có phải trải qua để viết unit test có ảnh hưởng lớn

Rốt cuộc, unit testing trông giống vật phức tạp huyền bí, sử dụng trường hợp cần thiết Những lợi ích xem đạt tới đươc thời gian dường khơng cho phép cho Nó phải luyện tập nhiều (ti có thời gian khó khăn để học unit test cách để dung nó), lợi ích thấy trưc tiếp

The Tools

Với StructureMap giới thiệu chương trước, cần phải thêm frameworks tool vào unit testing framework: nUnit, RhinoMocks

TestDriven.NET

TestDriven.NET add-on Visual Studio, thêm sẵn tuỳ chọn “Run Test” vào menu ngữ cảnh (context menu) không tốn thời gian để nói Personal License TestDriven.NET có hiệu lực cho mã nguồn mở người dung thử Tuy nhiên, không cần phải lo lằng license khơng thích hợp cho bạn, nUnit có cơng cụ chạy test riêng nó, khơng tích hợp vào VS.NET (Resharper - người dung sử dụng chức built-in nó)

(33)

RhinoMocks mocking framework sử dụng Trong phần trước, tạo mock cách thủ cơng – vừa có nhiều giới hạn, vừa tốn thời gian RhinoMocks tự động tạo lớp mock từ giao diện cho phép thẩm tra điều khiển tương tác với

nUnit

Điều phải làm thêm liên kết vào nunit.framework.dll va Rhino.Mocks.dll Theo sở thích riêng đặt unit tests vào phận riêng chúng Ví dụ, lớp domain tơi đặt CodeBetter.Foundations, tạo phận gọi CodeBetter.Foundations.Tests Nó có nghĩa khơng test phương thức private Trong NET 2.0+, sử dụng

InternalsVisibleToAttribute phép phép phận Test truy cập vào phương thức bên (mở Properties/AssemblyInfo.cs thêm

[assembly:InternalsVisibleTo(“CodeBetter.Foundations.Test”)]

vài thứ mà tơi phải làm.)

Có việc bạn cần phải biết nUnit Đầu tiên, bạn điều chỉnh việc test băng cách sử dụng thuộc tính TestFixtureAttribute áp dụng vào class chứa việc kiểm tra bạn, thiết lập teardown phương thức SetupAttribute áp dụng vào phương thức mà bạn muốn thực trước lần test - bạn không thường xuyên cần chúng Tương tự, TearDownAttribute áp dụng vào phương thức bạn muốn thực thi sau test Cuối cùng, TestAttribute áp dụng vào unit test bạn (cịn có thuộc tính khác, thuộc tính quan trọng nhất) Nó trơng giống như:

using NUnit.Framework; [TestFixture]

public class CarTests {

[SetUp]

public void SetUp() { //todo } [TearDown]

public void TearDown(){ //todo } [Test]

public void SaveThrowsExceptionWhenInvalid(){ //todo } [Test]

public void SaveCallsDataAccessAndSetsId(){ //todo } //more tests

}

Chú ý unit test có tên rõ ràng - quan trọng để phát biểu xác việc test làm gì, test bạn không nên làm nhiều việc, bạn có tên dài

Điều thứ hai cần phải biết nUnit bạn xác thực việc kiểm tra bạn thực thi mong muốn thông qua việc sử dụng lớp Assert nhiều phương thức Tơi biết điều khơng thoả đáng, có phương thức

nhận đối số int[] trả tổng nó, unit test trông sau:

[Test]

public void MathUtilityReturnsZeroWhenNoParameters() {

Assert.AreEqual(0, MathUtility.Add()); }

(34)

public void MathUtilityReturnsValueWhenPassedOneValue() {

Assert.AreEqual(10, MathUtility.Add(10)); }

[Test]

public void MathUtilityReturnsValueWhenPassedMultipleValues() {

Assert.AreEqual(29, MathUtility.Add(10,2,17)); }

[Test]

public void MathUtilityWrapsOnOverflow() {

Assert.AreEqual(-2, MathUtility.Add(int.MaxValue, int.MaxValue)); }

Bạn biết chúng từ ví dụ trên, lớp Assert có nhiều hàm,như là: Assert.IsFalse, Assert.IsTrue, Assert.IsNull, Assert.IsNotNull,

Assert.AreSame, Assert.AreNotEqual, Assert.Greater, Assert.IsInstanceOfType,

Unit Test gì?

Unit tests phương thức dùng để kiểm tra cách hoạt động múc chi tiết Người phát triển với việc unit test thường để phạm vi kiểm tra họ tăng lớn Hầu hết unit test cho phép thành phần giống nhau: thực thi vài code từ hệ thống bạn xác nhận hoạt động mong muốn Mục đích unit test để kiểm chứng hoạt động đặc biệt Nếu viết test cho phương thức Save lớp Car, không viết test chứa hết tất mà viết test cho tác vụ mà chứa - thất bại đối tượng trạng thái invalid, gọi đến phương thức Save lớp Data Access , thiết lập ID gọi phương thức Update lớp Data Access Một điều quan trọng unit test lỗi Tơi vài người số bạn thấy tests dung để cover phương thức MathUtility.Add dư thừa Bạn nghĩ tests nhóm lại thành – trường hợp nhỏ này, tơi nói thứ bạn thích Tuy nhiên, tơi bắt đầu sử dụng unit test, tơi rơi vào thói quen xấu để phạm vi test lớn Test tạo đối tượng, thực thi vài thành phần xác nhận chức Nhưng thường nói, tơi cịn đây, tơi qua cách tốt đẹp việc xác nhận thêm để field thiết đặt theo cách mà chúng phải làm Nó nguy hiểm vài thay đổi code bạn phá vỡ hang loạt test khơng lien quan với - dứt khoát dấu hiệu mà bạn đưa cho test bạn phải tập trung phạm vi nhỏ

Nó mang quay lại với việc kiểm tra với phương thức private Nếu bạn google, bạn tìm thấy nhiều thảo luận chủ đề này, trí chung bạn không nên kiểm tra phương thức private Tôi nghĩ lý thuyết phục để không test phương thức private mục đích để kiểm tra phương thức hay dòng code, mà để kiểm tra cách hoạt động Đó điều mà bạn phải ln ln nhớ Nếu bạn test code bạn thông qua public interface, phương thức private tự động test Một lý lẽ khác chống lại việc kiểm tra phương thức private phá vỡ tính đóng gói Chúng ta nói quan việc che giấu thông tin Các phương tức private chứa chi tiết thực mà muốn thay đổi mà khơng cần phá vỡ việc gọi code Nếu kiểm tra phương thức private cách trực tiếp, thay đổi thực phá hỏng test chúng ta, mà điều không báo trước tốt cho khả bảo trì cao

(35)

Để bắt đầu, ý kiến hay để kiểm tra chức đơn giản Trước lâu, bạn muốn kiểm tra phương thức mà có phụ thuộc vào thành phần bên - chẳng hạn database Ví dụ, bạn muốn hồn chỉnh việc kiểm tra lớp Car cách kiểm tra phương thức Save Vì muốn giữ cho test chi tiết tốt (và nhỏ tốt- test cần nên chạy nhanh thể thực thi chúng thường xuyên lấy feedback lập tức) thực không muốn tính tốn cách để tạo kiểm tra database mà làm giả liệu giữ trạng thái ban đầu qua test Theo tinh thần đó, tất điều muốn làm chắn phương thức Save tương tác với DAL Sau dùng unit test để kiểm DAL Nếu phương thức Save DAL hoạt động mong đợi chúng tương tác với nhau, có sở tốt để chuyển sang kiểm tra khác Trong chương trước, thấy bắt đầu việc kiểm tra với mock Chúng ta tạo lớp mock cách thủ cơng, mà điều có vài giới hạn lớn Sự khác biệt lớn khơng thể xác thực việc gọi đến đối tượng mock xuất mong muốn Đó thật điều mà RhinoMock muốn giải Sử dụng RhinoMock đơn giản, cần cho mà bạn muốn mock (giao diện lớp - thường giao diện) phương thức bạn muốn gọi, theo với thơng số, thực thi kiểm tra thoả mãn mong muốn bạn chưa

Trước bắt đầu, cần cho RhinoMock truy cập vào kiểu nội Nó thực nhanh chóng cách thêm

[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

vào file Properties/AssemblyInfo.cs

Giờ bắt đầu code cách viết test để cover nhánh update phương thức Save:

[TestFixture]

public class CarTest {

[Test]

public void SaveCarCallsUpdateWhenAlreadyExistingCar() {

MockRepository mocks = new MockRepository();

IDataAccess dataAccess = mocks.CreateMock<IDataAccess>(); ObjectFactory.InjectStub(typeof(IDataAccess), dataAccess); Car car = new Car();

dataAccess.Update(car); mocks.ReplayAll(); car.Id = 32; car.Save();

mocks.VerifyAll();

ObjectFactory.ResetDefaults(); }

}

Mỗi đối tượng mock tạo ra, thực dịng code, xen vào dependency injecttion framework (trong trường hợp

(36)

bởi RhinoMocks (khơng có thật xày dataAccess đơn giản đối tượng mock mà khơng có thực thật sự) Chúng ta thoát khỏi record-mode cách gọi ReplayAll, có nghĩa sẵn sàng để thực thi code thật kiểm tra ngược lại với tiến trình định sẵn Khi gọi VerifyAll sau gọi Save đối tượng Car, RhinoMocks đảm bảo việc gọi thật diễn giống mong đợi Nói cách khác, bạn nghĩ thứ trước ReplayAll trạng thái mong muốn, thứ sau mà code kiểm tra thật với VerifyAll thực kiểm tra cuối

Chúng ta kiểm tra tất cách buộc cho sai (chú ý việc gọi dataAccess.Update):

[Test]

public void SaveCarCallsUpdateWhenAlreadyExistingCar() {

MockRepository mocks = new MockRepository();

IDataAccess dataAccess = mocks.CreateMock<IDataAccess>(); ObjectFactory.InjectStub(typeof(IDataAccess), dataAccess); Car car = new Car();

dataAccess.Update(car); dataAccess.Update(car); mocks.ReplayAll(); car.Id = 32; car.Save();

mocks.VerifyAll();

ObjectFactory.ResetDefaults(); }

Việc kiểm tra cúng ta thất bại với tin nhắn từ RhinoMocks báo có việc gọi đến Update mong đợi, có thật diễn

Cho hành động Save, tương tác phức tạp - phải đảm bảo giá trị trả sử lý phương thức Save Dưới test:

[Test]

public void SaveCarCallsSaveWhenNew() {

MockRepository mocks = new MockRepository();

IDataAccess dataAccess = mocks.CreateMock<IDataAccess>(); ObjectFactory.InjectStub(typeof(IDataAccess), dataAccess); Car car = new Car();

Expect.Call(dataAccess.Save(car)).Return(389); mocks.ReplayAll();

car.Save();

mocks.VerifyAll();

Assert.AreEqual(389, car.Id); ObjectFactory.ResetDefaults(); }

(37)

Nếu thay đổi hàm Save để đưa ngoại lệ id trả không hợp lệ, test trông này:

[TestFixture]

public class CarTest {

private MockRepository _mocks; private IDataAccess _dataAccess; [SetUp]

public void SetUp() {

_mocks = new MockRepository();

_dataAccess = _mocks.CreateMock<IDataAccess>();

ObjectFactory.InjectStub(typeof(IDataAccess), _dataAccess); }

[TearDown]

public void TearDown() {

_mocks.VerifyAll(); }

[Test, ExpectedException("CodeBetter.Foundations.PersistenceException")] public void SaveCarCallsSaveWhenNew()

{ Car car = new Car();

Expect.Call(_dataAccess.Save(car)).Return(0); _mocks.ReplayAll();

car.Save(); }

}

Để cho bạn thấy cách kiểm tra ngoại lệ (thơng qua thuộc tính ExpectedException), trích đoạn code tạo, thiết lập xác nhận đối tượng mock lặp lại vào phương thức Setup TearDown

More on nUnit and RhinoMocks

Cho đến chúng tơi nhìn tính cung cấp nUnit RhinoMocks, có nhiều việc mà thực làm với chúng Ví dụ,

RhinoMocks thiết lập để bỏ qua thứ tự gọi các phương thức, nhanh chóng mocks nhiều replay/xác minh cụ thể, mock số phương thức khác lớp (partial mock)

Kết hợp với tiện ích NCover, bạn nhận báo cáo khả bao phủ kiểm tra Khả bao phủ cho bạn biết tỷ lệ phần trăm assembly/namespace/class/phương thức thực test bạn NCover có trình duyệt mã trực quan, đánh dấu dịng mã khơng thực thi màu đỏ Nói chung, tơi khơng thích dùng mức độ bao phủ phương tiện để đo tính hồn chỉnh unit test Sau hết, bạn thực thi dịng code khơng có nghĩa bạn thực kiểm tra Những tơi làm NCover cho để làm bật code mà khơng bao phủ Nói cách khác, dịng mã phương pháp thực test, khơng có nghĩa việc bạn kiểm tra tốt Nhưng dòng mã phương pháp không thực thi, sau bạn cần phải xem xét để thêm số test

(38)

của bạn Trong TDD viết Save test trước có chức phương thức Save Tất nhiên, test thất bại Sau chúng viết hành vi cụ thể kiểm tra lại Các thần chung cho nhà phát triển red - green → refactor Có nghĩa bước có unit test thất bại, sau làm cho pass, sau đến Refactor code yêu cầu

Theo kinh nghiệm tôi, TDD tốt với Domain Driven Design, thực cho phép tập trung vào nguyên tắc hoạt động hệ thống Nếu khách hàng nói việc theo dõi phụ thuộc nâng cấp major pain-point cho họ (If our client says tracking dependencies between upgrades has been a major pain-point for them), đặt đường đắn với test xác định hành vi API tính cụ thể Tôi khuyên bạn nên tự làm quen với unit test, nói chung trước áp dụng TDD

UI and Database Testing

Việc dùng Unit Test với trang ASP.NET thật không xứng đáng với công sức bỏ ASP.NET framework phức tạp bị khớp nối chặt chẽ Thường xuyên bạn không yêu cầu HTTPContext thực tế, mà đòi hỏi nhiều công việc để thiết lập Nếu bạn sử dụng nặng nề tuỳ chỉnh HttpHandlers, bạn nên test chúng giống lớp khác (dĩ nhiên tuỳ thuộc vào bạn làm) Mặt khác, kiểm tra Data Access Layer có thể, tơi muốn recommend Có thể có phương pháp tốt hơn, phương pháp tiếp cận tơi trì tất CREATE Tables / CREATE Sprocs text files với dự án tôi, tạo sở liệu thử nghiệm on the fly, sử dụng phương thức Setup Teardown để giữ cho sở liệu trạng thái biết Chủ đề có giá trị đăng blog tương lai, bây giờ, tơi để tuỳ vào sáng tạo bạn

Trong chương này

(39)

CHƯƠNG Object Relational Mappers

THE OTHER OPTION INVOLVED WRITING WAY TOO MUCH SQL - CHRIS KOCH

Trong chương tìm hiểu hàn gắn liệu đối tượng giới cách viêt tay lớp data access mapper Hướng tiếp cận hạn chế yêu cầu nhiều mã lặp lặp lại (mặc dù hữu ích chứng minh vấn đề bản) Thêm nhiều đối tượng chức bloat DAL vào trạng thái vi phạm khơng thể bảo trì DRY (don't repeat yourself) Trong chương này, lấy O / R Mapping framework thực tế để làm tất thứ nặng nhọc cho Đặc biệt, nhìn qua framework mã nguồn mở phổ biến NHibernate Các hàng rào ngăn cản lớn người dân từ việc áp dụng tên miền hướng thiết kế vấn đề persistence Việc áp dụng riêng O / R mappers đến với rung chuyển nghi ngờ lớn Bạn yêu cầu trao đổi kiến thức bạn phương pháp thử cho huyền diệu Một bước nhảy vọt đức tin yêu cầu

Việc nói đến O / R mapper tạo SQL cho bạn Tơi biết, nghe chậm, khơng an tồn thiếu thuyết phục, đặc biệt bạn cấu hình để sử dụng inline SQL Nhưng bạn đẩy nỗi sợ khỏi tâm trí bạn, bạn phải thừa nhận tiết kiệm nhiều thời gian kết lỗi Hãy nhớ rằng, muốn tập trung vào xây dựng hành vi, khơng phải lo lắng plumbing (và làm cho bạn cảm thấy tốt hơn, O / R mapper tốt cung cấp cách đơn giản để bạn phá vỡ hệ mã tự động thực thi SQL riêng bạn thủ tục lưu trữ) Infamous Inline SQL vs Stored Procedure Debate

Trong năm qua, có số tranh luận Inline SQL thủ tục lưu trữ Các tranh luận khơng sơi lắm, người ta nghe thấy Inline SQL, họ nghĩ code không tốt kiểu như:

string sql = @"SELECT UserId FROM Users

WHERE UserName = '" + userName + "' AND Password = '" + password + "'"; using (SqlCommand command = new SqlCommand(sql)) {

return 0; //todo }

Tất nhiên, diễn đạt theo cách này, Inline SQL thực suck Tuy nhiên, bạn dừng lại suy nghĩ thực so sánh apples to apples, thật khơng tốt khác Hãy kiểm tra số luận điểm chung

Stored Procedures are More Secure

Inline SQL cần viết cách sử dụng truy vấn tham số giống bạn làm với thủ tục lưu giữ Ví dụ, cách xác viết mã để loại trừ khả xảy công SQL injection là:

string sql = @"SELECT UserId FROM Users

WHERE UserName = @UserName AND Password = @Password"; using (SqlCommand command = new SqlCommand(sql))

{

(40)

}

Từ trở đi, khơng có nhiều khác biệt - view sử dụng database role / người sử dụng thiết lập với điều khoản thích hợp Thủ tục lưu giữ cung cấp trừu tượng cho underlying schema Dù bạn có sử dụng Inline SQL hay thủ tục lưu giữ, chút trưu tượng bạn đặt câu lệnh SELECT giống Nếu có thay đổi thực hiện, thủ tục lưu trữ bạn bị phá vỡ có hội tốt bạn cần để thay đổi code gọi đến để đối phó với vấn đề Về chất, code, đơn giản nằm nơi khác nhau, khơng thể cung cấp trừu tượng O/R mappers lại mặt khác, nhìn chung cung cấp trừu tượng tốt khả cấu hình thực ngơn ngữ truy vấn riêng

Nếu tạo thay đổi, biên dịch lại code Ở vài nơi, cách đó, người ta ln nghĩ cần phải tránh việc biên dịch code giá (có thể đến từ thời gian mà project cần tốn ngày để biên dịch) Nếu bạn thay đổi thủ tục lưu trữ, bạn cần chạy lại unit integration test bạn triển khai thay đổi vào sản phẩm Nó thật đáng sợ làm bối rối nhà phát triển cho thay đổi thủ tục lưu trữ hay XML tầm thường so sánh thay đỏi tương tự code

Stored Procedure làm giảm lưu lượng mạng Có quan tâm? Trong nhiều trường hợp, sở liệu bạn đặt kết nối GigE với server bạn bạn trả cho bandwidth Bạn thật tốn có vài nano giây Hơn nữa, cấu hình tốt O/R mapper tiết kiệm hành trình thơng qua thực thực identify map, caching lazy loading

Thủ tục lưu trữ nhanh hơn

Đó lời biện hộ giữ thời gian lâu Viết câu lệnh SQL thông thường sau viết thứ thủ tục lưu trữ tính thời gian chúng Trong hầu hết trường hợp, có khơng có khác biệt Trong vài trường hợp, thủ tục lưu trữ lại chậm kế hoạch cached execution plan khơng hiệu thơng số Jeff Atwood gọi việc sử dụng thủ tục lưu trữ mục đích nâng cao hiệu suất “trường hợp khó khăn phương pháp tối ưu chưa hồn thiện” Ông ta Hướng đắn chọn hướng đơn giản (để cơng cụ tạo SQL cho bạn) , tối ưu truy vấn đặc trưng khi/nếu tượng thắt cổ chai

Nó tốn thời gian, sau khoảng năm, nhận tranh cãi Inline SQL thủ tục lưu trữ thật tầm thường tranh cãi C# VB.NET Nếu vấn đề hay kia, chọn mà bạn thích bước tiếp vào thử thách Nếu khơng cịn có thêm chủ đề này, chọn thủ tục lưu trữ Tuy nhiên, thêm O/R mapper

vào trong đống đó, bạn nhận thấy thuận lợi đặc trưng Bạn kết thúc lửa đấu tranh ngu ngốc đơn giản nói rằng:"I want that"

Một cách cụ thể, có lợi ích quan trọng có với O/R mapper:

1 Bạn viết code - điều thật có kết nhiều hệ thống có khả bảo trì

(41)

3 Code bạn trở nên đơn giản - impedance mismatch bạn thấp, bạn viết mã lặp lại Nếu impedance mismatch cao bạn kết hợp thiết kế database với thiết kế domain - bạn xây dựng hai theo cách tối ưu để O/R mapper quản lý mismatch

Cuối cùng, thật xuống để xây dựng giải pháp đơn giản Sự tối ưu hố có thẻ bỏ sau bạn sơ lược code bạn xác định bottleneck thật Giống hầu hết thứ, khơng nên thấy đơn giản thật phức tạp để học, thực tế nghề nghiệp

NHibernate

Trong framework tool mà nói qua, NHibernate phức tạp Sự phức tạp thứ dĩ nhiên bạn kể đến định giải pháp bền bỉ, bạn thấy dự án cho phép vài thời gian R&D, đáng giá tương lai Điều tốt NHibernate, mục đích thiết kế framework hồn tồn sáng - domain object bạn khơng buộc phải thừa kế lớp đặc trưng bạn khơng phải sử dụng hàng đống thuộc tính trang trí Nó làm cho unit test lớp dơmain khả thi - bạn sử dụng kỹ thuật bền bỉ khác, kiểu dataset, móc nối chặt chẽ domain data tạo cho khó/khơng thể để unit test cách xác Ở mức độ cao, bạn cấu hình NHibernate cách cho cách mà database ánh xạ vào domain object, sừ dụng NHibernate API NHibernate Query Language để nói cho database bạn để thực công việc cấp thấp ADO.NET SQL Đây không cung cấp tách biệt cấu trúc bảng domain object mà tách rời code với thực database đặc trưng

Trong chương trước, tập trung vào hệ thống cho đại lý phân phối xe - chi tiết tập trung vào xe nâng cấp Trong chương này, thay đổi nhìn tí xem qua việc bán xe (sales, models Sales people) Domain model đơn giản - SalesPeson có khơng nhiều Sales, mà Sales liên kết với Model cụ thể

Cũng kể đến giải pháp VS.NET, bao gồm code mẫu giải Tất bạn cần để làm cho chạy tạo database mới, thực thi đoạn mã SQL cung cấp cấu hình cho chuỗi kết nối Các ví dụ, với phần cịn lại chương này, cách để giúp bạ bắt đầu với NHibernate - chủ đề hay bị lờ Cuối cùng, bạn tìm thấy NHibernate reference manual có chất lượng tốt, đây tool hữu ích để bắt đầu tham khảo để tìm kiếm chủ đề cụ thể Cũng có sách xuất Manning, NHibernate in Action, xuất vào tháng

Configuration

Bí mật linh hoạt đáng ngạc nhiên NHibernate nằm khả cấu hình Ban đầu làm nản lịng để thiết lập nó, sau khoảng dự án trở nên tự nhiên Bước cấu hình NHibernate Việc cấu hình đơn giản nhất, phải thêm vào app.config hay web.config, là:

<configuration> <configSections>

<section name="hibernate-configuration"

type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" /> </configSections>

(42)

<property name="hibernate.dialect"> NHibernate.Dialect.MsSql2005Dialect </property>

<property name="hibernate.connection.provider"> NHibernate.Connection.DriverConnectionProvider </property>

<property name="hibernate.connection.connection_string">

Server=SERVER;Initial Catalog=DB;User Id=USER;Password=PASSWORD; </property>

<mapping assembly="CodeBetter.Foundations" /> </session-factory>

</hibernate-configuration> </configuration>

Trong bốn giá trị, phương ngữ thú vị Nó nói cho NHibernate ngơn ngữ cụ thể mà database sử dụng Nếu, code chúng ta, yêu cầu NHibernate trả trang kết Cars phương ngữ thiết lập SQL Server 2005,

NHibernate phát câu SQL SELECT sử dụng hàm ROW_NUMBER() Tuy nhiên, phương ngữ thiết lập MySQL, NHibernate phát câu SELECT với LIMIT Trong hầu hết trường hợp, thiết lập lần sau quên đi, cung cấp vài cách nhìn vào khả cung cấp lớp mà tạo tất code để truy cập liệu

Trong việc cấu hình, cho NHibernate file ánh xạ nằm đâu phận CodeBetter.Foundations File ánh xạ nhứng file XML, nói cho NHibernate cách lớp tiếp tục tồn Với thông tin này, NHibernate có khả trả đối tượng Car bạn yêu cầu lưu Quy tắc phổ biến có file ánh xạ cho domain object, đặt chúng bên thư mục Mapping File ánh xạ cho đối tượng Model, đặt tên Model.hbm.xml:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="CodeBetter.Foundations"

namespace="CodeBetter.Foundations">

<class name="Model" table="Models" lazy="true" proxy="Model">

<id name="Id" column="Id" type="int" access="field.lowercase-underscore"> <generator class="native" />

</id>

<property name="Name" column="Name"

type="string" not-null="true" length="64" /> <property name="Description" column="Description" type="string" not-null="true" />

<property name="Price" column="Price" type="double" not-null="true" /> </class>

</hibernate-mapping>

(nó quan trọng phải đảm bảo Build Action cho tất file ánh xạ set đến Embedded Resources) File nói cho NHibernate lớp Model ánh xạ đến hàng bảng Model, thuộc tính Id, Name, Description Price ánh xạ đến cột Id, Name, Description Price Những thông tin thêm xung quanh thuộc tính Id giá trị tạo database (trái ngược với NHibernate (chẳng hạn giải pháp clustered) hay giải thuật riêng chúng ta) khơng có setter, nên truy cập field với quy tắc đặt tên cụ thể (chúng ta cung cấp Id tên lowercase-underscore quy tắc đặt tên, sử dụng field đặt tên _id) Với file ánh xạ thiết lập, bắt đầu tương tác với database:

private static ISessionFactory _sessionFactory; public void Sample()

{

(43)

Model model = new Model(); model.Name = "Hummbee";

model.Description = "Great handling, built-in GPS to always find your way back home, Hummbee2Hummbe(tm) communication"; model.Price = 50000.00;

ISession session = _sessionFactory.OpenSession(); session.Save(model);

//Let's discount the x149 model

IQuery query = session.CreateQuery("from Model model where model.Name = ?"); Model model = query.SetString(0, "X149").UniqueResult<Model>();

model.Price -= 5000;

ISession session = _sessionFactory.OpenSession(); session.Update(model);

}

Ví dụ thật đơn giản để persist đối tượng vào database, khôi phục chúng cập nhật chúng - tất chẳng cần đến ADO.NET hay SQL Bạn ngạc nhiên, đối tượng _sessionFactory đến từ đâu, xác ISession

_sessionFactory đối tượng thread-safe có tầm vực tồn hệ thống, tạo ứng dụng bắt đầu Bạn thường cần đối tượng cho database mà ứng dụng bạn sử dụng (có nghĩa bạ thường cần cái), cơng việc nó, giống hầu hết factory, tạo đối tượng preconfigure: ISession ISession không giống ADO.NET, ánh xạ cách lỏng lẻo đến kết nối database Tuy nhiên, việc tạo ISession không cần thiết để mở kết nối Thay vào đó, ISession khơn khéo quản lý kết nối làm chủ đối tượng cho bạn Không giống kết nối nên mở sau đóng trước, bạn khơng cần phải lo lắng việc ISession lưu lại thời gian (mặc dù khơng phải thread-safe)

Nếu bạn xây dựng ứng dụng ASP.NET, bạn mở cách an toàn ISession BeginRequest đóng EndRequest (hay tốt hết sử dụng lazyload để trường hợp cụ thể không yêu cầu ISession) ITransaction phần khác vấn đề, tạo việc gọi BeginTransaction ISession Nó thường cho nhà phát triển NET bỏ qua cần thiết cho giao dịch ứng dụng họ Nó thật khơng may dẫn đến trạng thái dễ đổ vỡ chí khơng thể khôi phục liệu Một ITransaction sử dụng để đảm bảm hoạt động unit - theo dõi thay đổi, thêm hay xố đi, cấu hình cách để cập nhật vào database, cung cấp khả rollback có thất bại

Relationships

Trong hệ thống chúng ta, quan trọng kiểm sốt sales - đặc biệt với salespeople, có cung cấp vài báo cáo Chúng ta nói sale chi thuộc salesperson, thiết lập mối quan hệ 1-nhiều - có nghĩa salesperson có nhiều sales, sales thuộc

salesperson Trong database chúng ta, mối quan hệ thể hiển cột SalesPersonId bảng Sales (khố ngoại) Trong domain, lớp SalesPerson có tập hợp Sales lớp Sales có thuộc tính SalesPerson (tham chiếu) Cả kết thúc mối quan hệ cần để thiết lập file ánh xạ tương ứng Trong kết thúc Sales, ánh xạ thuộc tính đơn, sử dụng yếu tố thuộc tính gọi many-to-one:

(44)

not-null="true"/>

Chúng ta định tên thuộc tính, kiểu/lớp, tên cột khố ngoại Chúng ta ràng buộc mở rộng, thêm đối tượng Sales mới, thuộc tính SalesPerson null

Mặt khác mối quan hệ, tập hợp Sales mà SalesPerson có đặt tên phức tạp thuật ngữ NHibernate chuẩn NET Để thiết lập tập hợp, sử dụng set, list, map, bag mảng phần tử Xu hướng chung sử dụng list, NHibernate yêu caauf bạn có cột rõ mục Nói cách khác, NHibernate team thấy list tập hợp mà mục phần quan trọng mà cần phải xác định rõ Cái mà hầu hết nhà phát triển NET nghĩ đến list NHibernate gọi bag Khá rắc rối dù bạn sử dụng list hay bag element, kiểu domain phải môt IList (hay tương đương với IList<T>) Đó NET khơng có đối tượng IBag Một cách ngắn gọn, để việc sử dụng tập hợp ngày bạn, bạn sử dụng bag element tạo kiểu thuộc tính IList

Một tuỳ chọn khác tập hợp set Set tập hợp mà khơng có chứa trùng lặp - ngữ cảnh chung cho ứng dụng kinh doanh (mặc dù xác định rõ ràng) Kì quặc là, NET lại khơng có tập hợp kiểu set, NHibernate sử dụng giao diện

Iesi.Collection.ISet Có thực cụ thể: ListSet, thật nhanh cho tập hợp nhỏ (10 10 phần tử); SortedSet, xếp; HashSet, nhanh cho tập hợp lớn; HybridSet, khởi tạo sử dụng ListSet tự động chuyển đổi sang HashSet tập hợp bạn tăng lên

Cho hệ thống chúng ta, sử dụng bag (thậm chí khơng có Sales trùng lặp, cịn dễ hiểu lúc này), khai báo tập hợp Sales IList:

private IList<Sale> _sales; public IList<Sale> Sales {

get { return _sales;} }

Và thêm phần tử <bag> vào file ánh xạ SalesPerson:

<bag name="Sales" access="field.lowercase-underscore" table="Sales" inverse="true"

cascade="all">

<key column="SalesPersonId" /> <one-to-many class="Sale" /> </bag>

Nhắc lại, bạn nhìn vào phần tử/ thuộc tính, thật khơng q phức tạp Chúng ta định tên thuộc tính, xác định quy tắc truy cập (chúng ta khơng có setter, cho sử dụng field với quy ước đặt tên chúng ta), bảng cột giữ khoá ngoại, kiểu/class thành phần tập hợp Chúng ta thuộc tính Cascade cho tất cả, có nghĩa gọi Update đối tượng SalesPerson, thay đổi tạo cho tập hợp Sales (thêm vào, bỏ đi, thay đổi vào Sales có sẵn) tự động chỉnh theo Cascade giúp tiết kiệm thời gian thật hữu hiều mà hệ thống bạn tăng phức tạp lên

(45)

NHibernate hỗ trợ hướng truy vấn khác nhau: Hibernate Query Language (HQL) ang Criteria Queries (bạn truy vấn thật SQL, khả di chuyển làm thế) HQL đơn giản giống với SQL - sử dụng : from, where, aggregates, order by, group by, Tuy nhiên, thay truy vấn bảng bạn viết truy vấn domain -có nghĩa HQL hỗ trợ nguyên lý hướng đối tượng, giống thừa kế đa hình Các phương thức truy vấn mức trừu tượng cao SQL, có nghĩa bạn di chuyển đươc - tất bạn cần làm để thực database khác thay đổi cấu hình phương ngữ

HQL bỏ giao diện IQuery, tạo cách gọi CreateQuery phiên làm việc bạn Với IQuery bạn trả thực thể độc lập, tập hợp, tham số thay nhiều Đây vài ví dụ:

string lastName = "allen";

ISession session = _sessionFactory.OpenSession(); //retrieve a salesperson by last name

IQuery query = s.CreateQuery("from SalesPerson p where p.LastName = 'allen'");

SalesPerson p = query.UniqueResult<SalesPerson>();

//same as above but in line, and with the last name as a variable SalesPerson p = session.CreateQuery("from SalesPerson p where p.LastName = ?").SetString(0, lastName).UniqueResult<SalesPerson>();

//people with few sales

IList<SalesPerson> slackers = session.CreateQuery("from SalesPerson person where size(person.Sales) < 5").List<SalesPerson>();

Nó phần hồn hảo với HQL (các ví dụ download phức tạp hơn)

Set lựa chọn thú vị khác Set tập hợp mà không chứa phần tử giống – trường hợp thường thấy cho ứng dụng doanh nghiệp (tuy nhiên trường hợp thường ngầm hiểu) Tuy nhiên, NET không hỗ trợ set, Nhibernate sử dụng

Iesi.Collection.Iset interface Bốn cách thực là: ListSet phù hợp với tập hợp nhỏ

(ít 10 phần tử) xử lý nhanh, SortedSet xếp được, HashSet phù hợp với tập hợp lớn, HybridSet : lúc đầu ListSet sau chuyển sang HashSet khi kích thước tập hợp tăng

Đối với trường hợp này, sử dụng bag (mặc dù khơng thể có sale

giống sử …) vì tạo tập hợp dang Ilist:

private IList<Sale> _sales; public IList<Sale> Sales {

get { return _sales;} }

Và thêm phần tử <bag> vào file ánh xạ

SalesPerson

<bag name="Sales" access="field.lowercase-underscore"

table="Sales" inverse="true" cascade="all">

<key column="SalesPersonId" /> <one-to-many class="Sale" /> </bag>

Khi NET 3.5 đời, tập HashSet cuối thêm vào

(46)(47)

Một lần nữa, bạn xem xét phần tử/ thuộc tính, chúng khơng q phức tạp thấy Chúng ta xác định thuộc tính …

Querying

Nhibernate hỗ trợ kiếu truy vấn khác nhau: Hibernate Query Language (HQL) Criteria Queries ( bạn sử dụng SQL tính khả chuyển làm vậy) HQL dường dễ cú pháp gần giống SQL – bạn dùng from,where,

aggregates, order by, group by v.v Tuy nhiên, thay truy xuất với bảng bạn truy xuất domain - điều có nghĩa HQL hỗ trợ mơ hình hướng đối tượng thừa kế đa hình Các phương pháp truy vấn trừu tượng hóa SQL có nghĩa tính khả chuyển bảo toàn – việc bạn cần làm truy vấn CSDL khác thay đổi cú pháp

HQL bỏ Iquery interface tạo gọi hàm CreateQuery Với IQuery bạn trả thực thể riêng biệt, tập hợp, tham số phụ … Đây ví dụ:

string lastName = "allen";

ISession session = _sessionFactory.OpenSession(); //retrieve a salesperson by last name

IQuery query = s.CreateQuery("from SalesPerson p where p.LastName = 'allen'");

SalesPerson p = query.UniqueResult<SalesPerson>();

//same as above but in line, and with the last name as a variable

SalesPerson p = session.CreateQuery("from SalesPerson p where p.LastName = ?").SetString(0, lastName).UniqueResult<SalesPerson>();

//people with few sales

IList<SalesPerson> slackers = session.CreateQuery("from SalesPerson person where size(person.Sales) < 5").List<SalesPerson>();

Đây phần nhỏ HQL (các ví dụ mẫu phần download có ví dụ phức tạp hơn)

Lazy Loading

Khi nạp nhân viên bán hàng dòng lệnh sau:

SalesPerson person = session.Get<SalesPerson>(1)

Tập hợp Sales không nạp Đó mặc định tập hợp nạp cách thụ động Điều có nghĩa không truy cập CSDL chỉ thông tin cần truy cập ( vd: truy cập thuộc tính Sales) Chúng ta sửa cách đặt lazy=”false”.

Một cách khác thú vị để thực thụ động nạp thực thể Chúng ta thường muốn thêm tham khảo vào đối tượng mà nạp đối tượng từ CSDL Ví dụ như, thêm Sales vào SalesPerson, phải Model không muốn nạp tất thuộc tính – muốn lấy giá trị Id để lưu vào cột ModelId bảng Sales Khi gọi hàm

(48)

CSDL bạn yêu cầu Điều giúp ta viết đoạn mã sau mà khơng cần phải truy cập để nạp tồn Model:

Sale sale = new Sale(session.Load<Model>(1), DateTime.Now, 46000.00); salesPerson.AddSales(sale);

Download

Bạn tải project với cách sử dụng Nhibernate

http://codebetter.com/files/folders/codebetter_downloads/entry172562.aspx

Trong chương này

Chúng ta tìm hiểu qua cách sử dụng Nhibernate mà khơng tìm hiểu Criteria Queries (là truy xuất API gắn chặt với domain), khả cache, phân loại tập hợp, tối ưu hiệu năng, log hay khả SQL Hi vọng bạn khơng tìm hiểu

(49)

CHƯƠNG 7 Trở lại bản: Bộ nhớ

Dù cố gắng, ngơn ngữ lập trình đại khơng thể hồn tồn che dấu khái niệm hệ thống máy tính chứng exception khác xuất ngôn ngữ cấp cao Ví dụ như, bạn bắt gặp NET exception sau : NullReferenceException, OutOfMemoryException, SteckOverflowException

ThreadAbortException Sẽ quan trọng lập trình viên để hiểu cấu trúc kĩ lập trình cấp cao, nhiên lập trình viên cần phải hiểu hệ sinh thái mà chương trình họ chạy Nhìn vào lớp cung cấp trình biên dịch C# hay VB.NET, CLR hệ điều hành, thấy nhớ Mọi chương trình sử dụng nhớ hệ thống giao tiếp với nhiều cách, khó để trở thành người lập trình tốt khơng hiểu giao tiếp

Các nhập nhằng nhớ xuất phát từ việc C# VB.NET ngôn ngữ chỉnh sửa CLR cung cấp chế dọn rác Điều tạo cho lập trình viên hiểu nhầm họ khơng cần lo lắng nhớ

Cấp phát nhớ:

Ở NET, đa số ngôn ngữ, biến bạn định nghĩa lưu stack heap Hai không gian lưu trữ tồn riêng biệt nhớ hệ thống phục vụ mục đích khác Mục đích xác định trước: kiểu giá trị nằm stack kiểu tham khảo nằm heap Nói cách khác, kiểu

char,int,long,byte, enum cấu trúc (được định nghĩa NET hay người lập trình) nằm stack Chỉ có trường hợp ngoại lệ kiểu giá trị thuộc kiểu tham khảo – ví dụ thuộc tính ID class User nằm heap với thực thể User

Stack

Mặc dù quen với việc xử lí rác tự động, giá trị stack xử lí tự động mơi trường lập trình khơng có chế xử lí rác (như C) Điều xảy thêm vào giá trị (như phương thức hay câu lệnh if) giá trị đẩy vào đầu stack thoát giá trị stack lấy Đây lí mà stack LIFO Khi chương trình chạy đến khối phương thức, trỏ đánh dấu đầu stack giá trị thêm vào cần Khi bạn thoát khỏi khối đó, giá trị lấy với trỏ đánh dấu Việc xảy dù có khối lồng vào

Trừ bạn xem xét việc giao tiếp heap stack, vấn đề bạn gặp với stack StackOverflowException Nó có nghĩa bạn sử dụng hết khơng gian dùng có stack 99.9% trường hợp lời gọi recursive khơng có điểm dừng Trên lý thuyết, tràn stack xảy hệ thống thiết kế tồi chưa thấy lời gọi khơng recursive mà dùng hết không gian stack

Heap

(50)

nhau), tạo khối nhớ trả trỏ tới vị trí cấp phát Ví dụ đơn giản 1chuỗi: kí tự chuỗi chiếm byte tạo chuỗi với giá trị “Hello World” CLR cần phải cấp phát 22 byte (11x2) cộng thêm phần overhead

Chuỗi tạo thay đổi – nghĩa bạn khởi tạo chuỗi gán giá trị, bạn chỉnh sửa chuổi (bằng cách thay đổi giá trị hay nối chuỗi khác vào nó) chuỗi tạo Việc dẫn đến giảm hiệu ví cách tốt sử dụng StringBuilder cho việc xử lí chuỗi Sự thật đối tượng lưu heap khơng thể chỉnh sửa kích thước thay đổi kích thước cần phải có cấp phát StringBuilder với vài tập hợp khác giảm thiểu phần cách sử dụng buffer Khi buffer đầy, cấp phát tương tự xảy số thuật toán tăng trưởng dùng để xác định kích thước (đơn giản oldSize*2) Khi nên xác định giá trị ban đầu đối tượng để tránh việc cấp phát lại (constructor StringBuilder ArrayList (cũng collection khác) cho bạn xác định kích thước ban đầu)

Dọn rác heap việc không tầm thường Không stack ta cần loại khối cuối khỏi stack, đối tượng heap không nằm cục khối cụ thể Thay vào đó, đa số tham khảo lồng sâu đối tượng tham khảo khác Trong ngơn ngữ C, người lập trình cấp phát vùng nhớ, anh/cô ta phải chắn xóa khỏi heap kết thúc sử dụng Trong ngơn ngữ chỉnh sửa, mơi trường runtime chịu trách nhiệm dọn dẹp tài nguyên (.NET sử dụng Generational Garbage Collector – tìm hiểu thêm Wikipedia)

Lập trình viên gặp nhiều vấn đề phức tạp làm việc với heap Thất nhớ xảy thường xun, phân mảnh nhớ gây nhiều tác hại vấn đề hiệu suất xuất cấp phát khơng quản lí

Con trỏ

Đối với nhiều lập trình viên, học trỏ trải nghiệm đau lòng Chúng thể giao tiếp thật phần mã phần cứng Nhiều lập trình viên chưa tìm hiểu chúng – cách dụng ngôn ngữ không diễn đạt chúng cách trực tiếp Người ta cho C# hay Java ngơn ngữ khơng có trỏ sai lầm Con trỏ chế mà ngơn ngữ dùng để quản lí giá trị heap, thật ngờ nghệch khơng tìm hiểu cách hoạt động chúng

Con trỏ diễn tả mối quan hệ mơ hình nhớ hệ thống – đó, trỏ chế để stack heap làm việc với nhằm cung cấp hệ thống nhớ yêu cầu chương trình Như nói trên, khởi tạo đối tượng với new, NET cấp phát vùng nhớ heap trả trỏ đến đầu vùng nhớ Con trỏ đơn giản là: đĩa bắt đầu khối nhớ chứa đối tượng Địa khơng có khác số biểu diễn hệ thập lục phần Vì vậy, trỏ khơng số nói cho NET nơi lưu trữ đối tượng nhớ Khi bạn gán tham khảo cho biến, biến bạn thật trỏ tới đối tượng Sự giao tiếp che Java hay NET C hay C++ nơi mà bạn điều khiển địa nhớ cách trực tiếp thông qua phép toán trỏ Trong C hay C++ bạn lấy trỏ thêm vào đó, cách bạn tùy ý thay đổi vị trí mà trỏ trỏ tới (và dẫn đến crash nó)

(51)

tham khảo Điều chưa rõ ràng có nghĩa đối tượng nằm heap có gốc nằm stack (có thể thơng qua nhiều tầng tham khảo) Chúng ta xem xét ví dụ sau :

static void Main(string[] args) {

int x = 5;

string y = "codebetter.com"; }

Từ đoạn mã có giá trị stack, số nguyên trỏ tới chuỗi chuỗi nằm heap Đây hình minh họa

Khi thoát khỏi hàm main, giá trị địa phương loại khỏi stack, có nghĩa giá trị x y bị Điều đáng kể vùng nhớ heap chứa chuỗi tất tham khảo đến (khơng có trỏ đến nó) Trong C hay C++, điều dẫn đến thất thoát nhớ - khơng có tham khảo đến địa vùng heap khơng thể giải phóng nhớ Trong C# hay Java, trình dọn rác tìm đối tượng khơng tham khảo giải phóng

Chúng ta tìm hiểu đoạn mã sau, ngồi việc có nhiều tham khảo tương tự đoạn mã

public class Employee {

private int _employeeId; private Employee _manager; public int EmployeeId {

get { return _employeeId; } set { _employeeId = value; } }

public Employee Manager {

get { return _manager; } set { _manager = value; } }

public Employee(int employeeId) {

_employeeId = employeeId; }

}

public class Test {

private Employee _subordinate; void DoSomething()

(52)

Employee boss = new Employee(1); _subordinate = new Employee(2); _subordinate.Manager = _boss; }

}

Thú vị thay, kết thúc phương thức, biến boss không đưa khỏi stack biến subordinate định nghĩa lớp ngồi khơng Điều có nghĩa trình dọn rác khơng có để làm giá trị hreap tham khảo (1 trực tiếp từ stack, gián tiếp từ stack thông qua đối tượng tham khảo)

Như bạn thấy, trỏ chắn đóng vai trò quan trọng C# VB.NET Mặc dù phép tốn trỏ khơng có ngơn ngữ ,con trỏ đơn giản dễ hiểu

Mơ hình nhớ thực tế

Chúng ta tìm hiểu ảnh hưởng thực tế chúng chương trình Dù hiểu biết mơ hình nhớ khơng giúp tránh lỗi giúp viết chương trình tốt

Đóng hộp (Boxing)

Boxing xảy giá trị (được lưu stack) buộc phải lưu heap Unboxing xảy giá trị lưu lại stack Cách đơn giản để buộc giá trị kiểu nguyên lưu heap cách cast

Int x=5; Object y=x;

1 trường hợp thường thấy boxing bạn truyền giá trị cho phương thức có tham số đối tượng Trường hợp thường gặp NET 1.x trước có đời generic Những class non-generic làm việc hiệu với kiểu đối tượng đoạn mã sau cho kết boxing unboxing

ArrayList userIds = new ArrayList(2); userIds.Add(1);

userIds.Add(2);;

int firstId = (int)userIds[0];

(53)

hay khơng boxing ví dụ cách mà nhớ hệ thống ảnh hưởng đến chương trình bạn

ByRef

Nếu không hiểu rõ trỏ, bạn hiểu truyền giá trị truyền tham khảo Các nhà phát triển nói chung hiểu phải truyền giá trị tham khảo người hiểu phải truyền tham khảo tham khảo ByRef ByVal ảnh hưởng đến kiểu tham khảo kiểu giá trị Sử dụng BeRef trường hợp thường gặp NET không tự động giải mối quan hệ trỏ (truyền tham khảo hay trả dạng tham khảo không tồn Java)

Trước hết chung ta tìm hiểu ByVal/Byref ảnh hưởng đến kiểu giá trị:

public static void Main() {

int counter1 = 0; SeedCounter(counter1); Console.WriteLine(counter1);

int counter2 = 0;

SeedCounter(ref counter2); Console.WriteLine(counter2); }

private static void SeedCounter(int counter) {

counter = 1; }

private static void SeedCounter(ref int counter) {

counter = 1; }

Chúng ta nghĩ kết Lời gọi hàm khơng truyền counter1 tham khảo có nghĩa counter1 truyền cho SeedCounter thay đổi xảy cục hàm Nói cách khác, lấy giá trị stack chép vào vị trí khác stack

Trong trường hợp thứ 2, thực truyền giá trị tham khảo có nghĩa khơng có tạo thay đổi không cục hàm SeedCounter Cách hoạt động kiểu tham khảo hoàn toàn tương tự lúc đầu khác Chúng ta xem xét ví dụ sau: Ví dụ sử dụng class PayManagement để thay đổi thuộc tính Employee Trong đoạn mã sau, thấy nhân viên trường hợp nâng lương $2000 Sự khác chỗ người truyền tham khảo truyền theo giá trị

public class Employee {

private int _salary; public int Salary {

get {return _salary;} set {_salary = value;} }

public Employee(int startingSalary) {

_salary = startingSalary; }

(54)

public class PayManagement {

public static void GiveRaise(Employee employee, int raise) {

employee.Salary += raise; }

public static void GiveRaise(ref Employee employee, int raise) {

employee.Salary += raise; }

}

public static void Main() {

Employee employee1 = new Employee(10000); PayManagement.GiveRaise(employee1, 2000); Console.WriteLine(employee1.Salary);

Employee employee2 = new Employee(10000);

PayManagement.GiveRaise(ref employee2, 2000); Console.WriteLine(employee2.Salary);

}

Trong trường hợp, kết xuất 12000 Trước hết, khác với điều mà ta thấy với kiểu giá trị Điều xảy truyền kiểu tham khảo kiểu giá trị truyền giá trị mà khơng phải giá trị heap Thay vào đó, truyền trỏ Và trỏ trỏ đến vị trí heap nên thay đổi diễn trỏ thấy ngược lại Khi truyền tham khảo theo kiểu tham khảo, thật truyền trỏ trường hợp trỏ Điều làm xuất câu hỏi, cần truyền tham khảo tham khảo ? Lí để làm bạn muốn chỉnh sửa trỏ - chỉnh sửa nơi mà trỏ tới Điều dẫn đến số tác dụng phụ không mong muốn Chúng ta xem xét ví dụ sau:

public class Employee {

private int _salary; public int Salary {

get {return _salary;} set {_salary = value;} }

public Employee(int startingSalary) {

_salary = startingSalary; }

}

public class PayManagement {

public static void Terminate(Employee employee) {

employee = null; }

public static void Terminate(ref Employee employee) {

employee = null; }

(55)

public static void Main() {

Employee employee1 = new Employee(10000); PayManagement.Terminate(employee1); Console.WriteLine(employee1.Salary);

Employee employee2 = new Employee(10000); PayManagement.Terminate(ref employee2); Console.WriteLine(employee2.Salary); }

Cố tìm hiểu điều gỉ xảy Tôi cho bạn gợi ý exception xuất Nếu bạn đoán lời gọi employee1.Salary xuất 10000 kết thứ trả NullReferenceException bạn Trong trường hợp chép giá trị trỏ Null – khơng ảnh hưởng đến mà employee1 trỏ tới Trong trường hợp 2, không không truyền mà giá trị stack dùng employee2 Vì xác lập employee thành null tương đương với viết employee2 = null

Việc thay đổi địa trỏ trỏ biến phương thức khác khơng thường gặp – lí bạn thấy việc truyền tham khảo tham khảo bạn muốn trả nhiều giá trị từ lời gọi hàm ( trường hợp tốt sử dụng thơng số out cách hồn tồn hướng đối tượng) Ví dụ nguy hiểm làm việc môi trường mà bạn không hiểu rõ cách hoạt động

Quản lý thất nhớ

Chúng ta thấy ví dụ thất thoát nhớ C Đối với C# khơng có trình dọn rác đoạn mã sau gây thất thoát:

private void DoSomething() {

string name = "dune"; }

Giá trị stack (1 trỏ) lấy khỏi cách để tham khảo đến vùng nhớ chứa chuỗi đồng thời khơng có cách để giải phóng Đây khơng phải vấn đề NET có trình dọn rác để tìm kiếm vùng nhớ không tham khảo giải phóng Tuy nhiên loại thất nhớ xảy bạn sử dụng tham khảo vô tận Đây lỗi thường gặp phần mềm lớn với tham khảo lồng Chúng khó để phát vùng thất nhỏ chương trình bạn chạy khơng đủ lâu để phát Khi chương trình bạn kết thúc OS lấy lại toàn bộ nhớ dù có bị thất hay khơng Tuy nhiên, bạn thấy OutOfMemoryException không làm việc với liệu lớn, bạn gặp thất nhớ .NET có cung cấp cơng cụ để giúp bạn nhiên bạn dùng chương trình thương mại dotTrace hay ANTS Profiler Để tìm thất nhớ bạn cần tìm đối tượng bị thất (chúng dễ tìm cần bạn chụp hình nhớ bạn lần sau so sánh chúng) duyệt qua tất đối tượng cịn tham khảo tới sau sửa lỗi

(56)

(iDisposable giải pháp hoàn hảo) sử dụng WeakEvent Pattern hay phiên đơn giản

Phân mảnh

1 lí thường gây OutOfMemoryException phân mảnh nhớ Khi nhớ cấp phát heap, khối liên tục Điều có nghĩa vùng nhớ lại phải quét để tìm khối nhớ đủ lớn Khi chương trình chạy, vùng heap trở nên phân mảnh (như ổ cứng bạn) bạn gặp trường hợp nhiều khoảng trống nắm rải rác sử dụng Bình thường trình dọn rác tối ưu hóa nhớ cách giải phóng nhớ Khi tối ưu hóa nhớ, địa đối tượng thay đổi NET cập nhật tham khảo để phù hợp Đôi khi, NET dời đối tượng: đối tượng gắn chặt vào vùng nhớ định

Kết dính nhớ (Pinning)

Kết dính xảy đối tượng gắn chặt vào vùng nhớ xác định heap Vùng nhớ kết dính khơng thể tối ưu trình dọn rác dấn đến phân mảnh Tại giá trị lại bị kết dính ? Lí thường gặp đoạn mã bạn có tương tác với mã khơng quản lí Khi trình dọn rác tối ưu vùng heap, cập nhật tất tham khảo đoạn mã quản lý nhiên khơng thể xử lí đoạn mã khơng quản lý Vì trước biên dịch phải gắn đối tượng vào nhớ Vì có nhiều phương thức NET phụ thuộc vào mã khơng quản lý, kết dính xảy bạn khơng biết (ví dụ tơi thường gặp NET socket class phụ thuộc vào cách thực đoạn mã không quản lý buffer)

Một cách xử lí tạo đối tượng lớn chúng khơng gây phân mảnh đối tượng nhỏ (điều xác đối tượng đặt vùng nhớ heap đặc biệt (Large Object Heap) mà không tối ưu(compact)) Ví dụ, thay tạo hàng trăm buffer 4KB, bạn tạo buffer lớn tự gán khối Ví dụ thơng tin kết dính bạn đọc viết Gred Young kết dính socket khơng đồng

Có lí thứ mà đối tượng phải kết dính – bạn bắt phải Trong C# (khơng phải VB.NET) bạn biên dịch assembly với tùy chọn unsafe, bạn kết dính đối tượng thơng qua từ khóa fixed Mặc dù kết dính nhiều dẫn đến áp lực lên hệ thống, nhiên việc sử dụng câu lệnh fixed cách chuẩn xác làm tăng hiệu suất Vì ? Vì đối tượng kết dính điều chỉnh trực tiếp phép tốn trỏ - điều khơng thể đối tượng khơng kết dính trình dọn rác tái cấp phát đối tượng bạn nơi khác nhớ

Ví dụ sau chuyển từ chuỗi ASCII sang số nguyên chạy nhanh gấp lần hàm int.Parse

public unsafe static int Parse(string stringToConvert) {

int value = 0;

int length = stringToConvert.Length; fixed(char* characters = stringToConvert) {

for (int i = 0; i < length; ++i) {

value = 10 * value + (characters[i] - 48); }

}

return value; }

(57)

gì xảy ra) khơng có nhiều chức int.Parse nói chung nguy hiểm lợi ích

Xác lập giá trị null

Vậy, bạn có nên gán kiểu tham khảo thành null bạn không cịn làm việc với chúng ? Dĩ nhiên khơng Khi biến khỏi khối, đẩy khỏi stack tham khảo bị xóa Nếu bạn khơng thể chờ để kết thúc khối bạn nên viết lại mã mình, Kết thúc tiền định

Mặc dù có trình dọn rác nhà lập trình phải quản lý tham khảo Đó vài đối tượng giữ tài nguyên quan trọng hay giới hạn file kết nối CSDL Chúng phải giải phóng sớm tốt Điều vấn đề khơng biết lúc trình dọn rác chạy – thực tế trình dọn rác chạy nhớ cịn Đổi lại, class chứa tài nguyên nên sử dụng Disposable Tất nhà lập trình NET quen với khái niệm với thực cụ thể (Idisposable interface)vì khơng cần tìm hiểu lại thứ mà biết Trong chương này, bạn cần hiểu vai trị kết thúc tiền định Nó khơng giải phóng nhớ sử dụng đối tượng Nó giải phóng tài nguyên Trong trường hợp kết nối CSDL, giải phóng kết nối để sử dụng lại

Nếu bạn quên gọi hàm Dispose đối tượng thực Idisposable, trình dọn rác làm việc cho bạn Tuy nhiên bạn khơng thể tin tưởng vấn đề hạn chế tài nguyên có thật (nó tầm thường bạn thử dùng dịng lặp để mở liên kết tới CSDL) Bạn thắc mặc số đối tượng thực phương thức Close Dispose bạn nên gọi Trong trường hợp thấy chúng tương đương – chọn bạn Tôi đề nghị bạn sử dụng câu lệnh using quên Close Một cách riêng tư cảm thấy bực bội (và không quán) dùng Cuối bạn tạo class dùng kết thúc tiền định bạn thấy thực IDIsposable đơn giản Có hướng dẫn rõ MSDN

Trong chương này

(58)

CHƯƠNG 8 Trở lại bản: Exception Thất bại nhanh chóng – Jim Shore

Exception cấu trúc mạnh mà lập trình viên quản lí không chuẩn bị đầy đủ làm việc với Điều đáng tiếc exception thật đóng vai trị quan trọng việc làm cho hệ thống lập trình viên chạy tốt Trong chương tìm hiểu khái niệm : xử lý, tạo throw exception Vì exception khơng thể tránh bạn khơng thể chạy trốn

Xử lý exception

Chiến lược xử lí exception gồm nguyên tắc vàng: 1.Chỉ xử lí exception mà bạn thật xử lý

2 Và bạn khơng thể làm việc số lượng ngoại lệ vốn có rất nhiều Nhiều nhà lập trình viên làm điều hoàn toàn trái ngược với nguyên tắc thứ chiến đấu vô vọng với nguyên tắc thứ hai Khi chương trình bạn hoạt động khơng bình thường điều tốt cần phải làm loại bỏ lúc Nếu bạn không làm vậy, bạn không thơng tin quan trọng lỗi mà bạn cịn đặt chương trình bạn vào trạng thái khơng xác định, điều gây hậu nghiêm trọng

Bất bạn sử dụng câu lệnh try/catch, tự hỏi bạn làm tạo exception Nếu sở liệu bạn bị tắt, bạn viết mã để phục hồi hay khơng hay bạn hiển thị thông báo lỗi tới người dùng nhận thơng báo lỗi Trước tiên, điều khó chấp nhận đơi tốt để chương trình bị crash ghi lại lỗi sau tiếp tục Ngay hệ thống quan trọng, bạn sử dụng CSDL bạn làm bị hỏng? Lối suy nghĩ không giới hạn CSDL mà lỗi bình thường Nếu bạn chuyển giá trị cấu hình thành số nguyên tạo FormatException điều có ý nghĩa không bạn tiếp tục chuyện xảy ra? Chắc chắn khơng

Dĩ nhiên bạn giải exception bạn chắn nên làm – chắn bắt exception mà bạn giải Bắt exception khơng xử lí chúng gọi nuốt exception đoạn mã xấu Một ví dụ thường thấy kiểm tra giá trị đầu vào Ví dụ sau xem xét giá trị categoryId chuyển từ QueryString trang ASP.NET

int categoryId; try

{

categoryId = int.Parse(Request.QueryString["categoryId"]); }

catch(Exception) {

categoryId = 1; }

(59)

int categoryId; try

{

categoryId = int.Parse(Request.QueryString["categoryId"]) }

catch(FormatException) {

categoryId = -1; }

(Một cách tiếp cận tốt dùng với hàm int.TryParse dùng NET 2.0 – đặc biệt cần xem xét in.Parse ném loại ngoại lệ khác mà muốn điểu khiển theo cách, vấn đề khác)

Logging

Mặc dù hầu hết ngoại lệ không điều khiển, bạn nên ghi chép (log) lại đố tượng loại Một cách lý tưởng, bạn tập trung tất thông tin ghi chét – loại kiện OnError kiểu HttpModule lựa chọn tốt cho ứng dụng ASP.NET web service Tôi thường thấy nhà phát triển nắm bắt ngoại lệ chúng xuất để ghi lại ném Điều gây đoạn code lặp dư thừa – tốt ngoại lệ lên xuyên suốt code bạn ghi lại tất phần rìa ngồi hệ thống Có thể bạn muốn thông báo email ngoại lệ xuất hiện, xem lại ngày thơng qua trình khác để gửi tới bảng tổng kết ngày bạn Rất nhiều nhà phát triển tận dụng khn mẫu logging sẵn có log4net Microsoft’s Logging Application Block.

(Một vài từ cảnh báo dựa kinh nghiệm cá nhân : vài kiểu ngoại lệ có khuynh hướng bó lại thành cụm Nếu bạn chọn việc gửi email có ngoại lệ xuất bạn nhanh chóng cho hộp mail bị tràn Một giải pháp logging không ngoan thực vài dạng đệm (buffering) kết hợp (aggregation)

Cleaning (làm sạch)

Trong chương trước nói sự hoàn thành tất định ứng với chất làm việc “lười biếng” chương trình thu dọn rác Ngoại lệ tăng thêm phức tạp chất rời rạc làm cho việc gọi phương thức Dispose khơng diễn Xét lời gọi database theo cách cổ điển :

SqlConnection connection = new SqlConnection(FROM_CONFIGURATION) SqlCommand command = new SqlCommand("SomeSQL", connection); connection.Open();

command.ExecuteNonQuery(); command.Dispose();

connection.Dispose();

Nếu phương thức ExecuteNonQuery ném ngoại lệ, câu lệnh kết nối bị bỏ Mộ giải pháp dùng thêm phát biểu Try/Finally:

SqlConnection connection; SqlCommand command; try

{

connection = new SqlConnection(FROM_CONFIGURATION) command = new SqlCommand("SomeSQL", connection); connection.Open();

(60)

finally {

if (command != null) { command.Dispose(); } if (connection != null) { connection.Dispose(); } }

hoặc tốt mặt cú pháp dùng phát biểu using (phát biểu được biên dịch thành dạng try/finally trên):

using (SqlConnection connection = new SqlConnection(FROM_CONFIGURATION)) using (SqlCommand command = new SqlCommand("SomeSQL", connection)) {

connection.Open();

command.ExecuteNonQuery(); }

Điều quan trọng là, bạn điều khiển ngoại lệ, bạn tập trung tất việc ghi chép (logging), bạn cần phải quan tâm tới vị trí mà ngoại lệ xuất hiện, đặc biệt với lớp thực Idisposable

Ném ngoại lệ

Khơng có quy tắc ma thuật việc ném ngoại lệ đới việc bắt (nhắc lại, việc bắt ngồi lệ, quy tắc khơng bắt ngoại lệ trừ bạn thực điều khiển nó) Tuy nhiên, ném ngoại lệ, ngoại lệ có riêng bạn hay khơng (điều nói tới sau), cơng việc tương đối đơn giản, Đầu tiên nhìn chế thực việc ném ngoại lệ, chủ yếu dựa vào câu lệnh throw Sau xem xét bạn thực muốn ném ngoại lệ

Cơ chế ném ngoại lệ

Bạn ném ngoại lệ mới, ném lại ngoại lệ bắt Để ném ngoại lệ mới, đơn giản tạo ném

create a new exception and throw it

throw new Exception("something bad happened!"); //or

Exception ex = new Exception("something bad happened"); throw ex;

Tác giả thêm ví dụ hai vài nhà phát triển nghĩ ngoại lệ mà trường hợp đặc biệt, trường hợp riêng – thật chúng giống đối tượng khác (ngoại trừ thừa kế từ System.Exception, lớp thừa kế từ System.Object) Thật ra, bạn tạo ngoại lệ khơng có nghĩa bạn ném nó, bạn thực làm Thỉnh thoảng bạn cần ném lại ngoại lệ, bạn khơng thể giải ngoại lệ, bạn cần thực thi đoạn code ngoại lệ xuất Ví dụ phổ biến quay ngược lại giao dịch thất bại:

ITransaction transaction = null; try

{

transaction = session.BeginTransaction(); // some work

transaction.Commit(); }

catch {

(61)

throw; }

finally {

//cleanup }

Trong ví dụ trên, câu lệnh throw làm cho câu lệnh catch trở nên suốt. Đó là, câu lệnh điều khiển ngoại lệ phía chuỗi thực thi khơng có dẫn bắt ngoại lệ Trong hầu hết trường hợp, điều muốn – quay ngược loại giao dịch thực không giúp đỡ khác điều khiển ngoại lệ Tuy nhiên, có cách để ném lại ngoại lệ, ngoại lệ xuất bên đoạn code:

catch (HibernateException ex) {

if (transaction != null) { transaction.Rollback(); } throw ex;

}

Bằng cách ném ngoại lệ cách tường minh, vết ngăn xếp điều chỉnh để từ dịng lệnh ném lại ngoại lệ xuất mã nguồn Đây ý tưởng tồi, giống bị thơng tin sống cịn Vì cẩn thận với việc ném lại ngoại lệ - nhận khác biệt đòi hỏi tinh tế quan trọng Nếu bạn gặp phải tình thế, mà bạn ném lại ngoại lệ với phần điều khiển mã nguồn, cách làm tốt dùng ngoại lệ lồng nhau:

catch (HibernateException ex) {

if (transaction != null) { transaction.Rollback(); } throw new Exception("Email already in use", ex); }

Với cách này, vết ngăn xếp ban đầu truy cập thơng qua thuộc tính InnerException sẵn có cho loại ngoại lệ

Khi phải ném ngoại lệ

Một điều quan trọng biết cần ném ngoại lệ Một chủ đề thú vị nhiều ta nên ném chúng Làm cho đoạn code “bất trị” gậy hại cho ứng dụng bạn yếu tố Viết đoạn code riêng bạn để làm lại điều hoàn toàn ngốc ngếch Tuy nhiên, nhà phát triển giỏi khơng ngại việc sử dụng ngoại lệ cách sáng suốt

Thực có hai mức đọc suy nghĩ xem ngoại lệ nên dùng Ở mức độ thức nhất, chấp nhận rộng rãi, bạn khơng nên lưỡng lự bắt lấy ngoại lệ tình ngoại lệ thực xuất Ví dụ ưa thích người viết việc phân tích cú pháp (parse) file cấu hình Nhiều nhà phát triển sử dụng cách hào phóng giá trị mặc định cho mục không hợp lệ Điều khơng có vấn đề vài trường hợp, có đặt hệ thống vào tình trạng khơng đáng tin cậy trạng thái khơng mong đợi Một ví dụ khác ứng dụng Facebook trả kết không mong đợi từ lời gọi hàm API Bạn bỏ qua lỗi này, giải ngoại lệ này, ghi chép lại (để từ sửa lỗi, API thay đổi) thể thơng điệp hữu ích cho người dùng

(62)

(design by contract) – phương pháp luận mà người viết sử dụng lúc nhiếu Về mặt chất, phương thức SaveUser lưu lại người dùng, nên ném ngoại lệ

Trong ngôn ngữ C#, VB.NET Java, với việc không hỗ trợ chế thiết kế theo thỏa thuận, cách tiếp cận đưa kết lẫn lộn Một Hashtable (bảng băm) trả null khóa khơng tìm thấy, Dictionary ném ngoại lệ - cách hành xử không mong đợi xuất (nếu độc giả thấy tị mị chúng hoạt động khác kiểm tra blog Brad Abrams) Cúng có sợi dây liên hệ yếu tố hợp thành dòng điều khiển xem ngoại lệ Ngoại lệ khơng nên dùng để để điều khiển vấn đề logic kiểu nếu/thì, mà cho cơng việc lớn hơn, chẳng hạn thư viên, giống cách mà hầu hết lậptrình viên sử dụng chúng (phương thức int.Parse ví dụ tốt vấn đề này)

Nói chung, người viết nhận thấy dễ dàng xác định nên khơng nên ném ngoại lệ Tác giả thường tự đặt câu hỏi, ví dụ :

 Điều có thực ngoại lệ  Điều có trơng đợi

 Tơi tiếp tực điều có ý nghĩa điểm

 Có điều tơi nên đặt trạng thái lưu ý để tơi sửa chữa, xem lại lần thứ hai

Có lẽ điều quan trọng ném ngoại lệ, hay giải ngoại lệ nói chung, nghĩ người dùng Đa số người dùng “ngây thơ” đem so sánh họ với lập trình viên, dễ dàng trỡ nên lo lắng gặp thông điệp báo lỗi Jeff Atwood gần có viết blog tầm quan trọng việc chuẩn bị cho trường hợp chương trình có khả giải có hợp lý bị crash

-

NGƯỜI DÙNG KHÔNG BUỘC PHẢI NÓI RÕ VỚI BẠN VỀ MỌI LỖI CỦA PHẦN MỀM

ĐỪNG CHO NGƯỜI DÙNG THẤY NHỮNG MÀN HÌNH MẶC ĐỊNH ĐANG CHẾT ĐỨNG

CĨ MỘT GHI CHÉP CÔNG KHAI, CHI TIẾT VỀ NHỮNG LỖI CỦA ỨNG DỤNG

-Có vẻ an tồn nói hình Windows lên màu xanh việc chương trình chết đứng thơng điệp báo lỗi không nên đưa cho người dùng (và đừng nghĩ trở ngại bị làm chậm lại nhiều lười biếng) Tạo ngoại lệ riêng

(63)

thông tin đặc tả cho nó, điều mà khơng giúp cho bạn xác định lỗi tiềm ẩn, mà dùng để thể thơng tin có ý nghĩa cho người dùng

Nhiều ví dụ ngoại lệ mà tác giả tạo khơng có ý nghĩa ngoại lệ làm dấu, có nghĩa là, mở rộng sở lớp System.Exception, không cung cấp cách thực xa Tác giả ví von điều với giao diện làm dấu (hay thuộc tính làm dấu), ví dụ giao diện InamingContainer Những điều cách cụ thể hữu dụng cho phép bạn tránh việc nuốt ngoại lệ Hãy xem đoạn code ví dụ sau Nếu phương thức Save() không ném ngoại lệ riêng, thực có lựa chọn ngoại trừ việc nuốt ngoại lệ:

try {

user.Save(); }

catch {

Error.Text = user.GetErrors(); Error.Visible = true;

}

//versus try {

user.Save(); }

catch(ValidationException ex) {

Error.Text = ex.GetValidationMessage(); Error.Visible = true;

}

Ví dụ mở rộng ngoại lệ để có cách hành xử riêng đặc biệt liên quan tới ngoại lệ Điều đơn giải đoạn mã lỗi (ErrorCode), phức thông tin phức tạp ngoại lệ PermissionException, điều thể cho phép với người dùng cho phép cần thiết cịn thiếu Hiểu nhiên là, khơng phải tất ngoại lệ cột chặt vào lĩnh vực Chúng ta thường thấy ngoại lệ định hướng theo vận hành Nếu bạn dựa web service để trả đoạn mã lỗi, bạn phủ lên ngoại lệ riêng để ngắt việc thực thi (gọi “rơi” cách chắn) nâng lên sở hạ tầng cho việc ghi chép (logging) bạn

Thực tạo ngoại lệ riêng cần trình hai bước Đầu tiên (và mặt kỹ thuật tất bạn cần) tạo lớp, với tên có nghĩa, thừa kế từ System.Exception

public class UpgradeException : Exception {

}

Bạn nên theo bước tăng cường đánh dấu lớp bạn với thuộc tính SerializeAttribute, ln cung cấp bốn phương thức tạo (constructor) sau :

public YourException() 

public YourException(string message) 

public YourException(string message, Exception innerException) 

(64)

Ba phương thức cho phép ngoại lệ bạn sử dụng theo cách trơng đợi Cái thứ tư dùng để hỗ trợ việc hóa (serialize) trường hợp NET cần hóa ngoại lệ bạn – nghĩa bạn thực phương thức GetObjectData Chức hóa là, trường hợp bạn có thuộc tính riêng, mà bạn mong muốn tồn tại, hóa bất hóa (serialize/deserialize) Đây ví dụ hồn chỉnh:

[Serializable]

public class UpgradeException: Exception {

private int _upgradeId;

public int UpgradeId { get { return _upgradeId; } } public UpgradeException(int upgradeId)

{

_upgradeId = upgradeId; }

public UpgradeException(int upgradeId, string message, Exception inner) : base(message, inner)

{

_upgradeId = upgradeId; }

public UpgradeException(int upgradeId, string message) : base(message) {

_upgradeId = upgradeId; }

protected UpgradeException(SerializationInfo info, StreamingContext c) : base(info, c)

{

if (info != null) {

_upgradeId = info.GetInt32("upgradeId"); }

}

public override void GetObjectData(SerializationInfo i, StreamingContext c) {

if (i != null) {

i.AddValue("upgradeId", _upgradeId); }

base.GetObjectData(i, c) }

}

Trong chương này

(65)

CHƯƠNG 9 Basic : Proxy This and Proxy That

-MỘT TRONG NHỮNG VẺ ĐẸP CỦA LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG LÀ VIỆC SỬ DỤNG LẠI MÃ NGUỒN THÔNG QUA QUÁ TRÌNH THỪA KẾ, NHƯNG ĐỂ ĐẠT ĐƯỢC ĐIỀU ĐĨ, LẬP TRÌNH VIÊN PHẢI THỰC SỰ ĐỂ CHO NHỮNG LẬP TRÌNH VIÊN KHÁC TÁI SỬ DỤNG MÃ CỦA MÌNH – GARY SHORT

Một vào từ khóa đơn giản có sức mạnh kinh ngạc virtual C# (overridable in VB.NET) Khi bạn đánh phương thức virtual, bạn cho phép một lớp thừa kế thực lại cách hành xử Khơng có chức này, thừa kế đa hình khơng cịn hữu dụng Một ví dụ đơn giản, điều chỉnh tí từ Programming

Ruby (ISBN: 978-0-9745140-5-5)), lớp KaraokeSong override phương thức

to_s(ToString) lớp Song :

class Song def to_s

return sprintf("Song: %s, %s (%d)", @name, @artist, @duration) end

end

class KaraokeSong < Song def to_s

return super + " - " @lyrics end

end

Đoạn mã cách mà KaraokeSong xâu dựng phí cách thức hoạt động lớp sở Việc cụ thể hóa khơng đơn mặt liệu, mà cách thức hoạt động! Even if your ruby is a little rusty, you might have picked up that the base to_s method isn't marked as virtual Đó nhiều ngơn ngữ, gồm có Java, làm cho phương thức mặc định virtual Điều thể khác quan điểm nhà thiết kế Java nhà thiết kế C#/VB.NET Trong C#, phương thức mặc định final, người phát triển phải cho phép việc override cách tường minh (thông qua từ khóa virtual) Trong Java, phương thức mặc định virtual người phát triển phải thực cách tường minh việc khơng cho phép override từ khóa

final.

(Tranh luận việc thiết lập mặc định virtual hay final việc thú vị, đi tầm vực chương Nếu độc giả có hứng thú tìm hiểu, người viết đề nghị bạn đọc vấn Anders Heijsber (kiến trúc sư trưởng C#), blog Eric Gunnerson, nên xem thử quan điểm Michael Feathers.) Một cách điển hình, phương thức virtual xem xét với mơ hình miền thừa kế tương ứng Có nghĩa là, lớp KaraokeSong thừa kế từ Song, lớp Dog thừa kế từ lớp Pet Điều khái niệm quan trọng, tài liệu hóa tốt hiểu rõ Do đó, khảo sát phương thức virtual cho chứng có tính kỹ thuật : proxy

(66)

Proxy thứ hoạt động giống thứ khác Về khía cạnh luật pháp, proxy việc ủy quyền để bó phiếu hành động đại diện cho người khác Một proxy có quyền hành xử giống với người ủy nhiệm Trong giới phần cứng, proxy server nằm người dùng server mà người dùng truy cập Proxy server rõ ràng hành xử hệ server thực, với chức thêm - đệm (caching), ghi chép (logging) lọc (filtering) Trong phần mềm, mẫu thiết kế proxy lớp hành xử hệt lớp khác Ví dụ như, xây dựng tác vụ theo vết hệ thống, định sử dụng proxy để áp dụng việc ủy quyền cách rõ ràng đối tượng tác vụ đó:

public class Task {

public static Task FindById(int id) {

return TaskRepository.Create().FindById(id); }

public virtual void Delete() {

TaskRepository.Create().Delete(this); }

}

public class TaskProxy : Task {

public override void Delete() {

if (User.Current.CanDeleteTask()) {

base.Delete(); }

else {

throw new PermissionException( ); }

} }

Nhờ có tính đa hình, FingByID trả về, đối tượng kiểu Task đối tượng kiểu TaskProxy Phía client gọi phương thức không cần biết đối tượng kiểu trả về, chí khơng cần phải biết đối tượng TaskProxy có tồn Nó dựa API để lập trình cơng khai lớp Task

Bởi proxy lớp thực cách hành xử thêm vào, bạn tự hỏi việc lớp Dog proxy lớp Pet Những proxy có khuynh hướng thực chức kỹ thuật hệ thống khác (ghi chép, đệm, ủy quyền, kết nối từ xa…) theo cách thức rõ ràng Nói cách khác, bạn không khai báo biến thuộc kiểu TaskProxy – mà bạn khai báo biến kiểu Dog Vì proxy khơng thêm vào thành phần (vì bạn khơng lập trình với API nó), lớp Dog thêm phương thức Bark

Interception

(67)

dễ dàng để hiểu diễn phía sau vỏ bọc, mức độ hình mẫu áp dụng với RhinoMocks

(Một ghi bên lề Nhibernate Nó xem đối tượng ánh xạ O/R cách suốt, êm thấm, khơng địi hỏi bạn phải điều chỉnh lớp để hoạt động Tuy nhiên, bạn muốn cho phép khả tải từ từ, tất thành phần phải virtual Điều xem êm thấm/trong suốt bạn không thêm vào thành phần NHibernate cụ thể cho lớp bạn – ví dụ thừa kế từ lớp sở NHibernate rải rác thuộc tính NHibernate nơi)

Sử dụng NHibernate có hai hội khác để thúc đẩy việc tải từ từ Đầu tiên, hiển nhiên nhất, tải với tập hợp (collection) Ví dụ như, bạn khơng muốn tải tồn phần Upgrades đối tượng Model bạn thực cần Đây cách mà file ánh xạ bạn nhìn gần giống vậy:

<class name="Model" table="Models"> <id name="Id" column="Id" type="int"> <generator class="native" /> </id>

<bag name="Upgrades" table="Upgrades" lazy="true" > <key column="ModelId" />

<one-to-many class="Upgrade" /> </bag>

</class>

Bằng cách điều thuộc tính cho việc tải từ từ với thành phần bag, đang nói với NHibernate để nạp cách từ từ tập hợp Upgrades NHibernate làm việc cách dễ dàng trả kiểu tập hợp riêng (kiểu tập hợp thực giao diện chuẩn, ví dụ IList, đó, với bạn suốt) Thứ hai, thú vị hơn, cách dùng chức nạp từ từ dành riêng cho đối tượng nằm phạm vị Ý tượng chung bạn muốn toàn đối tượng khởi tạo cách từ từ Vì sao? Hãy nói biến sale tạo Những Sale kết hợp với SalesPerson lẫn mơ hình Car

Sale sale = new Sale();

sale.SalesPerson = session.Get<SalesPerson>(1); sale.Model = session.Get<Model>(2);

sale.Price = 25000; session.Save(sale);

Không may là, phải truy xuất database hai lần để nạp SalesPerson Model thích hợp – khơng thực dùng Sự thật tất ta cần ID chúng (bởi mà lấy điền vào database), mà có sẵn

Bằng cách tạo proxy, NHibernate cho phép hoàn toàn tải từ từ đối tượng kiểu tương ứng với trường hợp Đều phải làm thay đổi việc ánh xạ cho phép tải từ từ đối tượng Model SalesPeople :

<class name="Model" table="Models" lazy="true" proxy="Model"> </class> <class name="SalesPerson" table="SalesPeople"

lazy="true" proxy="SalesPerson "> </class>

(68)

proxy hữu ích với danh sách phương thức chưa khai báo virtual Và tốt nên xem thử tiếp:

Sale sale = new Sale();

sale.SalesPerson = session.Load <SalesPerson>(1); sale.Model = session.Load<Model>(2);

sale.Price = 25000; session.Save(sale);

Chú ý dùng phương thức Load thay Get Sự khác biệt hai bạn thu lớp hỗ trợ nạp từ từ, phương thức Load lấy proxy, Get lấy đối tượng thực Với đoạn code đưa vào này, khơng cịn “đánh” vào database mà nạp ID Thay vậy, gọi Session.Load<Model>(2) trả proxy – sinh động NHibernate Proxy có is 2, chúng cung cấp giá trị nó, thuộc tính khác không khởi tạo Bất lời gọi thành phần khác proxy, ví dụ sale.Model.Name rõ ràng chặn đối tượng nap cần từ database

Chỉ lưu ý, cách hành xử kiểu nạp từ từ NHibernate khó để gỡ lỗi Visual Studio Đó chức watch/local/tooltip VS.NET duyệt qua đối tượng, cho việc tải diễn Các tốt để khảo sát điều diễn thêm vào vài breakpoint xung quanh đoạn code kiểm tra xem hoạt động database thông qua ghi chép NHibernate, thông qua mô tả sơ lược SQL

Hy vọng bạn tưởng tượng cách mà proxy dùng RhinoMocks cho việc thu thập, xem lại xác thực tương tác Khi bạn tạo phần, bạn thực tao proxy đối tượng thực Proxy chặn đứng lời gọi, tùy vào trạng thái bạn mà việc Hiển nhiên, để hoạt động, bạn phải hoặc làm giả giao diện, thành phần virtual lớp.

In This Chapter

http://creativecommons.org/licences/by-nc-nd/3.0/legalcode http://www.openmymind.net/ http://www.codebetter.com/ , Y D S ( h http://codebetter.com/files/folders/codebetter_downloads/entry172562.aspx

Ngày đăng: 18/04/2021, 15:41

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w