Đề tài Ứng dụng kỹ thuật tái cấu trúc mã nguồn để triển khai dò tìm và cải tiến các đoạn mã xấu trong chương trình C#

MỤC LỤC

LỜI CAM ĐOAN . 2

MỤC LỤC . 3

DANH MỤC HÌNH ẢNH . 5

MỞ ĐẦU . 6

CHưƠNG I: KỸ THUẬT TÁI CẤU TRÚC MÃ NGUỒN (REFACTORING) . 7

I.1 ĐỊNH NGHĨA KỸ THUẬT TÁI CẤU TRÚC MÃ NGUỒN . 7

I.1.1 Ví dụ minh họa. 7

I.1.2 Định nghĩa kỹ thuật tái cấu trúc mã nguồn . 19

I.2 HIỆU QUẢ CỦA TÁI CẤU TRÚC MÃ NGUỒN . 20

I.2.1 Refactoring cải thiện thiết kế phần mềm . 20

I.2.2 Refactoring làm mã nguồn phần mềm dễ hiểu . 20

I.2.3 Refactoring giúp phát hiện và hạn chế lỗi . 21

I.2.4 Refactoring giúp đấy nhanh quá trình phát triển phần mềm . 21

I.3 KHI NÀO THỰC HIỆN TÁI CẤU TRÚC MÃ NGUỒN . 22

I.3.1 Refactor khi thêm chức năng . 22

I.3.2 Refactor khi cần sửa lỗi . 22

I.3.3 Refactor khi thực hiện duyệt chương trình . 23

I.4 CÁC KỸ THUẬT TÁI CẤU TRÚC MÃ NGUỒN . 23

I.4.1 Danh mục các kỹ thuật tái cấu trúc mã nguồn . 23

I.5 NHẬN XÉT VÀ KẾT LUẬN . 26

CHưƠNG II: LỖI CẤU TRÚC (BAD SMELLS) TRONG MÃ NGUỒN . 27

II.1 KHÁI NIỆM VỀ LỖI CẤU TRÚC (BAD SMELLS) . 27

II.2 LỖI CẤU TRÚC VÀ GIẢI PHÁP CẢI TIẾN . 27

II.2.1 Duplicated Code - Trùng lặp mã . 27

II.2.2 Long Method – Phương thức phức tạp . 28

II.2.3 Large Class – Qui mô lớp lớn . 30

II.2.4 Long Parameter List - Danh sách tham số quá dài . 31

II.2.5 Divergent Change – Cấu trúc lớp ít có tính khả biến . 32

II.2.6 Shotgun Surgery – Lớp được thiết kế không hợp lý và bị phân rã . 32

II.2.7 Feature Envy – Phân bố phương thức giữa các lớp không hợp lý . 33

II.2.8 Data Clumps – Gôm cụm dữ liệu . 34

II.2.9 Primitive Obsession – Khả năng thể hiện dữ liệu của lớp bị hạn chế . 34

II.2.10 Switch Statements – Khối lệnh điều kiện rẽ hướng không hợp lý . 36

II.2.11 Lazy Class – Lớp được định nghĩa không cần thiết . 38

II.2.12 Speculative Generality – Cấu trúc bị thiết kế dư thừa . 38

II.2.13 Temporary Field – Lạm dụng thuộc tính tạm thời . 39

II.2.14 Message Chains –Chuỗi phương thức liên hoàn khó kiểm soát. 39

II.2.15 Middle Man – Quan hệ ủy quyền không hợp lý/logic . 39

II.2.16 Inapproprite Intimacy - Cấu trúc thành phần riêng không hợp lý . 41

II.2.17 Alternative Classes with Different Interfaces - Đặc tả lớp không rõ ràng 41

II.2.18 Incomplete Library Class – Sử dụng thư viện lớp chưa được hòan chỉnh 41

II.2.19 Data Class – Lớp dữ liệu độc lập . 42

II.2.20 Refused Bequest – Quan hệ kế thừa không hợp lý/logic . 43

II.2.21 Comments – Chú thích không cần thiết . 43

II.3 NHẬN XÉT VÀ KẾT LUẬN . 44

CHưƠNG III: NỀN TẢNG .NET VÀ NGÔN NGỮ LẬP TRÌNH C# . 45

III.1 TỔNG QUAN VỀ NỀN TẢNG .NET . 45

III.1.1 Định nghĩa .NET . 45

III.1.2 Mục tiêu của .NET . 45

III.1.3 Dịch vụ của .NET . 45

III.1.4 Kiến trúc của .NET . 46

III.2 NGÔN NGỮ LẬP TRÌNH C# . 47

III.2.1 Tổng quan về ngôn ngữ lập trình C# . 47

III.2.2 Đặc trưng của các ngôn ngữ lập trình C# . 47

III.3 MÔI TRưỜNG PHÁT TRIỂN ỨNG DỤNG VISUAL STUDIO .NET . 48

CHưƠNG IV: ỨNG DỤNG KỸ THUẬT TÁI CẤU TRÚC MÃ NGUỒN ĐỂ

DÒ TÌM VÀ CẢI TIẾN CÁC ĐOẠN MÃ XẤU TRONG CHưƠNG TRÌNH C# . 49

IV.1 GIẢI PHÁP VÀ CÔNG CỤ HỖ TRỢ REFACTOR . 49

IV.1.1 Đặc tả giải pháp triển khai . 49

IV.1.2 Một số công cụ và tiện ích hỗ trợ việc dò tìm và cải tiến mã xấu . 50

IV.1.3 Thử nghiệm minh họa các công cụ hỗ trợ refactor trong VS.Net . 57

IV.1.4 Nhận xét và đánh giá . 80

IV.2 ỨNG DỤNG KỸ THUẬT TÁI CẤU TRÚC MÃ NGUỒN ĐỂ DÒ TÌM VÀ

CẢI TIẾN CÁC ĐOẠN MÃ XẤU TRONG CHưƠNG TRÌNH C#. 81

IV.2.1 Thực hiện kỹ thuật tái cấu trúc mã nguồn trên chương trình thực tế . 82

IV.2.2 Phân tích và đánh giá kết quả thực hiện . 94

IV.3 NHẬN XÉT VÀ KẾT LUẬN . 95

CHưƠNG V: KẾT LUẬN . 96

V.1 ĐÁNH GIÁ KẾT QUẢ CỦA ĐỀ TÀI . 96

V.2 PHẠM VI ỨNG DỤNG . 96

V.3 HưỚNG PHÁT TRIỂN . 97

V.3.1 Triển khai áp dụng trên các ngôn ngữ khác . 97

V.3.2 Thử nghiệm xây dựng một refactoring tool tích hợp vào VS.NET . 97

TÀI LIỆU THAM KHẢO . 98

 

pdf99 trang | Chia sẻ: lethao | Ngày: 04/04/2013 | Lượt xem: 1680 | Lượt tải: 5download
Bạn đang xem nội dung tài liệu Đề tài Ứng dụng kỹ thuật tái cấu trúc mã nguồn để triển khai dò tìm và cải tiến các đoạn mã xấu trong chương trình C#, để tải tài liệu về máy bạn click vào nút DOWNLOAD ở trên
chung với nhau lại. Sử dụng Extract Subclass khi các phƣơng thức kết hợp với các biến mở rộng chức năng của lớp. Thông thƣờng không phải lúc nào tất cả các biến trong một lớp cũng luôn đƣợc sử dụng , vì vậy chúng ta cũng có thể sử dụng Extract Class hoặc Extract Subclass để trích xuất chúng nhiều lần. - Một thủ thuật hữu ích nữa là xem xét mục đích sử dụng của các lớp kết hợp với kỹ thuật Extract Interface để tổ chức và phân chia lớp cho hợp lý. - Trong lập trình hƣớng đối tƣợng, khi gặp một lớp GUI có qui mô lớn cần chuyển dữ liệu và hoạt động xử lý đến một đối tƣợng có phạm vi riêng. Điều này yêu cầu có sự trùng lắp dữ liệu ở hai nơi cũng nhƣ sự đồng nhất trong quá trình động bộ. Lúc này Duplicate Observed Data trong refactoring sẽ đƣợc xem xét và sử dụng. Ví dụ: Một lớp mà có một số chức năng chỉ đƣợc sử dụng cho một vài thực thể (instances) cá biệt thì nên sử dụng Extract Subclass để tạo một lớp con đi kèm các chức năng đó Luận văn tốt nghiệp cao học – Khóa 2005 - 2008 Học viên thực hiện: Nhiêu Lập Hòa 31 II.2.4 Long Parameter List - Danh sách tham số quá dài Trong lập trình hƣớng thủ tục, việc trao đổi mọi thứ dữ liệu giữa các thủ tục và hàm thông qua việc truyền tham số. Một giải pháp khác tốt hơn là sử dụng dữ liệu toàn cục nhƣng tiềm ẩn nhiều rủi ro trong việc kiểm soát. Với lập trình hƣớng đối tƣợng thì khác, chúng ta không cần phải chuyển mọi thứ thông qua một danh sách dài các tham số khó hiểu mà chỉ là một ít dữ liệu cơ sở vừa đủ cho việc truy suất tất cả các giá trị cần thiết khác luôn đƣợc cập nhật mới. - Sử dụng Replace Parameter with Method để nhận dữ liệu từ một đối tƣợng đã biết. Đối tƣợng này có thể là một trƣờng dữ liệu hoặc một tham số khác. Sử dụng Preserve Whole Object để thay thế một nhóm dữ liệu trong lớp bởi một thành phần dữ liệu thay thế chung của bản thân lớp đó.Với những mục dữ liệu riêng mà không gắn liền với một đối tƣợng nào cả, sử dụng Introduce Parameter Object - Một ngoại lệ quan trọng trong việc thực hiện những thay đổi này, đó là khi chúng ta không muốn tạo ra một sự phụ thuộc từ đối tƣợng đƣợc gọi đến một đối tƣợng lớn hơn. Giải pháp hợp lý trong trƣờng hợp này là chuyển dữ liệu nhƣ các tham số, khi đó cần chú ý đến những rủi ro có liên quan. Nếu danh sách tham số quá dài hoặc thƣờng xuyên thay đổi, chúng ta cần xem xét lại cấu trúc phụ thuộc. Ví dụ 1: Sử dụng Replace Parameter with Method để rút gọn tham số phƣơng thức public double getPrice() { int basePrice = _quantity * _itemPrice; int discountLevel; if (_quantity > 100) discountLevel = 2; else discountLevel = 1; double finalPrice = discountedPrice (basePrice, discountLevel); return finalPrice; } private double discountedPrice (int basePrice, int discountLevel) { if (discountLevel == 2) return basePrice * 0.1; else return basePrice * 0.05; } public double getPrice() { int basePrice = _quantity * _itemPrice; int discountLevel = getDiscountLevel(); double finalPrice = discountedPrice (basePrice); return finalPrice; } private int getDiscountLevel() { if (_quantity > 100) return 2; else return 1; } Luận văn tốt nghiệp cao học – Khóa 2005 - 2008 Học viên thực hiện: Nhiêu Lập Hòa 32 private double discountedPrice (int basePrice) { if (getDiscountLevel() == 2) return basePrice * 0.1; else return basePrice * 0.05; } Ví dụ 2: Với những tham số là các mục dữ liệu riêng mà không gắn liền với một đối tƣợng nào cả, sử dụng Introduce Parameter Object II.2.5 Divergent Change – Cấu trúc lớp ít có tính khả biến Cấu trúc của phần mềm phải đƣợc thiết kế sao cho dễ dàng trong việc thay đổi và cập nhật. Nếu một lớp bị thay đổi theo nhiều cách khác nhau tùy thuộc vào các nguyên nhân khác nhau thì chúng ta nên chia lớp đó ra làm hai, trong đó mỗi lớp đƣợc tạo ra dựa trên yếu tố đặc trƣng thuận lợi với các cách thay đổi nêu trên. Trong trƣờng hợp chia tách này, giải pháp chính là sử dụng Extract Class. Ví dụ: Sử dụng kỹ thuật Extract Class tạo một lớp mới và dịch chuyển các thuộc tính và phƣơng thức có liên quan từ lớp cũ sang II.2.6 Shotgun Surgery – Lớp đƣợc thiết kế không hợp lý và bị phân rã Trái ngƣợc với Divergent Change là Shotgun Surgery. Mỗi khi chúng ta thực hiện một sự thay đổi trong chƣơng trình, thông thƣờng việc thay đổi này kéo theo một chuỗi các thay đổi nhỏ ở nhiều lớp khác nhau và diễn ra ở khắp nơi. Lúc này việc phát hiện chính xác và thay đổi toàn bộ là một điều rất khó khăn không loại trừ có thể thiếu sót ở một nơi quan trọng nào đó. - Sử dụng Move Method & Move Field để dịch chuyển (thâu gôm) các phƣơng thức và thuộc tính từ lớp này sang lớp khác để làm cho chƣơng trình có cấu trúc rõ hơn. - Nếu chƣa có lớp nào thích hợp để tích hợp vào thì tạo một lớp mới. Và trong trƣờng hợp này, chúng ta sử dụng giải pháp Inline Class để tích hợp các thành phần thay đổi này. Luận văn tốt nghiệp cao học – Khóa 2005 - 2008 Học viên thực hiện: Nhiêu Lập Hòa 33 Ví dụ 1: Nếu một thuộc tính aField đƣợc sử dụng nhiều hơn trong lớp khác Class 2 thay vì lớp chứa nó Class 1 thì chúng ta nên sử dụng Move Field để dịch chuyển phƣơng thức đó sang lớp kia. Ví dụ 2: Nếu hai lớp có qui mô nhỏ (không làm gì nhiều) và có quan hệ với nhau thì nên sử dụng Inline Class để hợp nhất chúng lại một lớp. II.2.7 Feature Envy – Phân bố phƣơng thức giữa các lớp không hợp lý Trong lập trình hƣớng đối tƣợng, đôi lúc chúng ta gặp trƣờng hợp một phƣơng thức của một lớp mà nó sử dụng hoặc hỗ trợ nhiều chức năng (thuộc tính và phƣơng thức) cho một lớp khác hơn chính lớp chứa nó. Khi đó chúng ta sẽ sử dụng Extract Method và Move Method để trích xuất và dịch chuyển phƣơng thức vào một vị trị lớp thích hợp nhất. Ví dụ: Một phƣơng thức của một lớp Class 1 mà nó sử dụng nhiều chức năng (thuộc tính và phƣơng thức) của một lớp khác Class 2 hơn chính nó thì sử dụng Move Method để dịch chuyển phƣơng thức đó sang lớp kia. Luận văn tốt nghiệp cao học – Khóa 2005 - 2008 Học viên thực hiện: Nhiêu Lập Hòa 34 II.2.8 Data Clumps – Gôm cụm dữ liệu Thông thƣờng trong mã nguồn, chúng ta cũng gặp trƣờng hợp cùng lúc ba hoặc bốn mục dữ liệu thƣờng đi chung với nhau (nhiều thuộc tính trong lớp, các tham số truyền cho phƣơng thức,…) và đƣợc sử dụng ở nhiều nơi. Một giải pháp tốt hơn là gôm cụm chúng lại và chuyển sang một lớp đối tƣợng: - Nếu các mục dữ liệu đó là các thuộc tính -> sử dụng Extract Class để định nghĩa các lớp mới phù hợp với từng nhóm thuộc tính. - Nếu các mục dữ liệu là các tham số -> sử dụng Introduce Parameter Object hoặc Preserve Whole Object để làm gọn hơn biểu thức tham số truyền vào và thân chƣơng trình xử lý Ví dụ: Thay vì truy xuất từng giá trị thuộc tính riêng lẻ của một đối tƣợng và truyền các giá trị này theo kiểu tham số cho một phƣơng thức. Chúng ta sẽ sử dụng kỹ thuật Preserve Whole Object để truyền nguyên đối tƣợng đó. int low = daysTempRange().getLow(); int high = daysTempRange().getHigh(); withinPlan = plan.withinRange(low, high); withinPlan = plan.withinRange(daysTempRange()); II.2.9 Primitive Obsession – Khả năng thể hiện dữ liệu của lớp bị hạn chế Một trong những mã xấu thƣờng gặp là sự gộp chung các lớp đối tƣợng có quan hệ với nhau thành một lớp đối tƣợng chung có cấu trúc phức tạp và khó hiểu. Trong trƣờng hợp này, chúng ta cần phải tùy biến cấu trúc ban đầu cho hợp lý hơn: - Nếu một thuộc tính dữ liệu trong lớp cần đƣợc bổ sung thêm thộng tin hoặc hành vi, sử dụng Replace Data Value with Object để chuyển đổi thuộc tính dữ liệu đó sang một lớp đối tƣợng. - Nếu giá trị dữ liệu là một mã kiểu (type code) thì sử dụng Replace Type Code with Class để chuyển sang một lớp mới - Nếu mã kiểu (type code) là điều kiện rẻ hƣớng (làm ảnh hƣởng hành vi của một lớp) thì sử dụng Replace Type Code with Subclasses hoặc Replace Type Code with State/Strategy để chuyển đổi mã kiểu này sang một đối tƣợng tĩnh - Nếu là các thuộc tính ban đầu thƣờng đi chung thì sử dụng Extract Class, và nếu đó là các danh sách tham số thì sử dụng Introduce Parameter Object hoặc Replace Array with Object trong trƣờng hợp một mảng dữ liệu để chuyển đổi sang một lớp đối tƣợng tƣơng ứng. Luận văn tốt nghiệp cao học – Khóa 2005 - 2008 Học viên thực hiện: Nhiêu Lập Hòa 35 Ví dụ 1: Chúng ta có một lớp Order dùng để lƣu các dữ liệu về đơn hàng của đối tƣợng khách hàng trong đó customer là một thuộc tính chuỗi của lớp. Trên thực tế, thì đôi lúc ngƣời sử dụng cần biết thêm thông tin về khách hàng tƣơng trong đơn hàng (địa chỉ, số điện thoại,…). Lúc này chúng ta cần sử dụng kỹ thuật Replace Data Value with Object để chuyển đổi dữ liệu customer sang một đối tƣợng Customer. Ví dụ 2: Sử dụng Replace Type Code with Class để thay kiểu dữ liệu số bởi một lớp class Person { public static final int O = 0; public static final int A = 1; public static final int B = 2; public static final int AB = 3; private int _bloodGroup; public Person (int bloodGroup) { _bloodGroup = bloodGroup; } public void setBloodGroup(int arg) { _bloodGroup = arg; } public int getBloodGroup() { return _bloodGroup; } } class BloodGroup { public static final BloodGroup O = new BloodGroup(0); public static final BloodGroup A = new BloodGroup(1); public static final BloodGroup B = new BloodGroup(2); public static final BloodGroup AB = new BloodGroup(3); private static final BloodGroup[] _values = {O, A, B, AB}; private final int _code; private BloodGroup (int code ) { _code = code; } public int getCode() { return _code; } Luận văn tốt nghiệp cao học – Khóa 2005 - 2008 Học viên thực hiện: Nhiêu Lập Hòa 36 public static BloodGroup code(int arg) { return _values[arg]; } } class Person { public static final int O = BloodGroup.O. getCode(); public static final int A = BloodGroup.A.getCode(); public static final int B = BloodGroup.B.getCode(); public static final int AB = BloodGroup.AB.getCode(); private BloodGroup _bloodGroup; public Person (BloodGroup bloodGroup ) { _bloodGroup = bloodGroup; } public int getBloodGroupCode() { return _bloodGroup.getCode(); } public void setBloodGroup(BloodGroup arg) { _bloodGroup = arg; } public BloodGroup getBloodGroup() { return _bloodGroup; } } II.2.10 Switch Statements – Khối lệnh điều kiện rẽ hƣớng không hợp lý Các câu lệnh switch thƣờng là nguyên nhân phát sinh việc trùng lặp và chúng nằm rải rác khắp ở nhiều nơi trong thân chƣơng trình. Trong trƣờng hợp cần thêm một giá trị điều kiện rẻ nhánh mới, chúng ta phải thực hiện việc kiểm tra tất cả các câu lệnh switch này. Một giải pháp thích hợp và cải tiến hơn là cần đƣợc xét đến đó là sử dụng lớp và tính chất đa hình (polymorphism) trong hƣớng đối tƣợng. - Đối với các mã kiểu, sử dụng kết hợp các kỹ thuật Extract Method, Move Method và Replace Type Code with Subclasses/State/Strategy để tạo nên các lớp mới từ việc trích xuất các khối lệnh điều kiện rẽ hƣớng sau đó dịch chuyển các lớp mới tạo này vào trong các lớp mà ở đó tính đa hình đƣợc sử dụng. Và cuối cùng là Replace Conditional with Polymorphism. - Nếu đó chỉ là một vài trƣờng hợp rẽ nhánh trong một phƣơng thức đơn mà tính chất đa hình không cần thiết, một giái pháp thích hợp hơn là áp dụng Replace Parameter with Explicit Methods hoặc Introduce Null Object trong trƣờng hợp có một điều kiện Null Ví dụ 1: Câu lệnh switch bên dƣới lựa chọn các xử lý khác nhau tùy thuộc vào giá trị của kiểu đối tƣợng. Sử dụng Replace Conditional with Polymorphism để chuyển đổi mỗi nhánh của điều kiện thành một phƣơng thức chồng (overriding method) trong mỗi lớp con. Luận văn tốt nghiệp cao học – Khóa 2005 - 2008 Học viên thực hiện: Nhiêu Lập Hòa 37 double getSpeed() { switch (_type) { case EUROPEAN: return getBaseSpeed(); case AFRICAN: return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts; case NORWEGIAN_BLUE: return (_isNailed) ? 0 : getBaseSpeed(_voltage); } throw new RuntimeException ("Should be unreachable"); } Ví dụ 2: Nếu chúng ta có phƣơng thức mà nó thực thi các mã lệnh khác nhau phụ thuộc vào giá trị của các tham số đƣợc liệt kê thì sử dụng Replace Parameter with Explicit Methods để tạo một phƣơng thức riêng tƣơng tứng với mỗi giá trị của tham số. void setValue (String name, int value) { if (name.equals("height")) _height = value; if (name.equals("width")) _width = value; Assert.shouldNeverReachHere(); } void setHeight(int arg) { _height = arg; } void setWidth (int arg) { _width = arg; } Luận văn tốt nghiệp cao học – Khóa 2005 - 2008 Học viên thực hiện: Nhiêu Lập Hòa 38 II.2.11 Lazy Class – Lớp đƣợc định nghĩa không cần thiết Một lớp đối tƣợng đƣợc tạo ra bao giờ cũng có những lợi ích của nó, tuy nhiên đi kèm với những lợi ích đó là những rủi ro có thể xảy ra và phí tổn trong quá trình hoạt động và bảo trì. Vì vậy chúng ta cần phải cân xét hai yếu tố này khi xác định sự hiện diện của một lớp. Trong quá trình refactoring, đến một lúc nào đó có thể chúng ta nhận ra rằng sự hiện diện của một lớp đối tƣợng A là không cần thiết, lúc này chúng ta có thể sử dụng kỹ thuật Collapse Hierarchy hoặc Inline Class để giản lƣợc hoặc tích hợp nó vào trong một lớp khác. Ví dụ: Nếu 2 lớp có quan hệ kế thừa không có sự khác biệt nhau nhiều, sử dụng Collapse Hierarchy để tích hợp chúng lại với nhau (giản lƣợc lớp con). II.2.12 Speculative Generality – Cấu trúc bị thiết kế dƣ thừa Đối với những phƣơng thức và lớp đối tƣợng đƣợc thiết kế nhƣng trên thực tế không thật sự dùng đến thì chúng ta nên xem xét và loại bỏ. - Sử dụng Collapse Hierarchy để loại bỏ các lớp trừu tƣợng (abstract classses) thật sự ít khi đƣợc sử dụng. - Xóa bỏ tính chất ủy quyền không cần thiết giữa các lớp với kỹ thuật Inline Class - Sử dụng Remove Parameter để xóa bỏ các tham số không thật sự dùng đến trong thân phƣơng thức - Sử dụng Rename Method để thay đổi tên phƣơng thức với mục đích dễ hiểu nhất Ví dụ: Một tham số mà không thật sự dùng đến trong thân phƣơng thức thì nên dùng Remove Parameter để xóa bỏ. Luận văn tốt nghiệp cao học – Khóa 2005 - 2008 Học viên thực hiện: Nhiêu Lập Hòa 39 II.2.13 Temporary Field – Lạm dụng thuộc tính tạm thời Chúng ta cũng có thể xem xét việc từ chối định nghĩa và sử dụng các biến thành viên không đƣợc sử dụng thƣờng xuyên bởi lớp. - Trên thực tế khi gặp trƣờng hợp các biến mồ côi, sử dụng Extract Class để gôm nhóm chúng cùng các mã lệnh có liên quan và đặt vào trong một lớp mới. Sử dụng Introduce Null Object nếu biến đó tham chiếu đến trƣờng hợp đặc biệt Null. - Với các thuật toán phức tạp làm phát sinh các thuộc tính tạm trong quá trình xử lý thì nên hạn chế việc truyền vào một danh sách tham số quá lớn. II.2.14 Message Chains –Chuỗi phƣơng thức liên hoàn khó kiểm soát Khi gặp một chuỗi các phƣơng thức/thông điệp yêu cầu xử lý đƣợc cấu trúc nhƣ thế này getA().getB().getC().getD().getE().doIt(); Đây là một hình thức quan hệ kết buộc trong cấu trúc giữa các phƣơng thức mà bất kỳ sự thay đổi nào trong mối quan hệ này cũng làm ảnh hƣởng kết quả nhận đƣợc. Trong trƣờng hợp này tùy theo quan điểm của từng ngƣời, có ngƣời vẫn chấp nhận sự hiện diện trong mã nguồn, có ngƣời thì cho rằng đó là một cấu trúc xấu cần phải cải tiến dựa trên kỹ thuật Extract Method và Move Method II.2.15 Middle Man – Quan hệ ủy quyền không hợp lý/logic Tính chất ủy quyền trong hƣớng đối tƣợng rất hữu dụng, tuy nhiên đôi lúc nó cũng bị lạm dụng quá mức. Nếu một lớp hoạt động nhƣ một sự ủy quyền nhƣng lại thực hiện một số công việc bổ sung không hữu ích thì chúng ta cũng nên xem xét để hủy bỏ trong một số trƣờng hợp. - Nhìn vào giao diện của một lớp, nếu thấy rằng ½ các phƣơng thức ủy quyền đến một lớp khác, thì hãy sử dụng Remove Middle Man để tái cấu trúc lại tính chất ủy quyền của các phƣơng thức giữa các lớp này. - Nếu chỉ xảy ra với vài phƣơng thức, sử dụng Inline Classs để tích hợp/hợp nhất chúng vào trong lớp gọi. - Nếu có thêm vào các hành vi xử lý, sử dụng Replace Delegation with Inheritance để chuyển đổi quan hệ ủy quyền không hợp lý này trong một lớp con của một đối tƣợng thực tế (quan hệ thừa kế) Luận văn tốt nghiệp cao học – Khóa 2005 - 2008 Học viên thực hiện: Nhiêu Lập Hòa 40 Ví dụ: Cải tiến mã xấu về quan hệ ủy quyền không hợp lý/logic thông qua kỹ thuật Remove Middle Man class Person...{ Department _department; public Person getManager() { return _department.getManager(); } } class Department...{ private Person _manager; public Department (Person manager) { _manager = manager; } public Person getManager() { return _manager; } } //Để tìm manager của một người manager = john.getManager(); class Person...{ Department _department; public Department getDepartment() { return _department; } } class Department...{ private Person _manager; public Person getManager() { return _manager; } } //Để tìm manager của một người manager = john.getDepartment().getManager(); Luận văn tốt nghiệp cao học – Khóa 2005 - 2008 Học viên thực hiện: Nhiêu Lập Hòa 41 II.2.16 Inapproprite Intimacy - Cấu trúc thành phần riêng không hợp lý Một trong những ƣu điểm của hƣớng đối tƣợng là tính đóng nhằm hạn chế các truy cập vào các thành phần riêng của lớp. Tuy nhiên nó cũng đem lại một số hạn chế trong một vài trƣờng hợp, chẳng hạn là tốn nhiều thời gian hơn trong việc truy cập và can thiệp vào các thành phần này. Sử dụng Move Method và Move Field để chia tách thành các phần giảm bởi các bƣợc truung gian việc truy cập đến các thành phần riêng tƣ. II.2.17 Alternative Classes with Different Interfaces - Đặc tả lớp không rõ ràng Với các phƣơng thức tuy có nội dung thực hiện các việc nhƣ nhau nhƣng về mặt ý nghĩa khác nhau thì nên sử dựng Rename Method để xác định tên sao cho dễ hiểu và thể hiện đƣợc mục đích của phƣơng thức. Khi cần thiết có thể áp dụng Move Method để dịch chuyển các hành vi xử lý tạo tính đồng nhất trong các giao thức thực hiện Ví dụ: Nếu tên phƣơng thức không rõ ràng và đặc tả đƣợc mục đích của của phƣơng thức thì nên sử dụng Rename Method để đổi tên cho hợp lý. II.2.18 Incomplete Library Class – Sử dụng thƣ viện lớp chƣa đƣợc hòan chỉnh Nhƣ ta biết thì đặc tính nổi bật của hƣớng đối tƣợng là khả năng tái sử dụng. Thông thƣờng trong lập trình hƣớng đối tƣợng, các lập trình viên thƣờng sử dụng các tập lớp đối tƣợng đã đƣợc các nhà phát triển thiết kế sẵn. Tuy nhiên không phải bao giờ các lớp đối tƣợng này cũng đã đƣợc thiết kế chính xác và hòan chỉnh. Nhƣ vậy một số hạn chế có thể gặp phải trong quá trình tái sử dụng các lớp đối tƣợng - Nếu thƣ viện lớp thiếu cần bổ sung 1 –2 phƣơng thức cần thiết, sử dụng Introduce Foregin Method để bổ sung - Với số lƣợng cần bổ sung nhiều hơn, chúng ta sử dụng Introduce Local Extension Ví dụ 1: Khi cần thêm một phƣơng thức vào một lớp phục vụ nhƣng không thể chỉnh sửa lớp -> Sử dụng Introduce Foregin Method để tạo một phƣơng thức ở lớp khách hàng có đối số truyền vào là một thực thể của lớp phục vụ. Date newStart = new Date (previousEnd.getYear(), previousEnd.getMonth(), previousEnd.getDate() + 1); Date newStart = nextDay(previousEnd); private static Date nextDay(Date arg) { return new Date (arg.getYear(),arg.getMonth(), arg.getDate() + 1); } Luận văn tốt nghiệp cao học – Khóa 2005 - 2008 Học viên thực hiện: Nhiêu Lập Hòa 42 Ví dụ 2: Khi cần thêm một vài phƣơng thức vào một lớp nhƣng không thể chỉnh sửa lớp -> Sử dụng Introduce Local Extension để định nghĩa một mới mà nó chứa các phƣơng thức bổ sung. Lớp mở rộng này sẽ là lớp con hoặv lớp bao của lớp ban đầu. II.2.19 Data Class – Lớp dữ liệu độc lập Với những lớp mà nội dung chỉ gồm các thuộc tính cùng các phƣơng thức truy vấn và thiết lập (getting & setting) giá trị trên các thuộc tính đó mà không có gì khác. - Sử dụng Encapsulate Field để chuyển thuộc tính chung (public) thành riêng (private) và cung cấp các phƣơng thức trên thuộc tính đó - Một phƣơng thức trả về một tập hợp, sử dụng Encapsulate Collection để cải biến kết quả trả về là một tập hợp có thuộc tính chỉ đọc và tạo thêm một số phƣơng thức hỗ trợ thêm mới hoặc hủy. - Với những thuộc tính mà không có sự thay đổi, nên sử dụng Remove Setting Method để hủy bỏ các phƣơng thức cập nhật giá trị trên thuộc tính đó. - Có thể sử dụng thêm các kỹ thuật Move Method, Extract Method và Hide Method trên các phƣơng thức truy vấn và thiết lập để tái cấu trúc lớp đƣợc hợp lý hơn Ví dụ 1: Sử dụng Encapsulate Field để chuyển thuộc tính chung (public) thành riêng (private) và cung cấp các phƣơng thức trên thuộc tính đó public String _name; private String _name; public String getName() {return _name;} public void setName(String arg) {_name = arg;} Ví dụ 2: Sử dụng Remove Setting Method để xóa bỏ phƣơng thức cập nhật trên các thuộc tính bất biến Luận văn tốt nghiệp cao học – Khóa 2005 - 2008 Học viên thực hiện: Nhiêu Lập Hòa 43 II.2.20 Refused Bequest – Quan hệ kế thừa không hợp lý/logic Một lớp con kế thừa một số thuộc tính và phƣơng thức từ lớp cha không cần thiết. Điều này đồng nghĩa với việc xác định thứ tự các lớp trong quan hệ kế thừa có khả năng không hợp lý. - Nếu một thuộc tính hoặc hành vi xử lý (phƣơng thức) trên lớp cha chỉ liên quan đến một số lớp con, sử dụng Push Down Method & Push Down Field chuyển nó đến các lớp con này. Lớp cha chỉ nên nắm giữ các thành phần chung. - Một lớp con chỉ sử dụng một phần giao diện của lớp cha hoặc không thừa kế dữ liệu, sử dụng Replace Inheritance with Delegation để tạo một thuộc tính mới và điều chỉnh các phƣơng thức ủy quyền đến lớp cha Ví dụ 1: Dùng Push Down Method để dịch chuyển một phƣơng thức từ lớp cha về lớp con có sử dụng đến. Ví dụ 2: Sử dụng Replace Inheritance with Delegation để tái cấu trúc một quan hệ kế thừa II.2.21 Comments – Chú thích không cần thiết Chú thích trong mã nguồn là một việc nên làm để mã nguồn dễ hiểu, tuy nhiên việc lạm dụng chúng là không nên. Một chƣơng trình có quá nhiều chú thích không cần thiết đôi lúc cũng tạo sự khó khăn và tăng chi phí trong việc dò tìm và cập nhật lỗi. Luận văn tốt nghiệp cao học – Khóa 2005 - 2008 Học viên thực hiện: Nhiêu Lập Hòa 44 II.3 NHẬN XÉT VÀ KẾT LUẬN Trong chƣơng này chúng ta tập trung tìm hiểu về các mã xấu thƣờng gặp trong quá trình thiết kế và lập trình phần mềm. Nhìn chung các mã xấu này rất đa dạng và hầu nhƣ có khả năng tồn tại ở khắp mọi nơi trong thân chƣơng trình: - Thiết kế lớp đối tƣợng không chặt chẽ - Đặt trên biến không rõ ràng - Khai báo và sử dụng trƣờng dữ liệu (thuộc tính lớp) không hợp lý - Nội dung phƣơng thức dài dòng và phức tạp - Khai báo và sử dụng biến trung gian không cần thiết - V.v Vấn đề nhận biết và chỉnh sửa các mã xấu này là không đơn giản, mỗi lỗi đều có những dấu hiệu cũng nhƣ giải pháp riêng để nhận biết và cải tiến. Và tất cả các giải pháp chỉnh sửa và cải tiến đều đều dựa trên kỹ thuật tái cấu trúc mã nguồn đã đƣợc trình bày ở trên. Dựa vào kỹ thuật này, chỉ qua một vài bƣớc biến đổi, chƣơng trình sẽ trở nên rõ ràng, dễ hiểu và xa hơn là làm tăng tính khả biến của chƣơng trình trong tƣơng lai. Trong phạm vi của đề tài, phần trình bày tập trung vào các mã xấu cơ bản nhất, đã đƣợc kiểm nghiệm thực tế trên một số ngôn ngữ lập trình và bƣớc đầu có những kết quả nhất định. Và ở phần tiếp theo, chúng ta sẽ hiện thực việc ứng dụng kỹ thuật refactoring để triển khai việc dò tìm và cải tiến mã xấu trong một ngôn ngữ lập trình cụ thể (C#) trong môi trƣờng phát triển ứng dụng Visual Studio .NET Luận văn tốt nghiệp cao học – Khóa 2005 - 2008 Học viên thực hiện: Nhiêu Lập Hòa 45 CHƢƠNG III: NỀN TẢNG .NET VÀ NGÔN NGỮ LẬP TRÌNH C# III.1 TỔNG QUAN VỀ NỀN TẢNG .NET Trong thế giới điện toán, những cải tiến và thay đổi vẫn thƣờng xuyên xảy ra. Đây là những biến đổi tất yếu và có tác động thúc đẩy sự phát triển. Một thách thức đối với bất kì nhà lập trình hay những công việc chuyên về CNTT nào là theo kịp những biến đổi liên tục và những sự phát triển trong công nghệ. Sự khởi xƣớng ý tƣởng .NET là một bƣớc đột phá mới của Microsoft. Đó là nền tảng cho các dịch vụ Web XML, là thế hệ phần mềm kế tiếp kết nối thế giới thông tin, các thiết bị và tất cả mọi ngƣời trong một thể thống nhất. Nền tảng .NET cho phép tạo ra và sử dụng các ứng dụng, các quá trình và các website dựa trên XML nhƣ những dịch vụ chia xẻ, kết nối thông tin và hoạt động cùng nhau trên bất cứ nền tảng hay thiết bị thông minh nào, nhằm mục đích cung cấp những giải pháp theo yêu cầu cho các tổ chức và cá nhân riêng biệt. III.1.1 Định nghĩa .NET Về tổng thể .NET có nghĩa một nền tảng hơn là một sản phẩm đơn lẻ, cho nên cách định nghĩa nó có thể đa dạng, có phần hơi khó hiểu và mơ hồ. Một cách đơn giản .

Các file đính kèm theo tài liệu này:

  • pdfỨng dụng kỹ thuật tái cấu trúc mã nguồn để triển khai dò tìm và cải tiến các đoạn mã xấu trong chương trình c#.pdf