Bài tập lớn nhập môn công nghệ phần mềm đề tài refactoring
Trang 1Bài tập lớn Nhập môn Công nghệ Phần mềm
Hà Nội, tháng 11/2015
Trang 2Mục lục
Chương 1 Giới thiệu 4
Chương 2 Các nguyên lý về refactoring 5
2.1 Định nghĩa refactoring 5
2.2 Hai chiếc mũ 5
2.3 Tại sao cần refactor? 6
2.3.1 Refactoring giúp cải thiện thiết kế 6
2.3.2 Refactoring giúp phần mềm dễ hiểu hơn 6
2.3.3 Refactoring giúp bạn lập trình nhanh hơn 6
2.4 Khi nào nên refactor? 7
2.4.1 Nguyên tắc 3 lần 7
2.4.2 Refactor khi thêm chức năng 7
2.4.3 Refactor khi sửa lỗi 7
2.4.4 Refactor khi xem lại mã nguồn (code review)7
2.5 Các vấn đề khi refactor 7
2.5.1 Cơ sở dữ liệu 7
2.5.2 Thay đổi giao diện 8
2.6 Refactoring với thiết kế 8
2.7 Refactoring với hiệu năng 9
Chương 3 Nhận biết mã nguồn cần refactor 10
3.1 Trùng lặp mã 10
3.2 Phương thức dài 10
3.3 Lớp lớn 11
Chương 4 Xây dựng bộ kiểm thử 12
4.1 Giá trị của mã kiểm thử 12
Chương 5 Các phương pháp refactor 13
5.1 Định dạng của các phương pháp refactor 13
5.2.5 Ví dụ: Gán lại một biến nội bộ 16
5.3 Nhập phương thức (Inline method) 17
5.3.1 Tóm tắt 17
5.3.2 Động cơ 18
5.4 Nhập biến tạm 18
5.4.1 Tóm tắt 18
Trang 3Phụ lục A Tài liệu tham khảo 26
Phụ lục B Danh sách “bad smells in code” 27
Phụ lục C Danh sách các phương pháp refactor 28
Phụ lục D Các công cụ refactor mã nguồn Java trong Eclipse Helios 31
Trang 4Chương 1 Giới thiệu
Refactoring là quá trình thay đổi một hệ thống phần mềm nhằm cái tiến cấu trúc bêntrong nhưng không làm biến đổi hành vi bên ngoài của nó Refactoring là việc dọn dẹp
mã nguồn một cách có tổ chức sao cho tối thiểu hoá khả năng gây ra lỗi Về bản chấtrefactoring là nâng cấp thiết kế của mã nguồn sau khi nó đã được viết ra
Trong những hiểu biết thông thường về công nghệ phần mềm, chúng ta thiết kế trướcsau đó mới cài đặt Cần có thiết kế tốt trước sau đó mới có cài đặt tốt Qua thời gian, mãnguồn bị sửa đổi và sự nhất quán của hệ thống, cấu trúc của nó theo thiết kế, dần dần mờnhạt Quá trình cài đặt chuyển dần từ chế tạo sang chắp vá
Refactoring là cách làm ngược lại Khi refactor bạn có thể lấy một thiết kế tồi, thậm chí
là hỗn loạn, và làm lại để nó trở thành một mã nguồn được thiết kế tốt Mỗi bước đềuđơn giản, thậm chí rất đơn giản Bạn chỉ chuyển một trường từ lớp này sang lớp khác,lấy vài đoạn mã ra khỏi phương thức để tạo ra phương thức riêng và đẩy vài đoạn mã lênhay xuống cây thừa kế Tuy nhiên tác động tích luỹ của những thay đổi nhỏ đó có thể cáithiện đáng kể thiết kế Đó là sự đảo ngược của khái niệm software decay
Khi refactor bạn sẽ nhận thấy sự phân phối công việc thay đổi Thiết kế thay vì diễn rađầu tiên, lại diễn ra liên tục trong suốt quá trình phát triển Bạn học được cách cải tiếnthiết kế từ việc xây dựng hệ thống Kết quả của sự tương tác này dẫn đến một chươngtrình với thiết kế luôn tốt khi quá trình phát triển tiếp diễn
Qua quá trình phát triển của công nghệ
phần mềm, cùng với sự phát triển mạnh
mẽ của phần cứng thì yêu cầu về hiệu
suất xử lý ngày càng ít quan trọng thay
vào đó, tính dễ hiểu được đề cao Do
đó các kỹ thuật refactoring cũng ngày
càng được chú ý nhằm nâng cao chất
lượng phần mềm
Thực tế học tập của sinh viên Việt Nam
cho thấy các phần mềm được viết trên
ghế nhà trường thường không có được
thiết kế tốt ngay từ ban đầu Dẫn đến tình trạng này có rất nhiều nguyên nhân chủ quanlẫn khách quan như thiếu kinh nghiệm, thiếu thời gian, chưa chú trọng đến quy trìnhphát triển phần mềm v.v Để những phần mềm này vượt ra khỏi phạm vi của những bàitập lớn, đồ án môn học, đồ án tốt nghiệp tiếp tục phát triển thành sản phẩm thực tế vàthành công trong cuộc sống thì kỹ năng refactoring là rất cần thiết
Trang 5Chương 2 Các nguyên lý về refactoring
2.1 Định nghĩa refactoring
Thuật ngữ refactoring được phát minh bởi Ward Cunningham và Kent Beck vào khoảngnhững năm 1980 khi làm việc với ngôn ngữ Smalltalk Trong cuốn sách “Refactoring:Improving the Design of Existing Code” cung cấp hai định nghĩa như sau:
Refactoring (danh từ): một sự thay đổi ở cấu trúc bên trong của phần mềm giúp nó dễ hiểu và dễ sửa đổi hơn mà không làm thay đổi hành vi bên ngoài.
Refactor (động từ): tái cấu trúc phần mềm bằng cách áp dụng một loạt thao tác refactoring mà không làm thay đổi hành vi bên ngoài.
Trong một số tài liệu tiếng Việt, refactoring được chuyển ngữ thành “cải tiến mã nguồn”tuy nhiên cách nói này khá dài dòng và cũng không diễn tả được hết ý nghĩa của thuậtngữ Tài liệu này sẽ để nguyên thuật ngữ tiếng Anh với hai dạng như trên, ngoài rarefactoring cũng được hiểu là tổng hợp các phương pháp và công cụ để tiến hànhrefactor phần mềm nói chung
Như vậy, hiểu theo nghĩa nào đó refactor chỉ đơn giản là dọn dẹp mã nguồn Tuy nhiênbằng cách áp dụng những phương pháp, công cụ của refactoring, việc dọn dẹp mã nguồn
sẽ hiệu quả hơn đáng kể
Cần phân biệt refactoring và tối ưu hoá hiệu năng (performance optimization) Mục đíchcủa refactoring là làm cho phần mềm dễ hiểu và dễ sửa chữa hơn Tối ưu hoá hiệu năngcũng không làm thay đổi hành vi bên ngoài của phần mềm (trừ tốc độ) mà chỉ thay đổicấu trúc bên trong tuy nhiên việc tối ưu hoá thường dẫn đến những đoạn mã nguồn khóhiểu và khó sửa chữa
Một đặc tính quan trọng nữa của refactoring là không thay đổi hành vi bên ngoài củaphần mềm Phần mềm vẫn phải thực hiện những chức năng giống hệt những gì nó làmtrước đó Người dùng, kể cả end user và lập trình viên, không thể nhận ra rằng có gì đóvừa thay đổi
2.2 Hai chiếc mũ
Kent Beck sử dụng hình ảnh hai chiếc mũ để chỉ quá trình phát triển phần mềm córefactor Bạn chia thời gian ra thành hai hoạt động tách biệt: thêm chức năng và refactor.Khi thêm chức năng, bạn không nên thay đổi những đoạn mã đã có, bạn chỉ thêm vàonhững khả năng mới Bạn có thể đo tiến độ bằng cách tạo ra các bộ test và đảm bảo nóchạy Khi refactor, bạn không thêm chức năng nào mà chỉ tái cấu trúc mã nguồn Bạncũng không thêm bộ test nào (trừ khi phát hiện ra một bộ test bạn đã bỏ lỡ trước đó) vàchỉ thay đổi một bộ test khi thực sự cần thiết để phù hợp với sự thay đổi của giao diện
Trang 62.3 Tại sao cần refactor?
2.3.1 Refactoring giúp cải thiện thiết kế
Không có refactoring, thiết kế của phần mềm sẽ phân rã (software decay) Khi mãnguồn được thay đổi để hiện thực hoá những mục tiêu ngắn hạn hoặc thay đổi mà khônghiểu đầy đủ thiết kế, mã nguồn mất dần cấu trúc Ngày càng khó nhận ra thiết kế bằngcách đọc mã nguồn Refactoring giống như sắp xếp lại mã nguồn Bạn xếp lại những gìkhông ở đúng chỗ của nó Sự mất cấu trúc của mã nguồn có hiệu ứng tích luỹ Càng khónhận ra thiết kế từ mã nguồn thì càng khó duy trì thiết kế đó Refactoring thường xuyên
có thể giúp mã nguồn giữ được hình dạng
Mã nguồn được thiết kế không tốt thường chữa nhiều đoạn mã làm cùng một việc, thôngthường là do mã nguồn hay làm cùng một việc ở những chỗ khác nhau Vì thế một khíacạnh quan trọng của cải tiến thiết kế là xoá bỏ những đoạn mã trùng lặp Tầm quan trọngcủa việc này nằm ở những thay đổi mã nguồn trong tương lai Giảm lượng mã khônggiúp hệ thống chạy nhanh hơn chút nào nhưng lại khiến việc sửa đổi dễ dàng hơn rấtnhiều Có ít mã cần phải hiểu hơn và chỉ cần sửa ở một nơi sự thay đổi sẽ được áp dụngtrên toàn bộ hệ thống
2.3.2 Refactoring giúp phần mềm dễ hiểu hơn
Lập trình giống như cuộc trò chuyện với máy tính Bạn viết mã để bảo máy tính cần phảilàm gì và nó sẽ trả lời bằng cách làm chính xác điều bạn bảo Bạn cần xoá bỏ khoảngcách giữa điều bạn muốn máy tính làm với điều bạn nói với nó Lập trình theo cách hiểunày chỉ đơn giản là nói chính xác điều bạn muốn Tuy nhiên còn có những người kháccần sử dụng mã nguồn của bạn Ai đó sẽ thử đọc mã nguồn của bạn trong thời gian vàitháng để có thể sửa đổi vài chỗ Chúng ta dễ quên mất người sử dụng bổ sung đó, trongkhi họ thực ra lại là những người quan trọng nhất Ai bận tâm nếu máy tính mất thêm vàixung nhịp để dịch? Nhưng sự khác biệt sẽ rất lớn nếu một lập trình viên mất một tuần đểthực hiện một sự sửa đổi trong khi đáng lẽ mất vài giờ nếu anh ta hiểu mã nguồn củabạn
Bạn cũng có thể sử dụng refactoring như một cách để hiểu những đoạn mã của ngườikhác Đọc một vài dòng mã và cố gắng thay đổi nó để phản ánh đúng hơn cách hiểu củabạn sau đó thử chạy lại xem nó còn làm việc hay không Với cách làm này dần dần bạn
sẽ có hiểu biết chắc chắn về hệ thống và việc trở lại những đoạn mã đã được refactoring
sẽ dễ dàng hơn nhiều
2.3.3 Refactoring giúp bạn lập trình nhanh hơn
Điều này nghe như một nghịch lý Để nâng cao chất lượng mã nguồn, chúng ta cần đầu
tư thời gian vào việc refactoring, chẳng phải việc đó sẽ làm giảm năng suất?
Trang 7Thực tế chứng minh rằng thiết kế tốt là rất quan trọng đối với tốc độ phát triển phầnmềm Không có một thiết kế tốt, bạn có thể tiến nhanh một lúc nhưng sau đó sẽ sớmchậm lại Bạn phải dành thời gian tìm và sửa lỗi thay vì thêm chức năng mới Việc sửađổi lâu hơn vì bạn phải cố hiểu hệ thống và tìm những đoạn mã trùng lặp.
Thiết kế tốt là điều kiện cần để duy trì tốc độ phát triển phần mềm và refactoring giúpbạn chống lại sự phân rã (decay) của thiết kế, thậm chí còn cải thiện thiết kế nữa
2.4 Khi nào nên refactor?
2.4.1 Nguyên tắc 3 lần
Lần đầu tiên bạn làm gì đó, hãy làm Lần thứ hai bạn làm điều tương tự, bạn nhận ra sựlặp lại nhưng vẫn làm Lần thứ ba bạn làm điều tương tự, hãy refactor
2.4.2 Refactor khi thêm chức năng
Hoặc một phần đoạn mã cần viết đã được viết ở đâu đó hoặc bạn nhận thấy việc thay đổichút ít thiết kế có thể giúp cài đặt chức năng nhanh chóng hơn, hãy refactor Đừng chỉ
bỏ qua những sai sót trong thiết kế, hãy refactor nó để công việc tiếp theo được thuận lợihơn
2.4.3 Refactor khi sửa lỗi
Lỗi được báo cáo là dấu hiệu cho thấy cần refactor Refactoring giúp bạn hiểu rõ mãnguồn hơn và tìm và loại bỏ lỗi dễ dàng hơn
2.4.4 Refactor khi xem lại mã nguồn (code review)
Xem lại mã nguồn giúp lan truyền kiến thức trong nhóm phát triển, tạo cơ hội cho cáclập trình viên cũ truyền kinh nghiệm cho những lập trình viên mới hơn Refactor khiếnngười review không chỉ tưởng tượng ra mã nguồn trông ra sao với những đề nghị củamình mà thực sự nhìn thấy nó Quá trình xem lại có kết quả rõ ràng hơn, không chỉ làmột vài đề nghị mà là những đề nghị đã được thực hiện
2.5 Các vấn đề khi refactor
2.5.1 Cơ sở dữ liệu
Nhiều ứng dụng gắn chặt với lược đồ dữ liệu hỗ trợ nó, đây là một lý do khiến cơ sở dữliệu khó thay đổi Một lý do khác là việc chuyển đổi dữ liệu có thể rất lâu và nguy hiểm.Với các cơ sở dữ liệu phi đối tượng một cách giải quyết là đặt một tầng phần mềm giữa
mô hình đối tượng và mô hình cơ sở dữ liệu Bằng cách đó bạn có thể tách rời các thayđổi giữa hai mô hình Bạn cũng không cần thiết phải tạo một lớp riêng biệt từ đầu Bạn
có thể xây dựng lớp này khi nhận ra mô hình đối tượng không còn ổn định
Trang 8Cơ sở dữ liệu hướng đối tượng vừa hỗ trợ vừa cản trở Một vài cơ sở dữ liệu hướng đốitượng có khả năng chuyển đổi tự động từ phiên bản này sang phiên bản khác của đốitượng Nếu không, bạn phải rất cẩn thận khi chuyển đổi dữ liệu bằng tay Bạn có thểthoải mái di chuyển các hành vi nhưng với các trường thì phải cần thận Có thể sử dụnghàm truy cập để tạo ra cảm giác là dữ liệu đã được chuyển và khi chắc chắn về sự thayđổi bạn có thể thực sự di chuyển trường.
2.5.2 Thay đổi giao diện
Khi làm việc với đối tượng bạn có thể tuỳ ý thay đổi nội dung bên trong miễn là giaodiện vẫn giữ nguyên Nếu cần thay đổi giao diện và bạn có thể thay đổi tất cả những nơi
sử dụng nó, bạn chỉ việc sửa tất cả đồng thời Tuy nhiên nếu bạn có một giao diện đãđược công bố, mọi việc trở nên phức tạp hơn nhiều Bạn không thể sửa những đoạn mã
do người khác viết sử dụng giao diện của bạn Bạn cần một quy trình phức tạp hơnnhiều
Khi refactor một giao diện đã được công bố, bạn cần giữ lại toàn bộ giao diện cũ, ít nhấtcho đến khi người sử dụng có thể phản ứng với sự thay đổi Nếu bạn đổi tên một hàm,hãy giữ lại hàm cũ và cho nó gọi đến hàm mới Đừng sao chép thân hàm vì bạn sẽ lạimất thời gian xử lý sự trùng lặp mã nguồn Bạn cũng nên sử dụng công cụ deprecate củaJava để thông báo những chỗ bị phản đối
Công bố giao diện có ích lợi của nó nhưng cũng gây nhiều khó khăn vì vậy nếu có thể,đừng công bố giao diện Tất nhiên nếu bạn thiết kế thư viện lập trình, điều đó là khôngtránh khỏi Nhưng nếu bạn làm một phần mềm với nhóm làm việc gồm ba người, đừngcông bố giao diện từ người này đến người kia Đơn giản là hãy mở mã nguồn ra và sửađổi
2.6 Refactoring với thiết kế
Một số người ví thiết kế với bản vẽ của kĩ sư và lập trình với công việc của thợ xây Tuynhiên phần mềm khác với nhà cửa đường xá Alistair Cockburn nói, “Khi thiết kế tôi cóthể nghĩ rất nhanh nhưng suy nghĩ của tôi đầy những lỗ hổng nhỏ.”
Một số khác tranh luận rằng refactoring là sự thay thế cho thiết kế Trong cách tiếp cậnnày bạn hoàn toàn không thiết kế chút nào Bạn chỉ lập trình theo cách đầu tiên nghĩ ra,làm cho nó chạy đúng và sau đó refactor Thực tế cách làm này có thể thực hiện đượcnhưng không phải cách hiệu quả nhất
Khi áp dụng refactoring, bạn không đặt áp lực lên thiết kế ban đầu Bạn không tìm kiếmgiải pháp tốt nhất mà chấp nhận một giải pháp hợp lý Cùng với quá trình phát triển vàhiểu rõ hơn bài toán bạn phát hiện ra những giải pháp hợp lý hơn mà đưa nó vào hệthống
Trang 9Một kết quả quan trọng của refactoring là sự đơn giản trong thiết kế Với cách thiết kếhoàn chỉnh từ đầu, bạn cần tìm ra giải pháp thật mềm dẻo để đáp ứng sự thay đổi củayêu cầu Vấn đề là thiết kế mềm dẻo thì phức tạp và tốn kém hơn thiết kế đơn giản Khitiếp cận vấn đề với refactoring, bạn sẽ tự hỏi “Để refactor thiết kế đơn giản này thànhthiết kế mềm dẻo có khó không?” Nếu câu trả lời là “khá dễ”, bạn chỉ việc cài đặt giảipháp đơn giản.
Như vậy refactoring dẫn đến những thiết kế đơn giản hơn mà không phải hy sinh sựmềm dẻo Khi bạn đã có một nhận thức nhất định về những gì có thể refactor dễ dàng,bạn thậm chí không nghĩ đến thiết kế mềm dẻo nữa Chỉ việc xây dựng những gì đơngiản nhất làm việc được Hầu hết thời gian bạn sẽ không cần dùng đến những thiết kếmềm dẻo và phức tạp nữa
2.7 Refactoring với hiệu năng
Mối quan ngại phổ biến với refactoring là làm giảm hiệu năng của chương trình Tronghầu hết trường hợp refactoring thực sự làm chương trình chậm đi Tuy nhiên bí mật củanhững chương trình chạy nhanh, trừ những trường hợp cực kì nghiêm ngặt, là trước tiên
nó được làm để dễ dàng tinh chỉnh, sau đó mới được tinh chỉnh để đạt tốc độ mongmuốn
Một điều thú vị về hiệu năng là các chương trình thường tốn nhiều thời gian nhất vàomột phần nhỏ của mã nguồn Do vậy nếu bạn tối ưu hoá toàn bộ mã nguồn như nhau thìbạn sẽ lãng phí 90% công sức vào việc tối ưu hoá những đoạn mã không được chạynhiều
Áp dụng nhận xét này bạn sẽ viết những chương trình tốt mà không để ý nhiều đến hiệunăng Đến khi bắt đầu bước tối ưu hoá, thường khá muộn trong quá trình phát triển, bạnbắt đầu sử dụng chương trình profiler để phân tích những đoạn mã tốn thời gian và bộnhớ nhiều nhất Tập trung vào những điểm nóng này, bạn sửa đổi mã nguồn và kiểm tra
sự tiến bộ về hiệu năng cho đến khi đạt được mục tiêu
Refactoring giúp sự tối ưu hoá dễ dàng hơn theo hai cách Thứ nhất, vì cài đặt nhanhhơn, bạn có nhiều thời gian hơn để tối ưu hoá Thứ hai, mã nguồn được chia nhỏ hơn để
dễ dàng phát hiện những điểm nóng và dễ hiểu hơn để bạn tối ưu hoá dễ dàng
Trang 10Chương 3 Nhận biết mã nguồn cần refactor
Hiểu thế nào là refactoring không có nghĩa là bạn đã biết nên refactor lúc nào và ở đâu.Mục đích của chương này là đưa ra những chỉ dẫn để bạn nhận biết những vấn đề trong
mã nguồn và biết cách khắc phục Các tác giả Martin Fowler và Kent Beck gọi chúng là
“bad smells in code” (tạm dịch: mùi hôi trong mã nguồn)
Đây không phải là những thước đo chuẩn để nói rõ đoạn mã nào có vấn đề và đoạn mãnào không, những thước đo như thế thậm chí có thể không tồn tại Tuy nhiên bạn sẽ xâydựng được cho mình những cảm nhận riêng rằng bao nhiêu biến là quá nhiều, phươngthức bao nhiêu dòng là quá dài
Danh sách các “bad smells” được đề xuất bởi Martin Fowler và Kent Beck tương đốidài, ở đây xin giới thiệu một số vấn đề thường gặp Hãy tham khảo Phụ lục B để xemdanh sách đầy đủ
3.1 Trùng lặp mã
Đứng đầu trong những “mùi hôi” là mã nguồn trùng lặp Nếu bạn thấy cùng một cấutrúc xuất hiện ở nhiều nơi, bạn có thể chắc chắn rằng sẽ tốt hơn nếu bạn hợp nhất chúnglại
Đơn giản nhất là trường hợp cùng một biểu thức xuất hiện trong hai phương thức củacùng một lớp Khi đó bạn chỉ việc Trích phương thức và gọi nó từ cả hai nơi
Một vấn đề thường gặp khác là khi cùng một biểu thức được sử dụng ở hai lớp chị em
Bạn có thể xoá bỏ sự trùng lặp này bằng cách Trích phương thức ở cả hai lớp rồi sau đó Kéo lên Nếu đoạn mã tương tự nhưng không giống hệt nhau, bạn cần Trích phương thức để tách đoạn giống và đoạn khác nhau Sau đó bạn có thể Tổ chức phương thức mẫu Nếu các phương thức làm cùng một việc với những giải thuật khác nhau, bạn có thể chọn giải thuật rõ ràng hơn và sử dụng Thay thế giải thuật.
Nếu bạn có những đoạn mã trùng lặp trong các lớp không liên quan, hãy xem xét đến
việc sử dụng Trích lớp từ một lớp và sử dụng thành phần mới ở lớp còn lại Một khả
năng khác là phương thức đáng lẽ chỉ thuộc về một lớp và được gọi bởi lớp còn lại hoặcphương thức thuộc về một lớp thứ ba và được sử dụng bởi cả hai lớp Bạn cần quyếtđịnh xem phương thức có nghĩa ở đâu và đảm bảo rằng nó phải ở đó mà không phải nơinào khác
3.2 Phương thức dài
Chương trình hướng đối tượng tốt nhất và sống lâu nhất là chương trình có các phươngthức ngắn Mọi người đều nhận ra rằng phương thức càng dài thì càng khó hiểu Cácphương thức ngắn với tên gọi được đặt hợp lý có thể tiết kiệm rất nhiều thời gian vì bạn
Trang 11không cần đọc thân hàm.
Trong 99% trường hợp, tất cả những gì cần làm để rút ngắn phương thức là Trích phương thức Hãy tìm những phần của phương thức kết hợp được với nhau và tạo ra
phương thức mới
Nếu bạn có một phương thức với rất nhiều tham số và biến tạm, những thành phần này
có thể cản trở việc trích phương thức Bạn sẽ phải truyền quá nhiều tham số và biến tạmnhư là tham số cho những phương thức được trích ra và kết quả còn khó đọc hơn cả lúc
đầu Bạn có thể sử dụng Thay biến tạm bởi truy vấn để loại bỏ một số biến tạm Danh sách tham số dài có thể được trở thành gọn gàng với các phương pháp Tạo đối tượng tham số và Giữ đối tượng nguyên vẹn.
Nếu bạn đã thực hiện những cách trên mà vẫn có quá nhiều biến tạm và tham số, đã đến
lúc dùng đến pháo hạng nặng: Thay phương thức bằng đối tượng phương thức.
Làm thế nào để xác định những đoạn mã có thể trích ra? Cách khá tốt là tìm chú thích.Một đoạn mã với chú thích chỉ ra rằng nó có thể được thay bởi một phương thức có têndựa trên nội dung chú thích Thậm chí một dòng cũng đáng để trích nếu nó cần phảiđược giải thích
Các câu lệnh điều kiện và vòng lặp cũng là dấu hiệu cần trích phương thức Sử dụng
Phân ly điều kiện để xử lý các biểu thức điều kiện Với vòng lặp, trích nó và mã nguồn
bên trong thành một phương thức riêng
3.3 Lớp lớn
Khi một lớp làm quá nhiều việc, nó thường thể hiện ở chỗ có quá nhiều biến thể hiện(instance variables) Khi một lớp có quá nhiều biến thể hiện, trùng lặp mã nguồn chắcchắn không xa
Bạn có thể Trích lớp để nhóm một số biến Chọn những biến có thể chung sống với nhau
trong một thành phần Chẳng hạn “depositAmount” và “depositCurrency” có nhiều khảnăng cùng thuộc về một thành phần Nói chung, chia sẻ một tiền tố hoặc hậu tố trongmột tập con của các biến trong một lớp gợi ý cần một thành phần mới Nếu thành phần
này thích hợp làm lớp con, bạn sẽ thấy Trích lớp con dễ thực hiện hơn.
Đôi khi một lớp không sử dụng tất cả các biến thể hiện của nó trong tất cả thời gian Nếu
thế, bạn có thể áp dụng Trích lớp và Trích lớp con nhiều lần.
Cũng như lớp có quá nhiều biến, một lớp có quá nhiều mã là mảnh đất màu mỡ cho sựtrung lặp mã nguồn, hỗn loạn và sự thất bại Giải pháp đơn giản nhất là giảm sự dư thừatrong bản thân lớp Nếu bạn có những phương thức dài 500 dòng mã với rất nhiều phầnchung, bạn có thể biến nó thành năm phương thức mười dòng với mười phương thức haidòng trích từ phương thức ban đầu
Trang 12Giải pháp thường gặp cho trường hợp này là Trích lớp và Trích lớp con Một mẹo hữu ích là sử dụng Trích giao diện để xác định khách hàng sử dụng lớp như thế nào Cách
này có thể cho bạn một vài ý tưởng về cách chia nhỏ thêm lớp
Nếu lớp lớn của bạn lại là một lớp GUI, bạn có thể phải chuyển dữ liệu và hành vi sangmột đối tượng nghiệp vụ khác Điều này có thể cần phải nhân bản dữ liệu chuyển cả hainơi và giữ dữ liệu đồng bộ Nhân bản dữ liệu gọi ý cách thực hiện việc này
Trang 13Chương 4 Xây dựng bộ kiểm thử
Nếu bạn muốn refactor, điều kiện tiên quyết là phải có bộ kiểm thử chắc chắn Ngay cảkhi bạn có những công cụ có thể tự động hoá refactoring, bạn vẫn cần kiểm thử Sẽ cònrất lâu nữa trước khi mọi thao tác refactor đều có thể thực hiện bằng công cụ
4.1 Giá trị của mã kiểm thử
Nếu bạn quan sát hầu hết các lập trình viên sử dụng thời gian của họ, bạn sẽ nhận rarằng viết mã thực ra chỉ chiếm một phần nhỏ Một ít thời gian để tìm hiểu cần phải làm
gì để viết tiếp, một ít để thiết kế, nhưng hầu hết thời gian được dùng để gỡ lỗi Chắcchắn bạn sẽ nhớ những tiếng đồng hồ dài gỡ lỗi, nhiều khi giữa đêm khuya Mọi lậptrình viên đều có thể kể chuyện một lỗi khiến anh ta mất cả ngày (hoặc nhiều hơn) đểtìm ra Sửa lỗi thường thường khá nhanh, nhưng tìm ra nó là cả một cơn ác mộng Vàkhi bạn sửa một lỗi, luôn luôn có khả năng một lỗi khác sẽ xuất hiện mà bạn không biếtcho đến rất lâu sau Bạn sẽ mất nhiều năm để tìm ra lỗi đó
Mã nguồn tự kiểm tra (self-testing code) là cách để máy tính tự động kiểm tra lỗi giúpbạn Các công cụ hiện nay cho phép bạn viết những đoạn mã khi được chạy sẽ đưa rathông báo lỗi nếu những kỳ vọng không được thoả mãn Ngay khi viết một hàm nào đó,hãy viết ngay mã kiểm tra sự thực hiện của hàm đó Bạn chỉ phải viết mã này một lần,mỗi khi refactor hay bất cứ thay đổi nào khác với mã nguồn, bạn có thể chạy lại nó đểđảm bảo mọi thứ vẫn hoạt động chính xác
Theo Martin Fowler, “một bộ kiểm thử là chiếc máy phát hiện lỗi mạnh mẽ loại bỏ thờigian tốn để tìm lỗi.” Trên thực tế, dành thời gian để viết mã kiểm thử là một cách tiếtkiệm thời gian và phát triển phần mềm nhanh hơn
Thậm chí một số phương pháp phát triển phần mềm hiện đại yêu cầu viết mã kiểm thửtrước khi viết chương trình Bằng việc viết ra các trường hợp có thể xảy ra, bạn hiểu rõmình mong muốn điều gì ở tính năng sắp cài đặt Viết test cũng giúp bạn tập trung vàogiao diện hơn là cài đặt (luôn luôn là thói quen tốt) Nó cũng có nghĩa bạn biết chắc khinào hoàn thành việc viết mã: khi test chạy được
Định nghĩa refactoring yêu cầu quá trình này không làm thay đổi hành vi bên ngoài của
hệ thống Sử dụng mã kiểm thử là một cách rõ ràng để đạt được mục đích này Mỗi khibạn thực hiện một thao tác refactoring, hãy nhớ chạy lại toàn bộ test để đảm báo nókhông gây ra lỗi trước khi thực hiện thao tác tiếp theo
Trang 14Chương 5 Các phương pháp refactor
Martin Fowler trong cuốn sách của ông dẫn ra một danh sách dài các phương thức khácnhau để refactor Trong giới hạn của tài liệu này tôi không có điều kiện trình bày hết tất
cả mà chỉ chọn ra vài “gương mặt” tiêu biểu Danh sách đầy đủ các phương pháprefactor có ở Phụ lục C
5.1 Định dạng của các phương pháp refactor
Trong các phần tiếp theo, các phương pháp refactor được trình bày với một định dạngthống nhất như sau:
• Bắt đầu bằng tên của phương pháp
• Sau đó là tóm tắt về tình huống bạn cần sử dụng phương pháp và nội dung
phương pháp
• Phần động cơ mô tả tại sao phương pháp nên được thực hiện và những trường
hợp không nên dùng
• Phần cơ chế trong sách được lược bỏ do các công cụ hiện tại đã hỗ trợ rất tốt
những thao tác refactor thường dùng
• Các ví dụ cho một trường hợp rất đơn giản mà phương pháp được sử dụng để
minh hoạ cách nó làm việc
5.2 Trích phương thức
5.2.1 Tóm tắt
Một đoạn mã nào đó có thể được nhóm lại
Chuyển đoạn mã thành một phương thức, đặt tên để giải thích mục đích của phương thức đó.
void printOwing(double amount) {
printBanner();
//print details
System.out.println ("name:" + _name);
System.out.println ("amount" + amount);
}
Chuyển thành:
void printOwing(double amount) {
Trang 15printBanner();
printDetails(amount);
}
void printDetails (double amount) {
System.out.println ("name:" + _name);
System.out.println ("amount" + amount);
dễ dàng hơn
Đôi khi mọi người băn khoăn một phương thức nên dài bao nhiêu Độ dài của phươngthức thực ra không phải vấn đề, điều quan trọng là khoảng cách về ngữ nghĩa giữa tênphương thức và thân phương thức Nếu việc trích rút cải thiện tính rõ ràng, hãy làm điều
đó, kể cả khi tên phương thức còn dài hơn đoạn mã bạn vừa trích
5.2.3 Ví dụ: Không có biến nội bộ
Trang 16}
//print details
System.out.println ("name:" + _name);
System.out.println ("amount" + outstanding);
System.out.println ("name:" + _name);
System.out.println ("amount" + outstanding);