Thứ Sáu, 29 tháng 6, 2012

Khái niệm Coupling và Cohesion trong OOP

Khả năng đóng gói dữ liệu (Data encapsulation), kế thừa (Inheritance) và đa hình (Polymorphism) là nền tảng của lập trình hướng đối tượng, nhưng đơn thuần vận dụng cả ba khái niệm trên không ngẫu nhiên mang lại một thiết kế chương trình tốt. Một chương trình thiết kế theo hướng đối tượng đòi hỏi chia nhỏ bài toán thành các phần, sao cho chúng vừa có đặc tính cố kết (giao tiếp giữa các phần)  lại vừa có khả năng tách biệt riêng rẽ để thay đổi, kiểm tra mà không ảnh hưổng đến thành phần khác. Điều này rất quan trọng trong sự phát triển, tiến hóa của một sản phẩm phần mềm.
Coupling và Cohesion là hai khái niệm trong OOP đặc trưng cho vấn đề này. Coupling : là sự phụ thuộc mô tả mức độ mà theo đó một phương thức (method) hay một kiểu (type) phụ thuộc vào phương thức khác hoặc kiểu khác để thực hiện hành vi của nó. Lớp A được gọi là phụ thuộc ở mức độ cao vào lớp B nếu một sự thay đổi trong lớp B là có khả năng gây ra một thay đổi trong hành vi của lớp A. Coupling còn được hiểu là sự phụ thuộc – dependence.
Các kiểu Coupling được chia làm 2 nhóm : Loosely Coupling và Tightly Coupling. Một chương trình có thiết kế tốt phải đảm bảo được đặc tính Loosely Coupling.
Independent/No Coupling : Nếu 2 lớp hoặc phương thức không chia sẻ bất kỳ dữ liệu nào thì chúng là độc lập (independent). Việc lớp này sử dụng lớp kia một cách đơn giản không có nghĩa là 2 lớp phụ thuộc vào nhau. Ví dụ
    class A
    {
        public void Foo()
        {
            B b = new B();
            b.Bar();
        }
    }
    class B
    {
        public void Bar()
        {
            //Thay doi o day khong anh huong toi hanh vi cua A
        }
    }
Data Coupling : Khi lớp A truyền một giá trị cần thiết cho B, thì A và B được gọi là ràng buộc về dữ liệu (data-coupled).  Đây là kiểu Coupling phổ biến thường được áp dụng. Ví dụ
    class C
    {
        public void Foo()
        {
            string mes = "Hello OOP";
            D d = new D();
            d.Bar(mes);
        }
    }
    class D
    {
        public void Bar(string mes)
        {
            Console.WriteLine(mes);
        }
    }
Stamp Coupling : A truyền cho B kiểu tham chiếu hoặc toàn bộ cấu trúc dữ liệu thay vì chỉ truyền cho B trường dữ liệu cần thiết, do vậy nếu bất kỳ trường hay phương thức nào trong A thay đổi có thể ảnh hưởng tới xử lý của B, hoặc B có thể thay đổi A.
    class G
    {
        public void Foo()
        {
            K k = new K();
            k.Bar(this);
        }
        int field1;
        public int Field1
        {
            get { return field1; }
            set { field1 = value; }
        }
        int field2;
        public int Field2
        {
            get { return field2; }
            set { field2 = value; }
        }
    }
    class K
    {
        public void Bar(G myG)
        {
            //Co the anh huong len myG.Field1 va myG.Field2
        }
    }
Nhận xét : Stamp Coupling khiến chương trình trở lên rối và tạo ra nhiều bug tiềm ẩn, nên tránh truyền toàn bộ cấu trúc hoặc kiểu cho phương thức nếu phương thức đó chỉ sử dụng một phần của cấu trúc.
Tramp Coupling : Các tham số được truyền không đổi qua nhiều phương thức khác nhau cho đến khi chính thức được sử dụng
    class TransactionService
    {
        void CalculateOrder(string custname)
        {
            FetchListProduct(custname);
        }
        void FetchListProduct(string custname)
        {
            FetchAccountInfo(custname);
        }
        void FetchAccountInfo(string custname)
        {
            //Su dung custname o day
        }
    }
Nhận xét : Có thể loại bỏ Tramp Coupling bằng cách đặt custname là một trường trong lớp TransactionService, việc truyền một tham số cho phương thức luôn ngụ ý rằng phương thức đó sử dụng tham số này, do vậy lập trình viên phải tránh viết các phương thức có các tham số đầu vào không có mục đích.
Control Coupling : Nếu logic của lớp B được điều khiển bởi một tham số cho bởi lớp A, thì 2 lớp A và B được gọi là control-coupled. Ở đây điều kiện tiên quyết là A chỉ định logic của B, B không có quyền tự quyết định.
    class AirConditioner
    {
        const int NORMAL_TEMP = 20;
        public void ChangeTemperature()
        {
            int temp = RoomThermometer.RoomTemp;
            if(temp < NORMAL_TEMP)
            {
                IncreaseTemp();
            }
            else
            {
                DecreaseTemp();
            }
        }

        private void DecreaseTemp()
        {
            //throw new NotImplementedException();
        }

        private void IncreaseTemp()
        {
          //throw new NotImplementedException();
        }
    }
    class RoomThermometer
    {
        static int roomTemp;
        public static int RoomTemp
        {
            get { return GetRoomTemp(); }
            set { roomTemp = value; }
        }
        public static int GetRoomTemp()
        {
            Random rand = new Random();
            roomTemp = rand.Next(0, 50);
            return roomTemp;
        }
    }
Nhận xét:  đây là kiểu Coupling được sử dụng phổ biến trong lập trình.
External Coupling : Nếu 2 lớp A và B tương tác thông qua một non-native object (file, CSDL), thì 2 lớp đó được gọi là external coupled. Yếu điểm ở đây là khó theo dõi các trạng thái giao tiếp, debug gặp khó khăn.
Common Coupling : Nếu 2 lớp A và B là phụ thuộc vào cùng một dữ liệu tĩnh (static data), thì 2 lớp đó được gọi là common coupled.
Content Coupling : Nếu 2 lớp A và B thay đổi trực tiếp trạng thái nội tại của lớp kia, dẫn tới thay đổi hành vi của nó thì được gọi là content coupling

Không có nhận xét nào: