* 1. Lớp Trừu tượng (Abstract Classes)
- Lớp trừu tượng (Abstract Class) là lớp dùng để định nghĩa những thuộc tính và hành vi chung của những lớp khác..
Hay nói cách khác: Lớp trừu tượng là lớp dùng để khai báo thuộc tính và phương thức cho các lớp khác sử dụng.
(Lớp trừu tượng không cho phép khởi tạo tham số, chỉ khai báo mà thôi).
- Lớp trừu tượng được dùng như một lớp cha (base class) của các lớp có cùng bản chất.
Bản chất ở đây được hiểu là kiểu, loại, nhiệm vụ của class.
Mỗi lớp dẫn xuất (derived class - lớp con) có thể thừa kế từ một lớp trừu tượng.
Từ khóa abstract được dùng để định nghĩa một Lớp trừu tượng.
Những lớp được định nghĩa bằng cách dùng từ khóa abstract thì không cho phép khởi tạo đối tượng (instance) của lớp ấy.
Ví dụ 1: Tạo đối tượng (instance/obj) cho lớp trừu tượng.
using System;
using System.Collections.Generic;
using System.Text;
namespace Abstract_Class
{
//Định nghĩa 1 lớp trừu tượng
abstract class NhanVien
{
protected string HoTen;
protected int Tuoi;
protected bool GioiTinh;
}
class Program
{
static void Main(string[] args)
{
NhanVien objPerson = new NhanVien();
}
}
}
Cannot create an instance of the abstract class or interface 'Abstract_Class.NhanVien'
(Không thể tạo instance cho lớp trừu tượng và giao diện được)
Ví dụ 2: Định nghĩa 1 lớp trừu tượng có các thuộc tính & phương thức chung cho các lớp dẫn xuất sử dụng.
- Chúng ta nhận thấy, mỗi Nhân viên đều có các thuộc tính như: HoTen, Tuoi, GioiTinh. Nhưng ChucVu thì khác nhau.
- Ví dụ: Giám đốc có ChucVu là Giám đốc, Kế toán có ChucVu là Kế toán...
- Nên thuộc tính ChucVu ta khai báo trong lớp dẫn xuất - lớp con/derived class.
Định nghĩa lớp trừu tượng như sau:
using System;
using System.Collections.Generic;
using System.Text;
namespace Abstract_Class
{
//Định nghĩa 1 lớp trừu tượng
abstract class NhanVien
{
protected string HoTen;
protected int Tuoi;
protected bool GioiTinh;
//Khai báo 1 phương thức trừu tượng bằng từ khoá abstract
public abstract void CongViec();
}
class Program
{
static void Main(string[] args)
{
}
}
}
Định nghĩa các lớp dẫn xuất GiamDoc và KeToan như sau:
using System;
using System.Collections.Generic;
using System.Text;
namespace Abstract_Class
{
//Định nghĩa 1 lớp trừu tượng
abstract class NhanVien
{
public string HoTen;
public int Tuoi;
public bool GioiTinh;
//Khai báo 1 phương thức trừu tượng bằng từ khoá abstract
public abstract void CongViec();
}
//Định nghĩa lớp Dẫn xuất GiamDoc
class GiamDoc : NhanVien
{
public string ChucVu;
public override void CongViec()
{
//Code here
}
}
//Định nghĩa lớp Dẫn xuất KeToan
class KeToan : NhanVien
{
public string ChucVu;
public override void CongViec()
{
//Code here
}
}
class Program
{
static void Main(string[] args)
{
GiamDoc objGiamDoc = new GiamDoc();
objGiamDoc.HoTen = "Nguyen Van A";
objGiamDoc.Tuoi = 30;
objGiamDoc.GioiTinh = true;
objGiamDoc.ChucVu = "Giam Doc";
System.Console.WriteLine("Lop Dan xuat 'GiamDoc': {0}, {1}, {2}, {3}",
objGiamDoc.HoTen, objGiamDoc.Tuoi, objGiamDoc.GioiTinh, objGiamDoc.ChucVu);
KeToan objKeToan = new KeToan();
objKeToan.HoTen = "Nguyen Van B";
objKeToan.Tuoi = 26;
objKeToan.GioiTinh = true;
objKeToan.ChucVu = "Ke Toan";
System.Console.WriteLine("Lop Dan xuat 'Ke Toan': {0}, {1}, {2}, {3}",
objKeToan.HoTen, objKeToan.Tuoi, objKeToan.GioiTinh, objKeToan.ChucVu);
System.Console.ReadLine();
}
}
}
* 2. Giao diện (Interface )
1. Một vài định nghĩa:
Giao diện (Interface): là một chức năng mà ta có thể thêm vào bất kì class nào. Chức năng ở đây không đồng nghĩa với phương thức (hoặc hàm). Interface có thể bao gồm nhiều hàm/phương thức và tất cả chúng cùng phục vụ cho một chức năng.
Như vậy, Interface được dùng để mô tả một "bản thiết kế" cho một chức năng của class. Nếu muốn tạo một bản thiết kế, hãy sử dụng abstract class. Một bản thiết kế tất nhiên sẽ có những thứ đã được dựng sẵn và có những thứ là abstract.
Interface không phải là lớp. Các lớp dẫn xuất từ 1 Interface đều phải định nghĩa đầy đủ các phương thức và thuộc tính kế thừa từ Interface.
Mục đích của một Interface là để định nghĩa những khả năng mà chúng ta muốn có trong một lớp.
2. Cài đặt một Giao diện:
Để hiểu rõ về Interface hơn, chúng ta tìm hiểu ví dụ sau:
- Giả sử có 2 lớp: Lớp Máy bay & Lớp Chim. Ta thấy Máy bay có chức năng bay. Chim cũng có khả năng bay.
Ví dụ 1: Tạo Interface: Bay (IBay) cho lớp May bay & Lớp Chim thừa kế.
using System;
using System.Collections.Generic;
using System.Text;
namespace Interface
{
//khai bao interface Bay
public interface IBay
{
void Bay();
}
//Lớp Chim thừa kế interface IBay
public class Chim : IBay
{
public void Bay()
{
Console.WriteLine("Chim: Bay");
}
}
//Lớp MayBay thừa kế interface IBay
public class MayBay : IBay
{
public void Bay()
{
Console.WriteLine("May Bay: Bay");
}
}
class Program
{
static void Main(string[] args)
{
Chim chim = new Chim();
chim.Bay();
MayBay maybay = new MayBay();
maybay.Bay();
System.Console.ReadLine();
}
}
}
3. Cài đặt nhiều Giao diện:
Trong ngôn ngữ C# cho phép chúng ta thực thi nhiều hơn một giao diện.
Các giao diện cách nhau bằng dấu phẩy ",".
- Ở ví dụ trên, chúng ta thấy rằng: Chim & Máy bay đều có khả năng Bay.
- Nhưng đồng thời Chim & Máy bay cũng có khả năng Chạy.
- Nên ta khai báo thêm 1 Interface Chạy có tên IChay.
Ví dụ 2: Cách C# thực thi nhiều giao diện.
using System;
using System.Collections.Generic;
using System.Text;
namespace Interface
{
//khai báo interface Bay
public interface IBay
{
void Bay();
}
//khai báo interface Chạy
public interface IChay
{
void Chay();
}
//Lớp Chim thừa kế 2 interfaces IBay, IChay
public class Chim : IBay, IChay
{
public void Bay()
{
Console.WriteLine("Chim: Bay");
}
public void Chay()
{
Console.WriteLine("Chim: Chay");
}
}
//Lớp MayBay thừa kế 2 interfaces IBay, IChay
public class MayBay : IBay, IChay
{
public void Bay()
{
Console.WriteLine("May Bay: Bay");
}
public void Chay()
{
Console.WriteLine("May Bay: Chay");
}
}
class Program
{
static void Main(string[] args)
{
Chim chim = new Chim();
chim.Bay();
chim.Chay();
MayBay maybay = new MayBay();
maybay.Bay();
maybay.Chay();
System.Console.ReadLine();
}
}
}
- Kết quả:
4. Mở rộng Giao diện (tức là - Thừa kế Giao diện):
Chúng ta có thể mở rộng giao diện đã tồn tại bằng cách thêm vào đó những phương thức hoặc thành viên mới.
Chẳng hạn: Interface IBay có thể mở rộng thêm thành IDoCao (Độ Cao). Cho biết độ cao của Máy Bay & Chim.
//khai báo interface IDoCao thừa kế interface IBay
public interface IDoCao:IBay
{
void DoCao();
}
5. Ép kiểu Giao diện:
Trong một vài trường hợp, chúng ta không biết đối tượng ấy hỗ trợ những giao diện loại gì.
Giả sử như Lớp MayBay, chúng ta không biết là nó hỗ trợ bởi giao diện IBay hay IChay hay là hỗ trợ cả hai?
Để giải quyết vấn đề này, chúng ta dùng cách "Ép kiểu giao diện".
Với cách này nó chỉ filter những phương thức thuộc Interface đó mà thôi.
MayBay maybay = new MayBay();
//Nó chỉ filter những phương thức của Interface IBay
IBay isBay = (IBay)maybay;
isBay.Bay();
//Nó chỉ filter những phương thức của Interface IChay
IChay isChay = (IChay)maybay;
isChay.Chay();
6. Cài đặt Giao diện một cách tường minh:
Một lớp có thể cài đặt nhiều giao diện nên có thể xảy ra trường hợp đụng độ về tên khi hai giao diện có cùng một tên hàm.
Để giải quyết xung đột này ta khai báo cài đặt một cách tường minh hơn.
Ví dụ: Nếu ta có hai giao diện IStorable và ITalk đều cùng có phương thức Read().
Lớp Document sẽ cài đặt hai giao diện này. Khi đó ta phải thêm tên giao diện vào trước tên phương thức
Ví dụ:
using System;
using System.Collections.Generic;
using System.Text;
namespace Interface_Test
{
interface IStorable
{
void Read();
void Write();
}
interface ITalk
{
void Read();
void Talk();
}
public class Document : IStorable, ITalk
{
// document constructor
public Document(string s)
{
Console.WriteLine("Creating document with: {0}", s);
}
// tạo read của IStorable
public virtual void Read()
{
Console.WriteLine("Implementing IStorable.Read");
}
public void Write()
{
Console.WriteLine("Implementing IStorable.Write");
}
// cài đặt phương thức Read của ITalk
void ITalk.Read()
{
Console.WriteLine("Implementing ITalk.Read");
}
public void Talk()
{
Console.WriteLine("Implementing ITalk.Talk");
}
}
class Program
{
static void Main(string[] args)
{
// create a document object
Document theDoc = new Document("Test Document");
// Ép kiểu để có thể gọi IStorable.Read()
IStorable isDoc = theDoc as IStorable;
if (isDoc != null)
{
isDoc.Read();
}
// Ép kiểu để có thể gọi ITalk.Read()
ITalk itDoc = theDoc as ITalk;
if (itDoc != null)
{
itDoc.Read();
}
theDoc.Read();
theDoc.Talk();
}
}
}