Một cách để xem xét sự gắn kết trong điều khoản của OO là nếu các phương thức trong lớp đang sử dụng bất kỳ thuộc tính riêng tư nào. Sử dụng các số liệu như LCOM4 (Thiếu phương pháp gắn kết), như được chỉ ra bởi gnat trong câu trả lời này ở đây , bạn có thể xác định các lớp có thể được cấu trúc lại. Lý do bạn muốn cấu trúc lại các phương thức hoặc các lớp để gắn kết hơn là vì nó làm cho thiết kế mã đơn giản hơn cho người khác sử dụng nó . Tin tôi đi; hầu hết các lập trình viên công nghệ và bảo trì sẽ yêu bạn khi bạn khắc phục những vấn đề này.
Bạn có thể sử dụng các công cụ trong quy trình xây dựng của mình như Sonar để xác định độ gắn kết thấp trong cơ sở mã. Có một vài trường hợp rất phổ biến mà tôi có thể nghĩ đến khi các phương thức có "tính cố kết" thấp :
Trường hợp 1: Phương thức hoàn toàn không liên quan đến lớp
Hãy xem xét ví dụ sau:
public class Food {
private int _foodValue = 10;
public void Eat() {
_foodValue -= 1;
}
public void Replenish() {
_foodValue += 1;
}
public void Discharge() {
Console.WriteLine("Nnngghhh!");
}
}
Một trong những phương thức, Discharge()
thiếu sự gắn kết vì nó không chạm vào bất kỳ thành viên riêng nào của lớp. Trong trường hợp này chỉ có một thành viên tư nhân : _foodValue
. Nếu nó không làm gì với các lớp bên trong, thì nó có thực sự thuộc về nó không? Phương thức có thể được chuyển sang một lớp khác có thể được đặt tên, vd FoodDischarger
.
// Non-cohesive function extracted to another class, which can
// be potentially reused in other contexts
public FoodDischarger {
public void Discharge() {
Console.WriteLine("Nnngghhh!");
}
}
Trong khi bạn thực hiện nó trong Javascript, vì các hàm là các đối tượng hạng nhất, nên việc xả có thể là một hàm miễn phí:
function Food() {
this._foodValue = 10;
}
Food.prototype.eat = function() {
this._foodValue -= 1;
};
Food.prototype.replenish = function() {
this._foodValue += 1;
};
// This
Food.prototype.discharge = function() {
console.log('Nnngghhh!');
};
// can easily be refactored to:
var discharge = function() {
console.log('Nnngghhh!');
};
// making it easily reusable without creating a class
Trường hợp 2: Lớp tiện ích
Đây thực sự là một trường hợp phổ biến phá vỡ sự gắn kết. Mọi người đều yêu thích các lớp tiện ích, nhưng những điều này thường chỉ ra các lỗi thiết kế và hầu hết thời gian làm cho codebase khó bảo trì hơn (vì sự phụ thuộc cao liên quan đến các lớp tiện ích). Hãy xem xét các lớp sau:
public class Food {
public int FoodValue { get; set; }
}
public static class FoodHelper {
public static void EatFood(Food food) {
food.FoodValue -= 1;
}
public static void ReplenishFood(Food food) {
food.FoodValue += 1;
}
}
Ở đây chúng ta có thể thấy rằng lớp tiện ích cần truy cập vào một thuộc tính trong lớp Food
. Các phương thức trong lớp tiện ích hoàn toàn không có sự gắn kết nào trong trường hợp này bởi vì nó cần các tài nguyên bên ngoài để thực hiện công việc của nó. Trong trường hợp này, sẽ tốt hơn nếu có các phương thức trong lớp mà chúng hoạt động với chính nó (giống như trong trường hợp đầu tiên)?
Trường hợp 2b: Các đối tượng ẩn trong Lớp tiện ích
Có một trường hợp khác của các lớp tiện ích nơi có các đối tượng miền chưa được thực hiện. Phản ứng giật đầu tiên của một lập trình viên khi lập trình thao tác chuỗi là viết một lớp tiện ích cho nó. Giống như một ở đây xác nhận một vài biểu diễn chuỗi phổ biến:
public static class StringUtils {
public static bool ValidateZipCode(string zipcode) {
// validation logic
}
public static bool ValidatePhoneNumber(string phoneNumber) {
// validation logic
}
}
Điều mà hầu hết không nhận ra ở đây là mã zip, số điện thoại hoặc bất kỳ sự lặp lại chuỗi nào khác có thể là một đối tượng:
public class ZipCode {
private string _zipCode;
public bool Validates() {
// validation logic for _zipCode
}
}
public class PhoneNumber {
private string _phoneNumber;
public bool Validates() {
// validation logic for _phoneNumber
}
}
Khái niệm rằng bạn không nên "xử lý chuỗi" trực tiếp được chi tiết trong blog này bởi @codemonkeyism , nhưng có liên quan chặt chẽ đến sự gắn kết bởi vì cách các lập trình viên sử dụng chuỗi bằng cách đưa logic vào các lớp tiện ích.