Ý nghĩa của 'sự gắn kết cao' là gì?


28

Tôi là một sinh viên gần đây đã gia nhập một công ty phát triển phần mềm như một thực tập sinh. Trở lại trường đại học, một trong những giáo sư của tôi từng nói rằng chúng ta phải cố gắng để đạt được "Sự liên kết thấp và sự gắn kết cao".

Tôi hiểu ý nghĩa của khớp nối thấp. Điều này có nghĩa là giữ riêng mã của các thành phần riêng biệt, để thay đổi ở một nơi không phá vỡ mã ở nơi khác.

Nhưng những gì có nghĩa là sự gắn kết cao. Nếu nó có nghĩa là tích hợp tốt các phần khác nhau của cùng một thành phần với nhau, tôi không hiểu làm thế nào điều đó trở nên thuận lợi.

Có nghĩa là gì bởi sự gắn kết cao? Một ví dụ có thể được giải thích để hiểu lợi ích của nó?



1
Bài viết trên wikipedia không trả lời đầy đủ câu hỏi của bạn? vi.wikipedia.org/wiki/Cohesion_(computer_science)
Eoin Carroll

Có một bài viết hay về điều này tại: msdn.microsoft.com/en-us/magazine/cc947917.aspx
NoChance

1
@EoinCarroll: Thật không may, bài viết wikipedia tại thời điểm hiện tại không đưa ra bất kỳ ví dụ cụ thể tốt nào mà các lập trình viên mới có thể làm việc với. Lý thuyết là tốt và tất cả, nhưng không thực sự gắn bó cho đến khi bạn thực hiện những sai lầm xung quanh sự gắn kết thấp. Sự gắn kết cao là một trong những chủ đề khiến tôi mất nhiều năm lập trình để hiểu đầy đủ lý do tại sao nó quan trọng và làm thế nào để đạt được nó.
Spoike

Tôi chưa bao giờ thực sự hiểu về Sự gắn kết cho đến khi tôi đọc Clean Code. Bạn cũng nên.
Sebastian Redl

Câu trả lời:


25

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.


Bây giờ nếu chỉ chúng ta có thể có được các ORM của mình để xử lý đúng các lớp ZipCode và PhoneNumber tùy chỉnh của chúng tôi: |
Pete

+1 ví dụ hay. Để biết điểm cuối cùng, hãy xem thêm sourcemaking.com/refactoring/primitive-obsession
AlexFoxGill

@Pete ORM của bạn sẽ xử lý nó nếu bạn cung cấp các toán tử chuyển đổi từ chuỗi sang loại được xác định của bạn (ZipCode, PhoneNumber): msdn.microsoft.com/en-us/l
Marcus

9

Sự gắn kết cao có nghĩa là giữ những thứ tương tự và liên quan với nhau, để ghép nối hoặc hợp nhất các phần có chung nội dung, chức năng, lý do hoặc mục tiêu . Nói cách khác, ví dụ, sự gắn kết thấp có thể có nghĩa là một thực thể chức năng / lớp / mã phục vụ nhiều mục đích hơn là " đến điểm ". Một trong những ý tưởng mang theo là làm một việc và làm tốt . Những người khác có thể bao gồm một thực tế rõ ràng là bạn không sao chép chức năng tương tự ở nhiều nơi. Điều này cũng cải thiện địa phương của các cơ sở mã, một số loại vật được tìm thấy ở một nơi nào đó (tập tin, lớp, tập hợp các hàm, ...) chứ không phải được rải rác xung quanh.

Ví dụ, xem xét một lớp phục vụ hai hoặc ba mục đích: Tải / lưu trữ tài nguyên (ví dụ: một tệp), sau đó phân tích và hiển thị nội dung. Một lớp như vậy có độ gắn kết thấp vì nó quản lý ít nhất hai nhiệm vụ riêng biệt hoàn toàn không liên quan (tệp I / O, phân tích và hiển thị). Một thiết kế gắn kết cao có thể sử dụng các lớp riêng biệt để tải và lưu trữ tài nguyên, phân tích nó và sau đó hiển thị nó.

Mặt khác, khớp nối thấp nhằm mục đích giữ những thứ riêng biệt tách biệt - để chúng tương tác với nhau ít nhất có thể, sau đó giảm độ phức tạp và đơn giản hóa thiết kế.


7

Nó có nghĩa là các bộ phận của một đối tượng nhất định có liên quan chặt chẽ đến chức năng của đối tượng. Điều này có nghĩa là có rất ít hoặc không có chất thải trong đối tượng về chức năng hoặc trách nhiệm. Điều này đến lượt nó có thể cải thiện sự hiểu biết về những gì đối tượng trong câu hỏi được cho là được sử dụng cho.


không phải nó cũng áp dụng cho một cấp độ cao hơn so với chỉ các đối tượng? ví dụ: nhóm objcts / hàm liên quan đến một tác vụ trong không gian tên?
stijn
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.