Đặc biệt hóa và tổng quát hóa
Lớp và các thể hiện của lớp tức đối tượng tuy không tồn tại trong cùng một khối, nhưng chúng tồn tại trong một mạng lưới sự phụ thuộc và quan hệ lẫn nhau. Ví dụ như con người và xã hội động vật cùng sống trong một thế giới có quan hệ loài với nhau.
Quan hệ là một (is-a) là một sự đặc biệt hóa. Khi chúng ta nói rằng mèo là một loại động vật có vú, có nghĩa là chúng ta đã nói rằng mèo là một trường hợp đặc biệt của loại động vật có vú. Nó có tất cả các đặc tính của bất cứ động vật có vú nào (như sinh ra con, có sữa mẹ và có lông…). Tuy nhiên, mèo có thêm các đặc tính riêng được xác định trong họ nhà mèo mà các họ động vật có vú khác không có được. Chó cũng là loại động vật có vú, chó cũng có tất cả các thuộc tính của động vật có vú, và riêng nó còn có thêm các thuộc tính riêng xác định họ loài chó mà khác với các thuộc tính đặc biệt của loài khác, ví dụ như mèo chẳng hạn.
Quan hệ đặc biệt hóa và tổng quát hóa là hai mối quan hệ đối ngẫu và phân cấp với nhau.
Chúng có quan hệ đối ngẫu vì đặc biệt được xem như là mặt ngược lại của tổng quát. Do đó, loài chó và mèo là trường hợp đặc biệt của động vật có vú. Ngược lại động vật có vú là trường hợp tổng quát từ các loài chó và mèo.
Mối quan hệ là phân cấp bởi vì chúng ta tạo ra một cây quan hệ, trong đó các trường hợp đặc biệt là những nhánh của trường hợp tổng quát. Trong cây phân cấp này nếu di chuyển lên trên cùng ta sẽ được trường hợp tổng quát hóa, và ngược lại nếu di chuyển xuống ngược nhánh thì ta được trường hợp đặc biệt hóa. Ta có sơ đồ phân cấp minh họa cho loài chó, mèo và động vật có vú như trên:
Tương tự, khi chúng ta nói rằng ListBox và Button là những Window, ta phải chỉ ra những đặc tính và hành vi của những Window có trong cả hai lớp trên. Hay nói cách khác, Window là tổng quát hóa mô tả những thuộc tính của hai lớp ListBox và Button, trong khi đó mỗi trường hợp đặc biệt ListBox và Button sẽ có riêng những thuộc tính và hành vi đặc thù khác.
Thông thường lưu ý rằng khi hai lớp chia xẻ chức năng với nhau, các thành phần chung của chúng được trích ra và đưa vào lớp cha để chia xẻ.Điều này rất có lợi, vì nó cung cấp khả năng sử dụng lại các mã nguồn chung và dễ dàng duy trì mã nguồn.
Giả sử chúng ta bắt đầu tạo một loạt các lớp đối tượng theo hình vẽ như bên trên. Sau khi làm việc với RadioButton, CheckBox, và CommandButton một thời gian ta nhận thấy chúng chia xẻ nhiều thuộc tính và hành vi đặc biệt hơn Window nhưng lại khá tổng quát cho cả ba lớp này. Như vậy ta có thể chia các thuộc tính và hành vi thành một nhóm lớp cha
riêng lấy tên là Button. Sau đó ta sắp xếp lại cấu trúc kế thừa như hình vẽ dưới. Đây là ví dụ về cách tổng quát hóa được sử dụng để phát triển hướng đối tượng.
Sơ đồ trên vẽ lại quan hệ giữa các lớp. Cả hai lớp Button và ListBox được thừa kế từ lớp Window, trong đó Button có trường hợp đặc biệt là CheckBox và Command. Cuối cùng thì RadioButton được thừa kế từ CheckBox. Chúng ta cũng có thể nói rằng RadioButton là một CheckBox, và tiếp tục CheckBox là một Button, và cuối cùng Buttonlà Window.
Sự thiết kế trên không phải là duy nhất hay cách tốt nhất để tổ chức những đối tượng, nhưng đó là khởi điểm để hiểu về cách quan hệ giữa đối tượng với các đối tượng khác.
Sự kế thừa
Trong ngôn ngữ C#, quan hệ đặc biệt hóa được thực thi bằng cách sử dụng sự kế thừa. Đây không phải là cách duy nhất để thực thi đặc biệt hóa, nhưng nó là cách chung nhất và tự nhiên nhất để thực thi quan hệ này.
Trong mô hình trước, ta có thể nói ListBox kế thừa hay được dẫn xuất từ Window. Window được xem như là lớp cha, và ListBox được xem như là lớp con. Như vậy, ListBox thừa kế tất cả các thuộc tính và hành vi từ lớp Window và thêm những phần đặc biệt riêng để xác nhận ListBox.
Cài đặt sự thừa kế trong C#
Trong ngôn ngữ C# để tạo một lớp con thừa kế từ một lớp ta thêm dấu hai chấm vào sau tên lớp con và trước tên lớp cha:
public class ListBox : Window
Đoạn lệnh trên khai báo một lớp mới tên là ListBox, lớp này thừa kế từ lớp Window. Dấu hai chấm có thể được đọc như là “thừa kế từ”.
Lớp con sẽ kế thừa tất cả các thành viên của lớp cha, bao gồm tất cả các phương thức và biến thành viên của lớp cha. Lớp con có thể nhìn thấy và thi hành các phương thức của lớp cha. Lớp con cũng có thể tạo một phương thức mới với từ khóa new.
Ví dụ dưới đây minh họa việc tạo và sử dụng các lớp cha và lớp con.
using System; public class Window { public Window(int top, int left) { this.top = top; this.left = left; } // mô phỏng vẽ cửa sổ public void DrawWindow() { Console.WriteLine("Drawing Window at {0}, {1}", top, left); } // Có hai biến thành viên private do đó hai // biến này sẽ không thấy bên trong lớp con private int top; private int left; } // ListBox thừa kế từ Window public class ListBox : Window { // Khởidựng có tham số public ListBox(int top, int left, string theContents) : base(top, left) // gọi khởi dựng của lớp cha { mListBoxContents = theContents; } public new void DrawWindow() { base.DrawWindow(); Console.WriteLine("ListBox write: {0}", mListBoxContents); } // biến thành viên private private string mListBoxContents; } public class Tester { public static void Main() { // tạo đối tượng cho lớp cha Window w = new Window(5, 10); w.DrawWindow(); // tạo đối tượng cho lớp con ListBox lb = new ListBox(20, 10, "Hello world!"); lb.DrawWindow(); Console.ReadLine(); } }
Ví dụ trên bắt đầu với việc khai báo một lớp cha tên Window. Lớp này thực thi một phương thức khởi dựng và một phương thức đơn giản DrawWindow(). Lớp này có hai biến thành viên có quyền truy xuất private là top và left, hai biến này do khai báo là private nên chỉ sử dụng bên trong của lớp Window, các lớp con sẽ không truy cập được.