Hướng đối tượng – OO (P.3)

3.  LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG (C#)

3.1.  Lớp và các thành phần lớp

3.1.1.     Định nghĩa lớp

Lớp được định nghĩa theo cú pháp:

class ClassName

{

}

Ví dụ: Ta xây dựng một lớp Person đại diện cho các đối tượng người

class Person

{

}

3.1.2.     Thuộc tính

Cú pháp định nghĩa của một thuộc tính:

[modifier] datatype attName;

Ví dụ: Lớp Person ở trên có các thuộc tính: name, age

class Person

{

private string name;

private byte age;

}

 

3.1.3.     Phương thức

Cú pháp định nghĩa phương thức:

[modifiers] return_type MethodName([parameters])

{

//to do

}

Ví dụ: lớp Person ở trên định nghĩa một số phương thức: hiển thị thông tin, cho phép truy xuất/thiết lập tên, tuổi cho một người

class Person

{

public void Display()

{

Console.Write(“Person infor: ” + name + ” – ” + age.ToString());

}

public void SetInfor(string n, byte a)

{

name = n;

age = a;

}

public string GetName()

{

return name;

}

public byte GetAge()

{

return age;

}

}

3.1.4.     Kiểm soát truy xuất

Ý nghĩa các từ khóa qui định mức độ truy xuất đến các thành phần của lớp

–         private: từ khóa này chỉ ra thành phần (thuộc tính, phương thức) chỉ được phép truy xuất nội bên trong lớp đó, các lớp khác không có quyền truy xuất đến các thành phần này

–         protected: từ khóa này chỉ ra thành phần được phép truy xuất cao hơn private một mức, tức là ngoài việc nội bên trong lớp đó được phép truy xuất thì những lớp kế thừa trực tiếp từ lớp đó có quyền truy xuất đến thành phần này.

–         public: từ khóa này chỉ ra thành phần (thuộc tính, phương thức) được truy xuất ở mức độ công cộng, không có bất cứ ràng buộc nào cho mức truy xuất public

Ngoài ra một số ngôn ngữ còn định nghĩa một số mức truy xuất khác, ví dụ như package trong Java, hoặc internal trong C#, hoặc friend trong C++…

3.1.5.     Phương thức tạo (Constructor)

–         Phương thức tạo là một phương thức đặc biệt của lớp, đây là phương thức được gọi tự động khi khởi tạo một đối tượng của lớp thông qua toán tử new.

–         Các đặc điểm của phương thức tạo

  • Có tên trùng với tên lớp
  • Không có kiểu trả về
  • Mục đích của phương thức tạo là khởi tạo các thuộc tính của lớp
  • Mỗi lớp nếu không định nghĩa bất kỳ phương thức tạo nào thì mặc định sẽ có một phương thức tạo không tham số

–         Ví dụ: xây dựng phương thức tạo cho lớp Person ở trên

class Person

{

private string name;

private byte age;

public Person()

{

name = “”;

age = 0;

}

public Person (string n, byte a)

{

name = n;

age = a;

}

}

 

Ở ví dụ này ta xây dựng 2 phương thức tạo, một không có tham số và một có 2 tham số.

Lưu ý, nếu ta không định nghĩa bất kỳ phương thức tạo nào thì phương thức tạo Person() không tham số mặc định sẽ tồn tại. Nhưng nếu ta chỉ định nghĩa phương thức tạo Person() 2 tham số ở dưới mà không định nghĩa phương thức tạo không tham số như ví dụ, thì lớp Person sẽ chỉ có một phương thức tạo là Person(string n, byte a) mà thôi.

3.1.6.     Con trỏ this

Trong lớp, có một thành phần đặc biệt, con trỏ this, đây là thành phần đại diện cho chính lớp đó. Thành phần this chỉ sử dụng được bên trong lớp, mục đích thường dùng đó là phân biệt các thành phần thuộc lớp đó với các tham số, thành phần ngoài lớp.

class Person

{

private string name;

private byte age;

public Person(string name, byte age)

{

this.name = name;

this.age = age;

}

public void Display()

{

Console.Write(“Person name: ” + this.GetName());

}

public string GetName()

{

return name;

}

}

Trong phương thức tạo ta thấy tham số name, age trùng tên với 2 thuộc tính của lớp Person, khi ta sử dụng thành phần con trỏ this

this.name = name;

this.age = age;

hệ thống sẽ hiểu được this.name là thuộc tính của lớp và name là tham số của phương thức.

3.1.7.     Khởi tạo đối tượng

Để khởi tạo đối tượng, ta sử dụng toán tử new

Person person1 = new Person();

Person person2 = new Person(“NGUYEND”, 26);

Khởi tạo person1 bằng phương thức tạo không tham số, khi đó name = “”, age = 0

Khởi tạo person2 bằng phương thức tạo 2 tham số, khi đó name = “NGUYEND”, age = 26

Xét hành động khởi tạo một đối tượng

Person person = new Person();

Khi khởi tạo một đối tượng lớp bằng toán tử new, hệ thống sẽ tạo ra một vùng nhớ tương ứng với lớp Person đồng thời cấp phát một vùng nhớ tham chiếu cho biến person. Và vùng nhớ hệ thống cấp phát tương ứng với lớp Person sẽ được trỏ đến bởi vùng nhớ tham chiếu person, nói cách khác, tại vùng nhớ của person sẽ chứa địa chỉ của vùng nhớ hệ thống cấp phát tương ứng với lớp Person.

Tiếp đó, ta khởi tạo một đối tượng person3 như sau:

Person person3 = person;

Lúc này hệ thống cấp cho biến person3 một vùng nhớ tham chiếu và trỏ đến vùng nhớ ở địa chỉ mà biến person đang trỏ. Nói cách khác 2 đối tượng person và person3 cùng trỏ đến một vùng nhớ dữ liệu, bất kỳ thay đổi nào trên đối tượng này cũng làm đối tượng kia thay đổi theo.

ClassName person = new ClassName(“NGUYEND”, 26);

ClassName person3 = person;

string s = person.GetName();

string s3 = person3.GetName();

person.SetInfor(“MUC DONG”, 21);

s = person.GetName();

s3 = person3.GetName();

s và s3 ở dòng lệnh 3,4 sẽ là NGUYEND

s và s3 ở dòng lệnh 6,7 sẽ là MUC DONG

3.1.8.     Thành viên tĩnh (static)

–         Thành phần tĩnh là một thành phần (thuộc tính, phương thức) đặc biệt của lớp, nó được định nghĩa bởi từ khóa static. Thành phần tĩnh khác các thành phần khác của lớp ở điểm, với các thành phần bình thường, chúng chỉ hoạt động khi đối tượng của lớp được tạo ra, và các thành phần này được truy xuất thông qua đối tượng đó, trong khi đó thành phần tĩnh không truy xuất thông qua đối tượng lớp, mà là lớp đó. Nếu thành phần tĩnh là thuộc tính, thì nó có một vùng nhớ chung cho toàn lớp, các truy xuất ở bất kỳ đâu đều tương tác trên vùng nhớ này

–         Việc truy xuất đến thành phần tĩnh sẽ thông qua tên lớp

public class ClassA

{

public static byte staticAtt1 = 1;

private static byte staticAtt2 = 2;

public byte GetStatic2()

{

return ClassA.staticAtt2;

}

public void ChangeStatic(byte a)

{

ClassA.staticAtt1 += a;

ClassA.staticAtt2 += a;

}

}

ClassA objA = new ClassA();

byte b1 = ClassA.staticAtt1;

byte b2 = objA.GetStatic2();

objA.ChangeStatic(3);

b1 = ClassA.staticAtt1;

b2 = objA.GetStatic2();

ở dòng lệnh 1,2:

b1 = 1; b2 = 2;

ở dòng lệnh 4,5:

b1 = 4; b2 = 5;

3.2.  Kế thừa

Ta có một lớp Person đại diện các thông tin về một người, và lớp Employee đại diện các thông tin của một người lao động, lớp Employee kế thừa từ lớp Person

public class Person

{

protected string name;

protected string birthday;

public Person(string name, string birthday)

{

this.name = name;

this.birthday = birthday;

}

public void Display()

{

Console.WriteLine(“Person: ” + name + ” ” + birthday);

}

}

public class Employee : Person

{

private string job;

private string salary;

public Employee(string name, string birthday, string job, string salary)

: base(name, birthday)

{

this.job = job;

this.salary = salary;

}

}

Khi đó:

Person person = new Person(“NGUYEND”, “16/06”);

Employee emplyee = new Employee(“MUC DONG”, “01/10”, “BANK”, “500$”);

person.Display();

emplyee.Display();

Thông tin hiển thị ra là:

Person: NGUYEND 16/06

Person: MUC DONG 01/10

Mặc dù lớp Employee không định nghĩa phương thức Display() nhưng mà nó được kế thừa từ lớp Person

Nếu ta định nghĩa lại lớp Employee như sau

public class Employee : Person

{

private string job;

private string salary;

public Employee(string name, string birthday, string job, string salary)

: base(name, birthday)

{

this.job = job;

this.salary = salary;

}

public void Display()

{

base.Display();

Console.WriteLine(“Employee: ” + job + ” ” + salary);

}

}

Khi đó:

Person person = new Person(“NGUYEND”, “16/06”);

Employee emplyee = new Employee(“MUC DONG”, “01/10”, “BANK”, “500$”);

person.Display();

emplyee.Display();

Thông tin hiển thị là

Person: NGUYEND 16/06

Person: MUC DONG 01/10

Employee: BANK 500$

Phương thức Display() của lớp employee đã được định nghĩa, do đó nó sẽ được gọi khi thực hiện dòng lệnh emplyee.Display();

Nếu ta thực hiện dòng lệnh sau

((Person)emplyee).Display();

Khi đó phương thức Display() của lớp Person sẽ được gọi, vì employee đã được hiểu là đối tượng của lớp Person thông qua lệnh ép kiểu.

Thông tin hiển thị ra chỉ là:

Person: MUC DONG 01/10

3.3.  Đa hình

Ta sẽ tìm hiểu tính đa hình thông qua ví dụ sau

Ta định nghĩa một lớp đối tượng đồ họa DrawingObject với một phương thức Draw() như sau:

public class DrawingObject

{

public virtual void Draw()

{

Console.WriteLine(“This is Drawing Objects !”);

}

}

Phương thức Draw() được định nghĩa với từ khóa virtual với ý nghĩa đây là một phương thức ảo, và phương thức này sẽ được triển khai ở các lớp con kế thừa từ lớp DrawingObject

Có 2 lớp con kế thừa từ lớp DrawingObject là Line và Square

public class Line : DrawingObject

{

public override void Draw()

{

Console.WriteLine(“This is Line !”);

}

}

public class Square : DrawingObject

{

public override void Draw()

{

Console.WriteLine(“This is Square !”);

}

}

Hai lớp này thực thi lại phương thức Draw() với từ khóa override

Khi đó:

DrawingObject[] arr = new DrawingObject[3];

arr[0] = new DrawingObject();

arr[1] = new Line();

arr[2] = new Square();

foreach (DrawingObject obj in arr)

{

obj.Draw();

}

Thông tin hiển thị là:

This is DrawingObjects !

This is Line !

This is Square !

Với tính đa hình, khi lệnh được thực thi ở thời gian chạy (run-time), hệ thống sẽ biết chính xác phương thức Draw() nào được gọi tùy vào từng đối tượng cụ thể thuộc vào lớp nào.

Nếu ta định nghĩa thêm một lớp Circle nhưng không ta không override lại phương thức Draw()

public class Circle : DrawingObject

{

}

Khi đó

DrawingObject[] arr = new DrawingObject[4];

arr[0] = new DrawingObject();

arr[1] = new Line();

arr[2] = new Square();

arr[3] = new Circle();

foreach (DrawingObject obj in arr)

{

obj.Draw();

}

Kết quả hiển thị là:

This is DrawingObjects !

This is Line !

This is Square !

This is DrawingObjects !

Đối tượng thuộc lớp Circle không định nghĩa lại phương thức Draw() nên hệ thống sẽ gọi phương thức ảo Draw() của lớp DrawingObject

3.4.  Lớp trừu tượng (Abstract)

Lớp trừu tượng là một lớp không có thể hiện, tức là ta không thể tạo ra một đối tượng thuộc lớp này mà chỉ có thể tạo ra các đối tượng cụ thể thông qua các lớp con kế thừa từ lớp này.

Lớp trừu tượng được sử dụng khi ta muốn trừu tượng hóa một lớp đối tượng chung mà ta biết rằng trong quá trình thực thi run-time ta sẽ sử dụng các lớp con kế thừa từ lớp đó.

Lớp trừu tượng thường chứa một số phương thức trừu tượng mà các lớp con khi kế thừa từ nó bắt buộc phải định nghĩa chi tiết

Như ở ví dụ trên, ta thấy lớp DrawingObject trong thực tế không bao giờ ta sử dụng đến, nhưng với các xây dựng ở trên, ta vẫn có thể khởi tạo nên một đối tượng của lớp đó, điều này là không cần thiết. Với ví dụ trên, ta xây dựng lại như sau

public abstract class DrawingObject

{

public abstract void Draw();

}

Lớp DrawingObject được định nghĩa với từ khóa abstract, cho biết đây là một lớp trừu tượng, phương thức Draw() với từ khóa abstract định nghĩa đây là một phương thức trừu tượng và bắt buộc các lớp con khi kế thừa phải triển khai phương  thức này một các cụ thể, phương thức này không định nghĩa chi tiết trong lớp DrawingObject

public class Line : DrawingObject

{

public override void Draw()

{

Console.WriteLine(“This is Line !”);

}

}

public class Square : DrawingObject

{

public override void Draw()

{

Console.WriteLine(“This is Square !”);

}

}

public class Circle : DrawingObject

{

public override void Draw()

{

Console.WriteLine(“This is Circle !”);

}

}

Phương thức Draw() bắt buộc phải được override trong các lớp con kế thừa từ DrawingObject

Khi đó

DrawingObject[] arr = new DrawingObject[3];

arr[0] = new Line();

arr[1] = new Square();

arr[2] = new Circle();

foreach (DrawingObject obj in arr)

{

obj.Draw();

}

Kết quả hiển thị:

This is Line !

This is Square !

This is Circle !

Và lệnh DrawingObject obj = new DrawingObject(); sẽ bị báo lỗi, vì lớp DrawingObject là một lớp trừu tượng, nó không được phép tạo ra một đối tượng cụ thể.

Lưu ý, trong lớp trừu tượng, ngoài các thành phần trừu tượng như lớp Draw() ở trên, vẫn cho phép định nghĩa các thành phần không trừu tượng, và các thành phần này sẽ được truy xuất thông qua các lớp con kế thừa từ lớp trừu tượng

3.5.  Giao tiếp (Interface)

Khái niệm Interface cho phép một hệ thống định nghĩa ra một số các hành vi để giao tiếp với nó, các hệ thống bên ngoài, muốn giao tiếp được với hệ thống này thì cần phải triển khai các Interface mà hệ thống định nghĩa. Trong một số trường hợp, trong công nghệ phần mềm, một số công việc tùy theo từng nhóm, từng trường hợp lại có những cách giải quyết, thực hiện khác nhau. Từng cách giải quyết, thực hiện này sẽ được cụ thể hóa trong từng thực thi của Interface mà hệ thống định nghĩa.

Về mặc định nghĩa, Interface cũng giống như một lớp trừu, chỉ khác là nó không có phần thực thi, các phương thức định nghĩa trong Interface chỉ có tên, kiểu trả về, tham số nhưng không có phần thực thi (thân phương thức). Các lớp thực thi từ Interface sẽ phải thực thi cụ thể các phương thức này.

Một Interface cũng không thể tạo ra một thể hiện như lớp trừu tượng, các thể hiện chỉ có thể tạo ra thông qua các lớp thực thi (implements cũng tương tự kế thừa lớp) từ Interface

public interface MyInterface

{

int MyMethod1(int param);

string MyMethod2();

void MyMethod3();

}

public class MyImplement1 : MyInterface

{

public int MyMethod1(int param)

{

return param + 1;

}

public string MyMethod2()

{

return “nothing”;

}

public void MyMethod3()

{

//to do 1

}

}

public class MyImplement2 : MyInterface

{

public int MyMethod1(int param)

{

return param + 6;

}

public string MyMethod2()

{

return “has something”;

}

public void MyMethod3()

{

//to do 2

}

}

Giả sử có một hệ thống, định nghĩa một phương thức giao tiếp với nó thông qua MyInterface như sau

public class MySystem

{

public string GetSystemInfor(MyInterface param)

{

int a = param.MyMethod1();

string s = param.MyMethod2();

param.MyMethod3();

return a.ToString + s;

}

}

Khi đó, để giao tiếp với MySystem, ta sẽ thực hiện thông qua các MyImplement

MySystem s = new MySystem();

s.GetSystemInfor(new MyImplement1());

s.GetSystemInfor(new MyImplement2());

  1. Để lại bình luận

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s

%d bloggers like this: