Thứ Tư, ngày 04 tháng 8 năm 2010

// // 2 comments

Sự Khác Biệt Giữa Lớp Trừu Tượng Abstract Và Giao Diện Interface

    Đối với lập trình C# hay lập trình hướng đối tượng nói chung, vấn đề kế thừa sẽ đem lại nhiều phiền toái nếu bạn chưa nắm rõ về vấn đề này. Vậy thì sự khác biệt giữa lớp abstract và interface ở chỗ nào?

    Một lớp trừu tượng thì không có thể hiện nghĩa là ta không thể khởi tạo nó bằng toán tử new, và một phương thức trong nó là abstract thì chỉ được đưa ra định nghĩa (khai báo) mà không được thực thi và nó sẽ được overriden lại trong các lớp con kế thừa. Và trong lớp mà tồn tại phương thức abstract thì lớp đó cũng được định nghĩa abstract.

    Đối với giao diện thì khác hoàn toàn với lớp trừu tượng, nó định nghĩa một cách cứng nhắc các phương thức và thuộc tính trong chúng nghĩa là không cho phép ta thực thi bất kỳ một đoạn mã nào. Và tất cả các thành viên trong nó đều được định nghĩa công khai (public). Một cách tổng quan về giao diện: Giao diện là ràng buộc, giao ước đảm bảo cho các lớp hay các cấu trúc sẽ thực hiện một điều gì đó. Khi một lớp thực thi một giao diện, thì lớp này báo cho các thành phần client biết rằng lớp này có hỗ trợ các phương thức, thuộc tính, sự kiện và các chỉ mục khai báo trong giao diện.

    Một số lưu ý khi sử dụng abstract hay interface

Một class chỉ có thể kế thừa từ một abstract class, nhưng có thể kế thừa nhiều interface.
Trong Interface chỉ có thể khai báo các fields, methods, mà không được hiện thực nó. Còn đối với abstract thì dùng các biến, hiện thực cách methods.
Các fields, methods trong interace đều là public và bắt buộc các class kế thừa phải cài đặt nó (abstract). Trong abstract class thì có các fields, methods có thể là private, internal, public, protected và có thể là abstract hoặc non-abstract.
Interface dùng để gom các hành động cần được hiện thực , các khả năng của một đối tượng, còn abstract class cho các lớp thừa kế cùng 1 loại, tính chất hay trạng thái.
Abstract class có tốc độ thực thi nhanh hơn interface.
Thêm 1 tính năng mới vào interface sẽ phá vỡ toàn bộ các lớp hiện thực, còn abstract thì không.

    Ví dụ về interface, các thành viên của interface phải được thực thi trong các lớp mà kế thừa từ nó

    public interface IPlayer
    {
        string Name
        {
            get;
            set;
        }
        Player Actor
        {
            get;
            set;
        }
        
        Bitmap Image
        {
            get;
            set;
        }
    }

    Ví dụ dưới đây khai báo một lớp abstract có các thành viên trong nó là abstract và non-abstract, và được thực thi trong lớp con là Faculty

    abstract class Employee
    {
        protected string m_str_department;
        protected double m_db_salary;
        protected int m_i_dateHired;

        public string Department
        {
            get { return m_str_department; }
            set { m_str_department = value; }
        }

        public double Salary
        {
            get { return m_db_salary; }
            set { m_db_salary = value; }
        }

        public int DateHired
        {
            get { return m_i_dateHired; }
            set { m_i_dateHired = value; }
        }

        public override string ToString()
        {
            return "Employee: " + m_str_name
            + "\nEmail: " + m_str_email;
        }

        public abstract double CalculateBonus();
        public abstract int CalculateVacation();
    }

    class Faculty : Employee
    {
        string m_str_rank;
        double m_db_hours;

        public override double CalculateBonus()
        {
            return 1000 + 0.05 * m_db_salary;
        }

        public override int CalculateVacation()
        {
            if (m_i_dateHired > 3)
            {
                if (m_str_rank == "Senior Lecture")
                    return 6;
                return 5;
            }
            if (m_str_rank == "Senior Lecture")
                return 5;
            return 4; 
        }
    }

2 comments:

bboyjeans nói...

Bạn cho mình hỏi là khi nào thì sử dụng abtract class, nó có lợi hơn một lớp cha bình thường ở chỗ nào. Cám ơn bạn

Vũ Nhữ Bảo nói...

@bboyjeans
Chào bạn,

Bản thân một lớp (cha) bình thường là được cụ thể hóa việc tạo lập nên nó theo một điều kiện nhất định, môi trường đã là xác định.

Còn đối với lớp trừu tượng được thiết kế với các hành vi, đặc điểm tổng quát mang ý nghĩa chung chung nên bản thân nó là chưa có ý nghĩa hoặc không đầy đủ. Như vậy, cần có một điều kiện xác định để viết mã hoặc khởi tạo lớp "dẫn xuất".

Để thấy được lợi điểm của abtract class, bạn hãy xem ví dụ sau:

Một ví dụ đơn giản như sau: Lớp "HinhHoc" được định nghĩa trừu tượng với 1 phương thức là TinhDienTich(). Nhưng bản thân lớp HinhHoc này chưa xác định cụ thể các đặc tính của nó nên ph.thức TinhDienTich() chỉ có thể được định nghĩa là trừu tượng. Và nó phụ thuộc vào lớp dẫn xuất của nó là: "HinhVuong, HinhTrong, ..."

Một ví dụ khác kinh điển hơn đó là các ứng dụng có thể chạy trên nhiều hệ điều hành khác nhau như: Firefox, Eclipse, MySQL, ... chạy được trên Windows và họ nhà UNIX. Và một số hoặc chỉ có thể chạy trên Win hoặc một số chỉ chạy trên *NIX.