Architecture, Design Pattern

Low Coupling và High Cohesion là gì?

Low couplinghigh cohesion là 2 thuộc tính đi cùng với nhau như là mục tiêu cần đạt được trong thiết kế, trong bài viết này, cùng tìm hiểu xem chúng là gì, làm sao để đạt được và tránh các lỗi liên quan đến coupling và cohesion khi thiết kế phần mềm.

Coupling

Low coupling, loose coupling hay high couplingtight coupling, ắt hẳn ai trong chúng ta khi học về các nguyên lý lập trình căn bản đều biết về khái niệm coupling này. Coupling đề cập đến vấn đề phụ thuộc lẫn nhau giữa các component. Low coupling, loose coupling có nghĩa là các component ít phụ thuộc vào nhau, sự thay đổi trong component này ít khi, hoặc không ảnh hưởng đến component kia. Ngược lại, high coupling và tight coupling cho thấy các component phụ thuộc nhiều vào nhau, khi thay đổi 1 component thì các component kia đều bị ảnh hưởng và có khả năng phải thay đổi theo. Tất nhiên, low coupling là mục tiêu chúng ta cần hướng đến để đảm bảo cho hệ thống ít bị ảnh hưởng khi có thay đổi và do đó, tăng tốc độ thực hiện công việc và bảo trì.

Thêm một ví dụ nữa về coupling

Trong câu hỏi trên StackOverflow có một câu trả lời cho thấy một mô tả hài hước nhưng khá chính xác và rõ ràng về những gì 1: 1 coupling là:

iPod là một ví dụ tốt về tight coupling: một khi pin chết bạn cũng có thể mua một chiếc iPod mới vì pin được hàn cố định và sẽ không bị lỏng, do đó thay thế rất tốn kém. Một cái máy có tính loosely coupled sẽ cho phép thay đổi pin dễ dàng.

iPods are a good example of tight coupling: once the battery dies you might as well buy a new iPod because the battery is soldered fixed and won’t come loose, thus making replacing very expensive. A loosely coupled player would allow effortlessly changing the battery.

Tương tự, 1: 1, trong phát triển phần mềm.

Chúng ta sẽ xem xét diagram dưới đây:

tight_coupling

Nếu chúng ta nhìn vào hình trên, nó cho chúng ta thấy một mối liên hệ giữa hai class được gọi là tight coupling. Class1 ở trên tạo ra các đối tượng của Class2 trực tiếp, và thậm chí là đi đến các biến thành viên và truy cập vào. Điều này làm cho nó rất phụ thuộc vào Class2. Điều gì sẽ xảy ra nếu chúng ta quyết định rằng chúng ta muốn thêm tham số thêm vào trong constructor của Class2 và đặt mặc định là private? Sau đó, chúng ta phải thay đổi mọi cách sử dụng Class2 ở mọi nơi. Không đẹp lắm, heh? Có thể là một cơn đau đầu rất lớn và là một trong những vấn đề đầu tiên trong thiết kế.

Dưới đây là ví dụ bằng code:

public class ClassA {

	private boolean attributeA;

	public int methodA() {
		if(attributeA) {
			return new ClassB().attributeB;
		}
		return -1;
	}

	public String getValue() {
		return new ClassB().getValue();
	}

}

public class ClassB {

	public int attributeB;

	public String getValue() {
		return "Heh?!?";
	}

}

Một số giải pháp

Dependency inversion

Ví dụ trong Java, ta sẽ thêm một interface. Đó là cách Class1 sẽ chỉ phụ thuộc vào interface đó, chứ không phải là implementation thực tế của Class2, do đó giảm thiểu sự phụ thuộc trực tiếp giữa 2 class với nhau.

Law of Demeter (Don’t talk to strangers!)

Lợi điểm của Law of Demeter là nó giúp hệ thống của chúng ta đứng vững trước những thay đổi bằng cách giảm coupling hay còn gọi là cách design loose coupling, mọi sự thay đổi sẽ là nhỏ nhất nếu có thể.

Cohesion

Còn high cohesion (trái ngược với nó là low cohesion) là gì? Khi nói đến cohesion chúng ta nghĩ đến nhiệm vụ của từng module. Nhiệm vụ của từng module càng rõ ràng và tách biệt thì cohesion càng cao (high cohesion), và đó là mục tiêu cần đạt tới khi thiết kế. Giải thích bằng code có lẽ sẽ không rõ ràng, hãy xem xét câu dưới đây:

Tại kỳ họp Quốc hội thứ năm, khi thảo luận về quản lý chất lượng vệ sinh an toàn thực phẩm có vị đại biểu Quốc hội đã ví việc có tới 5 bộ chịu trách nhiệm chính như vậy cũng giống như “nhiều sãi không ai đóng cửa chùa”.Bởi thế, làm rõ trách nhiệm của từng cơ quan quản lý Nhà nước về an toàn thực phẩm là một yêu cầu được nhấn mạnh khi xây dựng Dự Luật An toàn thực phẩm.

Nguồn: http://vneconomy.vn/thoi-su/an-toan-thuc-pham-bo-nao-trang-trai-bo-nao-ban-an-20100519102635734.htm

Nếu xem Dự Luật An toàn thực phẩm là một feature thì rõ ràng nó đã không đạt được tính high cohesion trong thiết kế vì nó phải dàn trải và phụ thuộc vào rất nhiều module (5 bộ, phòng ban) khác nhau. Do đó, khi cần chỉnh sửa bổ sung dự luật sẽ rất khó khăn vì phải sửa 1 lúc 5 module, mà bạn thấy đó, điều đó rõ ràng là rất khó. Nếu quy trách nhiệm xây dựng bộ luật này cho một bộ ban duy nhất thì sẽ giảm tính phức tạp và do đó, tăng tính cohesion. High cohesion thường đạt được nếu ta tuân thủ theo nguyên tắc đơn nhiệm (Single responsibility principle), mỗi module, khi đó chỉ đảm nhiệm một nhiệm vụ duy nhất, không hơn không kém, và không có chuyện 2 module cùng làm một nhiệm vụ, một tính năng.

Đến đây chắc ai cũng hiểu được rồi đúng không? Ít nhất là về mặt lý thuyết, hãy xem xét bảng sau trước khi mình đi vào các dẫn giải tiếp theo

COHESION COUPLING
Cohesion thể hiện mối quan hệ bên trong module. Coupling thể hiển sự liên kết giữa các modules.
Cohesion thể hiện sức mạnh liên kết giữa các chức năng Coupling thể hiện quan hệ phụ thuộc vào nhiều modules.
Cohesion đánh giá chất lượng mà một component / module tập trung vào một việc đơn lẻ. Coupling là đánh giá mức độ một component / module liên kết với mudule khác.

Cohesion Type

Sự gắn kết ngẫu nhiên (Coincidental cohesion) – *Bad* Very low cohesion

Sự gắn kết ngẫu nhiên là khi các phần của mô-đun được nhóm lại một cách tùy tiện; mối quan hệ duy nhất giữa các bộ phận là họ đã được nhóm lại với nhau. Một ví dụ của điều này có thể là một lớp Utility, nơi các phần tử hoàn toàn độc lập với nhau lại được group lại với nhau. Hình dưới minh họa rõ hơn về điều này

your-email-1

Sự gắn kết logic (Logical cohesion)

Sự gắn kết logic là khi các phần của mô-đun được nhóm lại vì chúng được phân loại logic để làm việc tương tự, ngay cả khi chúng khác nhau theo bản chất (ví dụ: nhóm tất cả các function xử lý đầu vào cho chuột và bàn phím).

Sự gắn kết thời gian (Temporal cohesion)

Sự gắn kết thời gian là khi các phần của mô-đun được nhóm lại khi chúng được xử lý – các phần được xử lý tại một thời điểm cụ thể trong quá trình thực hiện chương trình (ví dụ như một hàm được gọi là sau khi catch một exception khi close file, tạo ra một error log và thông báo đến người dùng).

Sự gắn kết theo thủ tục (Procedural cohesion)

Sự gắn kết theo thủ tục là khi các phần của mô-đun được nhóm lại bởi vì chúng luôn tuân theo một trình tự thực thi nhất định (ví dụ: một chức năng kiểm tra quyền truy cập tệp và sau đó mở tệp).

Sự gắn kết thông tin / trao đổi (Communicational/informational cohesion)

Sự liên kết giao tiếp là khi các phần của mô-đun được nhóm lại vì chúng hoạt động trên cùng một dữ liệu (ví dụ như mô-đun hoạt động trên cùng một bản ghi thông tin).

Sự liên kết tuần tự (Sequential cohesion)

Sự liên kết tuần tự là khi các phần của một mô-đun được nhóm lại bởi vì đầu ra từ một phần là đầu vào đến một phần khác giống như một dây chuyền lắp ráp (ví dụ như một chức năng đọc dữ liệu từ một tệp tin và xử lý dữ liệu).

Sự gắn kết về mặt chức năng (Functional cohesion) *Good*

Sự gắn kết chức năng là khi các phần của mô-đun được nhóm lại vì tất cả chúng đóng góp vào một nhiệm vụ duy nhất được định nghĩa rõ ràng của mô-đun (ví dụ, phân tích Lexical của một chuỗi XML).

Một cách nhìn sự gắn kết trong hướng đối tượng là nếu các method trong class đang sử dụng bất kỳ private attribute nào. Hình dưới là một ví dụ về cách một lớp gắn kết cao sẽ như thế nào.

high_cohesion
Sử dụng các số liệu (metric) như LCOM4 (Lack of Cohesive Methods) trong Sonar, và “Relational cohesion measurement” trong NDepend (trong .NET) và JDepend (trong Java), bạn có thể xác định các lớp cần được sắp xếp lại. Lý do bạn muốn các method hoặc các class refactor lại để được gắn kết hơn là nó làm cho việc thiết kế mã đơn giản hơn cho người khác sử dụng nó.

Có một vài trường hợp rất phổ biến mà tôi có thể nghĩ đến cho thấy tính low cohesion trong thiết kế:

Trường hợp 1: Method không liên quan gì đến class

Trường hợp 2: Utility Class

Trường hợp 3: Hidden objects and subclasses

Không phải lúc nào cũng có thể đạt được sự gắn kết tuyệt đối cao trong lớp, nhưng biết nó là gì và làm thế nào để nó góp phần vào việc thiết kế tổng thể có thể là một kiến thức tốt. Chúng ta không thể luôn luôn làm cho hoàn hảo, nhưng ít nhất chúng ta có thể phấn đấu để làm tốt hơn.

<span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>
public class HighCohesionExample {

	private String attributeA;
	private String attributeB;

	public int calculate() {
		return doCalculate(attributeA) + doCalculate(attributeB);
	}

	private int doCalculate(String attribute) {
		return attribute.length() * 3;
	}

	public String getValue() {
		return String.format("%s & %s", attributeA, attributeB);
	}

}

Một class càng nhỏ, có ít attribute và method thì càng dễ đạt được tính cohesion cao, vì khi đó, chúng dễ dàng được sử dụng trong class một cách có ý nghĩa và liên quan đến nhau.

Mở rộng

Một câu hỏi đặt ra là hai tính chất này có cùng lúc đạt được song song với nhau hay không? Hay sẽ loại trừ nhau? Ví dụ như phân rã thành càng nhiều module thì tính low coupling sẽ càng đạt được, ngược lại nếu như một tính năng bị phân rã thành nhiều module thì khi sửa đổi sẽ gặp nhiều khó khăn, do đó lại không đạt được tính high cohesion như mong đợi, và trường hợp ngược lại.

Để trả lời câu hỏi này, chúng ta cần tập trung vào scope (phạm vi) của chúng. Nhìn nhận một cách tổng thể từ ban đầu, low coupling liên quan đến sự phụ thuộc, hiểu biết giữa các module với nhau, còn high cohesion là việc gom nhóm các thành phần có liên quan về cùng một module để dễ dàng quản lý và bảo trì. Vì vậy, chúng không loại trừ nhau và chúng ta có thể đạt được cả 2.

Có trường hợp ta tổ chức các thành phần làm chung một chức năng vào cùng một module rồi, thì vẫn có khả năng xảy ra chuyện các module phụ thuộc chồng chéo lên nhau, khi đó ta gọi nó có tính high cohesion nhưng tight coupling.

Kết luận

Mục tiêu của thiết kế có thể túm gọn trong 3 từ: low coupling, high cohesion và encalsulation, bằng cách đảm bảo được 3 tính chất này, hệ thống sẽ có khả năng phát triển, mở rộng và bảo trì cực cao (tất nhiên, nói dễ hơn làm)

Đây là bài viết trong loạt bài viết về “Tổng quan về sự phát triển của kiến trúc phần mềm“. Đây là loạt bài viết chủ yếu giới thiệu về một số mô hình kiến trúc phần mềm hay nói đúng hơn là sự phát triển của chúng qua từng giai đoạn, qua đó giúp chúng ta có cái nhìn tổng quát, up-to-date và là roadmap để bắt đầu hành trình chinh phục (đào sâu) thế giới của những bản thiết kế với vai trò là những kỹ sư và kiến trúc sư phần mềm đam mê với nghề.

Bài viết được tham khảo từ:

High Cohesion, Loose Coupling

Tổng hợp bởi edwardthienhoang

6 thoughts on “Low Coupling và High Cohesion là gì?”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.