Khi nói đến các tính chất của lập trình hướng đối tượng. Anh em lập trình có thể liệt kê ra cả bốn đặc tính ngay lập tức. Này là tính đóng gói, tính kế thừa, tính đa hình và tính trừu tượng. So với một số anh em, lý thuyết phần này khá khó hiểu và khó nhớ. Ở phần 2 của series “Hướng đối tượng bỏ túi”. Mình sẽ hệ thống lại tri thức về bốn đặc tính trọng yếu khi lập trình hướng đối tượng.

Đặt vấn đề

Để nội dung của nội dung liền mạch hơn, mình sẽ đặt vấn đề trước. Mọi tính chất của hướng đối tượng sẽ được mình lồng ghép và xây đắp từ vấn đề này.

Tiếp nối phần 1 của series. Sau khi thiết kế ra chiếc xe chỉ chạy được trên … NetBeans. Mình đã được sếp thăng chức, chuyển sang làm nhân viên vườn thú. Với người thông minh và tài giỏi như mình, việc này dễ như tìm đường vào tim crush vậy. Suốt ngày chỉ có cho ăn, đếm thú, tối đến lại lùa vào chuồng. Dù thời gian bận không nhiều nhưng mình vẫn rãnh để mô phỏng sở thú này trên máy tính. Vậy mình nên thiết kế như nào anh em nhỉ?

Encapsulation – Tính bao đóng

Tính bao đóng là gì?

Encapsulation means that a group of related properties, methods, and other members are treated as a single unit or object.

Tính chất trước hết tất cả chúng ta cần nhớ chính là tính bao đóng (đóng gói). Tính bao đóng yêu cầu thực hiện gom những thứ liên quan lại thành một nhà cung cấp hoặc đối tượng, Class. Đồng thời ẩn những dữ liệu nhạy cảm khỏi khả năng truy xuất của người dùng.

Tính bao đóng có thể đạt được bằng cách sử dụng Access ModifierGetter - Setter. (xem lại khái niệm ở phần 1)

Phân tích và vận dụng tính bao đóng

Tính chất này cũng dễ hiểu thôi, tất cả chúng ta sẽ khởi đầu với con Vịt nhé. Vậy là giờ tất cả chúng ta cần gom mấy thứ có liên quan lại thành một class, đặt tên là Duck. Để xem nào, nó là loài là vịt nè, phải có tên riêng nữa. Nó có thể ăn, bơi rồi kêu cak cak cak. ? Nếu như vậy thì tên loài sẽ không cho phép bên ngoài thay đổi, còn tên riêng con vịt có thể đặt sao cũng được. Sau này là khái niệm các phương thức đại diện cho hành động ăn, bơi, kêu của con vịt.

Example 01: Khái niệm class Duck vận dụng tính đóng gói

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

class

Duck

{

  

// Constructor để tạo con vịt

  

public

Duck

(

string

name

)

{

Name

=

name

;

}

 

  

// Tên loài, thông tin này dùng nội bộ

  

private

string

Species

{

get

;

set

;

}

=

“Duck”

;

 

  

// Đặt tên cho con vịt

  

// Chỉ đọc và đặt tên 1 lần lúc tạo

  

public

string

Name

{

get

;

private

set

;

}

 

  

// Các hành động của con vịt

  

public

void

Eat

(

)

  

{

    

Console

.

WriteLine

(

$

“{Species} name: {Name} is Eating!”

)

;

  

}

  

public

void

Swim

(

)

  

{

    

Console

.

WriteLine

(

$

“{Species} name: {Name} is Swimming!”

)

;

  

}

  

public

void

Sound

(

)

  

{

    

Console

.

WriteLine

(

$

“{Name} – Mot con Vit xoe ra hai cai canh, no keu rang cak cak cak cak cak cak.”

)

;

  

}

}

Như vậy là tất cả chúng ta đã khái niệm được con vịt trông như vậy nào rồi. Việc còn sót lại là viết hàm main và chạy thử thôi. Dùng thử từ ngữ Java anh em cho mình xin khất. Ai cần hãy comment trong nội dung, mình sẽ bổ sung sau.

Inheritance – Tính kế thừa

Tính kế thừa cần được hiểu như vậy nào?

Inheritance describes the ability to create new classes based on an existing class.

Đây là tính chất dễ hiểu nhất của hướng đối tượng. Nó cho phép tất cả chúng ta khái niệm một class mới dựa trên một class đã khái niệm trước đó. Nói cách khác là class con kế thừa lại những đặc tính đã có của class cha.

Tính kế thừa có ba cấp độ:

  1. Base class inheritance: Kế thừa toàn thể từ lớp nền tảng.
  2. Abstract class inheritance: Kế thừa từ lớp abstract. Gọi là kế thừa một phần vì phải khái niệm lại những hàm abstract.
  3. Interface inheritance: Kế thừa khuôn mẫu, phải khái niệm rất cả những gì interface yêu cầu.

Tuy nhiên, để đảm bảo tính kế thừa, ta cần lưu ý khá nhiều thứ:

  • Lớp kế thừa được sử dụng những thành phần được cho phép của lớp nền tảng quy định bở Access Modifier (public, protected, v.v…).
  • Nói cho chuẩn: Kế thừa class gọi là extends và kế thừa interface gọi là implements. Java sử dụng 2 từ khóa đó để thực hiện kế thừa. So với C#, chỉ cần dùng dấu : cho cả class và interface.
  • Từ khóa this: Đại diện cho lớp chứa cục code hiện tại.
  • base class đại diện cho lớp nền tảng, lớp cha. Từ ngữ Java sử dụng phương thức super(), C# sử dụng base. So với C++ là Google.com.
  • Kế thừa 1 cấp & kế thừa nhiều cấp.
  • Lớp kế thừa có thể ép kiểu về lớp nền tảng mà không làm mất tính đúng đắn của chương trình.
  • Hầu như các từ ngữ không cho phép đa kế thừa class. Tránh việc 2 class kế thừa có cùng các thuộc tính giống nhau, nhưng có thể đã implement khác nhau.
  • Có thể đa kế thừa Interface.

Phân tích và xây dựng sở thú dựa trên tính kế thừa

Nếu như tất cả chúng ta xây dựng sở thú chỉ dựa vào tính bao đóng như ở ví dụ 1. Thì nghĩa là cứ 10 loài ta phải tạo 10 class, sau đó khái niệm lại thuộc tính, hành động một lần nữa. Nếu đặc điểm giống nhau giữa chúng có điểm cần thay đổi lại phải sửa thủ công 10 class khác nhau. Rất là mất thời gian, công sức và hơi khổ râm. Này là nguyên nhân tất cả chúng ta phải vận dụng tính kế thừa.

Lúc này, tất cả chúng ta cần gom những đặc tính chung của động vật vào một lớp gọi là Animal. Sau đó muốn tạo ra loài mới, ta chỉ cần kế thừa từ lớp động vật là nó đã có những đặc tính của động vật rồi. Chương trình của tất cả chúng ta cần nâng cấp như sau:

Example 02: Khái niệm lớp nền tảng Animal để dùng chung

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

// Lớp Động vật

class

Animal

{

  

private

string

Species

{

get

;

set

;

}

        

// Loài

  

public

string

Name

{

get

;

private

set

;

}

    

// Tên

 

  

public

Animal

(

string

species

,

string

name

)

  

{

    

this

.

Species

=

species

;

    

this

.

Name

=

name

;

  

}

    

  

// Lấy thông tin loài vật

  

public

string

GetInfo

(

)

=

>

$

“{Species} name: {Name}”

;

 

  

// Hành động chung của các loài vật

  

public

void

Eat

(

)

  

{

    

Console

.

WriteLine

(

$

“{GetInfo()} is Eating!”

)

;

  

}

  

public

void

Sound

(

)

  

{

    

Console

.

WriteLine

(

$

“{GetInfo()} is Chirped”

)

;

  

}

}

Lúc này, class Duck cần kế thừa hay nói đúng hơn là mở rộng (extends) class Animal. Và nó được tinh gọn như sau:

Example 03: Class Duck kế thừa từ class Animal

1

2

3

4

5

6

7

8

9

10

11

12

13

// Loài Vịt

class

Duck

:

Animal

{

  

// Gọi lại constructor của lớp cha bằng hàm base

  

public

Duck

(

string

name

)

:

base

(

“Duck”

,

name

)

  

{

/* Some code here */

}

 

  

// Hành động riêng của con Vịt

  

public

void

Swim

(

)

  

{

    

Console

.

WriteLine

(

$

“{GetInfo()} is Swimming!”

)

;

  

}

}

Khi cần khái niệm một loài mới, ta chỉ cần tạo class mới, kế thừa class Animal, sau đó thêm các thuộc tính của riêng loài mới là xong. Tất cả chúng ta sẽ khái niệm thêm loài khỉ cho sở thú.

Example 04: Khái niệm loài khi từ class Animal

1

2

3

4

5

6

7

8

9

10

11

12

13

// Loài Khỉ

class

Monkey

:

Animal

{

  

// Gọi lại constructor của lớp cha bằng hàm base

  

public

Monkey

(

string

name

)

:

base

(

“Monkey”

,

name

)

  

{

/* Some code here */

}

 

  

// Hành động riêng của con Khỉ

  

public

void

Climb

(

)

  

{

    

Console

.

WriteLine

(

$

“{GetInfo()} is Climbing!”

)

;

  

}

}

Sau khi khái niệm 3 class trên, anh em tạo hàm main để chạy thử và xem kết quả:

Example 05: Main method

1

2

3

4

5

6

7

static

void

Main

(

string

[

]

args

)

{

  

var

duck

=

new

Duck

(

“Donal”

)

;

        

// Tạo con vịt Donal

  

var

monkey

=

new

Monkey

(

“Wukong”

)

;

  

// Tạo con khỉ wukong

  

duck

.

Eat

(

)

;

                          

// Cho vịt ăn

  

monkey

.

Eat

(

)

;

                        

// Cho khỉ ăn

}

Vậy là tất cả chúng ta đã vận dụng tính kế thừa cho sở thú rồi đấy. Bạn thử phán đoán kết quả in ra màn hình như vậy nào nhé. ?

Polymorphism – Tính đa hình

Khái niệm tính đa hình

Polymorphism means that you can have multiple classes that can be used interchangeably, even though each class implements the same properties or methods in different ways.

Ngay trong từ đa hình nó đã mang ý nghĩa là một thứ gì đó mang nhiều hình thái khác nhau. Trong lập trình hướng đối tượng. Nếu tính kế thừa cho phép ta thừa hưởng một phương thức từ một class. Thì tính đa hình cho phép ta triển khai lại phương thức đó theo những cách khác nhau. Việc này kéo theo việc các class kế thừa từ chung một class cha có thể được sử dụng thay thế cho nhau mà không làm tác động đến tính đúng đắn của chương trình.

Tính đa hình có thể đạt được bằng cách sử dụng:

  • Method Overloading: Nạp chồng

  • Method Overriding: Ghi đè

Method Overloading – Nạp chồng phương thức

Method Overloading cho phép triển khai cùng một tính năng với nhiều loại tham số khác nhau. Nạp chồng được gọi là compiletime polymorphism.

Ví dụ phương thức cho vịt ăn ngoài việc cho ăn mặc định, đôi lúc ta cần cho cho biết thêm loại thức ăn, số lượng, thời gian, v.v… .

Phương thức nạp chồng phải cùng têncùng kiểu trả vềthỏa mãn một trong các điều kiện sau:

  1. Khác số lượng tham số truyền vào (parameters).
  2. Khác kiểu dữ liệu của các tham số truyền vào.
  3. Khác thứ tự của tham số truyền vào.

Method Overriding – Ghi đè phương thức

Method Overriding là một phương pháp cho phép lớp kế thừa tái khái niệm một phương thức đã khái niệm ở lớp cha. Ghi đè được gọi là runtime polymorphism.

Ghi đè phương thức phải thỏa mãn ba điều kiện sau:

  1. Phải có quan hệ kế thừa giữa hai class.
  2. Cùng tên và cùng kiểu trả về (hoặc sub-type).
  3. Cùng các tham số truyền vào.

Thực tiễn, ghi đè phương thức của Java và C# có nhiều điểm khác nhau. Ngày xưa còn tay mơ mình rất hay nhầm lẫn mấy vấn đề này.

Java Method Overriding

So với Java, chỉ cần khái niệm phương thức ghi đè trong class con thì nó được ngầm hiểu là phương thức ghi đè. Từ khóa @Override có dùng hay không cũng được, nhưng anh em nên dùng để lúc đọc code đỡ lú.

Ví dụ: Ghi đè phương thức Sound() của con Vịt.

Example 06: Main.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

package

blog

.

hieuda

.

com

;

 

class

Animal

{

    

private

String

species

;

    

private

String

name

;

 

    

public

Animal

(

String

species

,

String

name

)

{

        

this

.

species

=

species

;

        

this

.

name

=

name

;

    

}

 

    

public

String

getName

(

)

{

        

return

name

;

    

}

 

    

private

void

setName

(

String

name

)

{

        

this

.

name

=

name

;

    

}

 

    

public

String

getInfo

(

)

{

        

return

species

+

” name: “

+

name

;

    

}

 

    

public

void

eat

(

)

{

        

System

.

out

.

println

(

getInfo

(

)

+

” is eating”

)

;

    

}

    

// Overloading

    

public

void

eat

(

String

food

)

{

        

System

.

out

.

println

(

getInfo

(

)

+

” is eating “

+

food

)

;

    

}

 

    

public

void

sound

(

)

{

        

System

.

out

.

println

(

getInfo

(

)

+

” is Chirped”

)

;

    

}

}

 

class

Duck

extends

Animal

{

    

// Gọi lại constructor của animal

    

public

Duck

(

String

name

)

{

        

super

(

“Duck”

,

name

)

;

    

}

 

    

// Ghi đè hàm sound, ko cần sử dụng annotation vẫn được

    

// @Override

    

public

void

sound

(

)

{

        

System

.

out

.

println

(

getInfo

(

)

+

” – Mot con vit xoe ra hai cai canh. No keu rang: cắk cắk cắk cằk cằk cằk.”

)

;

    

}

 

    

// Hành động riêng của con Vịt

    

public

void

swim

(

)

{

        

System

.

out

.

println

(

getInfo

(

)

+

” is swimming.”

)

;

    

}

}

 

public

class

Main

{

    

public

static

void

main

(

String

[

]

args

)

{

        

Animal

a

=

new

Duck

(

“Donal Trung”

)

;

        

a

.

Sound

(

)

;

        

// Output:

        

// Duck name: Donal Trung – Mot con vit xoe ra hai cai canh. No keu rang: cắk cắk cắk cằk cằk cằk.

    

}

}

C# Method Overriding

So với C#, khi ghi đè phương thức, ta bắt buộc phải sử dụng từ khóa virtual cho phương thức của lớp cha. Và sử dụng từ khóa override khi khái niệm phương thức ghi đè. Nếu không, compiler sẽ quăng cho bạn một cái warning bảo là this method bị hide gì đó. Khi đó, ta cần dùng từ khóa new để khái niệm phương thức này là Method Hiding. Đây không phải là bug, đây là cơ chế sẽ được đề cập ở nội dung Method Hidding.

Ví dụ C# Method Overriding mình đính kèm vào code trừu tượng bên dưới luôn nha.

Data Abstraction – Trừu tượng hóa dữ liệu

Trước khi khởi đầu, cần thẳng thắn với nhau rằng “Không có tính trừu trượng trong OOP, chỉ có Data Abstraction”. Các nội dung trên mạng đang ra rả rằng Hướng đối tượng có bốn tính chất. Như vậy là không đúng bản chất. Trong các thời kỳ phát triển software, Data Abstraction nằm trong thời kỳ thiết kế. Còn OOP nằm ở thời kỳ triển khai. Do đó, nó phải giải quyết được các yêu cầu nghiệp vụ, là tầng trung gian kết nối business logic với các thiết kế software. Data Abstraction là mục tiêu mà lập trình hướng đến. OOP sử dụng các object, class, interface, và ba tính chất đóng gói, kế thừa, đa hình cũng để đạt đến trạng thái Abstraction. Đó cũng là nguyên nhân mình giới thiệu Data Abstraction sau khi đã nói về những thứ khác của OOP.

Nghe có vẻ trừu tượng, nhưng nghĩ lại thì rất trừu tượng ?

Data Abstraction means hiding the unnecessary details from type consumers.

Trừu tượng hóa dữ liệu nghĩa là che giấu những thành phần không cần thiết khỏi người dùng. Các bạn tránh nhầm lần với việc “ẩn những dữ liệu nhạy cảm khỏi khả năng truy xuất của người dùng” của tính đóng gói. Điều này cho phép người dùng có thể triển khai những logic phức tạp dựa trên một lớp trừu tượng có sẵn mà không cần quan tâm bên trong thực sự làm gì.

Trừu tượng có thể đạt được bằng cách sử dụng:

  • Abstract class
  • Interface

Phân tích và trừu tượng hóa sở thú

Ở ba phần trên, tất cả chúng ta đã lần lượt xây dựng những thứ nền tảng nhất. Sở thú đã có thể đi vào hoạt động bình thường. Lần này, ta sẽ nâng cấp bằng cách trừu tượng hóa chúng. Làm sao khi sở thú thuê nhân viên mới, họ vẫn có thể cho thú ăn, ngủ, chạy mà không cần quan tâm rõ ràng công việc đó phải làm gì.

Interface – Chia tách các tính năng thành các Interface

Mindset của interface là quy định một số tính năng cho đối tượng. Nói đơn giản là interface báo cho người dùng biết class đang được sử dụng có thể làm gì. Thực tiễn, các interface có sẵn trong Java thường được đặt kiểu: Runnable, Enumerable,… đại diện cho khả năng mà class đó có thể phục vụ. Ở nội dung này, tất cả chúng ta sẽ tạo 3 interface là IAnimal, IRunnable, ISwimmable đại diện cho loài vật và khả năng di chuyển của chúng.

Example 07: Interface

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

interface

IAnimal

{

    

void

Eat

(

)

;

    

void

Eat

(

string

food

)

;

    

void

Sound

(

)

;

}

 

interface

IRunnable

{

    

void

Run

(

)

;

}

 

interface

ISwimmable

{

    

void

Swim

(

)

;

}

Abstract class – Implement một số tính năng

Trong thực tiễn, động vật chỉ là một khái niệm, không phải là loài vật rõ ràng nên được gọi là trừu tượng. Tất cả chúng ta khởi đầu xây dựng lớp trừu tượng Animal. Trong số đó có các property và implement sẵn một vài tính năng. Lúc này tất cả chúng ta không thể tạo object kiểu Animal ani = new Animal(); được mà nó sẽ được dùng để đại diện cho các object khác kế thừa từ nó.

Ví dụ: Animal ani = new Duck("Donal") hoặc Animal ani = new Monkey("Wukong");

Hoặc vjp pro hơn: IListvàlt;IAnimalvàgt; zoo = new Listvàlt;IAnimalvàgt;(); Sau đó zoo.Add(new Duck("Donal"));

Example 08: Animal abstract class

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

abstract

class

Animal

:

IAnimal

{

    

private

string

Species

{

get

;

set

;

}

    

public

string

Name

{

get

;

private

set

;

}

 

    

public

Animal

(

string

species

,

string

name

)

    

{

        

this

.Species

=

species

;

this

.Name

=

name

;

    

}

 

    

//

L

y

th

ô

ng

tin

con

v

t

    

public

string

GetInfo

(

)

=

>

$

“{Species} name: {Name}”

;

 

    

//

H

à

nh

độ

ng

ă

n

c

ó

th

ghi

đè

ho

c

kh

ô

ng

    

public

virtual

void

Eat

(

)

    

{

        

Console

.WriteLine

(

$

“{GetInfo()} is eating!”

)

;

    

}

    

public

virtual

void

Eat

(

string

food

)

    

{

        

Console

.WriteLine

(

$

“{GetInfo()} is eating {food}!”

)

;

    

}

 

    

//

C

á

c

l

p

k

ế

th

a

ph

i

implement

ti

ế

ng

k

ê

u

    

public

abstract

void

Sound

(

)

;

}

Tạo lớp Duck và Monkey

Example 09: Implement class

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

class

Duck

:

Animal

,

ISwimmable

{

    

// Constructor để tạo con Vịt

    

// Gọi lại constructor của lớp cha bằng hàm base

    

public

Duck

(

string

name

)

:

base

(

“Duck”

,

name

)

    

{

/* Some code here */

}

 

    

// Ghi đè hành động ăn

    

public

override

void

Eat

(

)

    

{

        

Console

.

WriteLine

(

“Override eating from Duck.Eat()”

)

;

        

base

.

Eat

(

)

;

    

}

    

public

override

void

Eat

(

string

food

)

    

{

        

Console

.

WriteLine

(

“Override eating from Duck.Eat(food)”

)

;

        

base

.

Eat

(

food

)

;

    

}

 

    

// Triển khai abstract method

    

public

override

void

Sound

(

)

=

>

Console

.

WriteLine

(

$

“{GetInfo()} – Mot con vit xoe ra hai cai canh. No keu rang: cắk cắk cắk cằk cằk cằk.”

)

;

 

    

// Khả năng bơi của con Vịt từ ISwimmable

    

public

void

Swim

(

)

=

>

Console

.

WriteLine

(

$

“{GetInfo()} is Swimming!”

)

;

}

 

class

Monkey

:

Animal

,

IRunnable

{

    

public

Monkey

(

string

name

)

:

base

(

“Monkey”

,

name

)

    

{

/* Some code here */

}

 

    

public

override

void

Sound

(

)

=

>

Console

.

WriteLine

(

$

“{GetInfo()} – Con khỉ kêu éc éc éc”

)

;

 

    

public

void

Run

(

)

=

>

System

.

Console

.

WriteLine

(

$

“{GetInfo()} is running.”

)

;

}

Viết hàm Main để tạo sở thú

Tất cả chúng ta sẽ tạo một danh sách các loài vật và cho chúng ăn mà không cần quan tâm chúng là con gì.

Example 10: Cho thú ăn

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

static

void

Main

(

string

[

]

args

)

{

    

IList

<

IAnimal

>

zoo

=

new

List

<

IAnimal

>

(

)

;

 

    

zoo

.

Add

(

new

Duck

(

“Donal”

)

)

;

    

zoo

.

Add

(

new

Duck

(

“Trung”

)

)

;

    

zoo

.

Add

(

new

Monkey

(

“Son”

)

)

;

    

zoo

.

Add

(

new

Monkey

(

“Wukong”

)

)

;

 

    

foreach

(

IAnimal

animal

in

zoo

)

    

{

        

animal

.

Sound

(

)

;

        

animal

.

Eat

(

“Special food”

)

;

    

}

}

Và đây là kết quả khi chạy hàm main trên. Kết quả có giống những gì bạn phán đoán không? Nếu không, trả lời thắc mắc vì sao nhé!

Cũng chính vì thế, mình không đặt tiêu đề nội dung là “Bốn tính chất của hướng đối tượng” mà đổi thành “Bốn tính chất cần lưu tâm khi học…”.

Tổng kết

Đây là nội dung thứ 2 của series Hướng đối tượng bỏ túi. Và cũng là trọng yếu nhất trong ba bài. Ở phần này, tất cả chúng ta đã nói về bốn tính chất của lập trình hường đối tượng. Có thể các bạn không để ý, mình sắp xếp theo thứ tự tính đóng gói, kế thừa, đa hình và trừu tượng là vì tính chất về sau yêu cầu nắm tính chất trước mới ứng dụng được.

Series hướng đối tượng bỏ túi – OOP in basic:

Nội dung nặng lý thuyết quá phải không anh em ? Để tổng hợp và viết bài này mình cũng mất mấy ngày lận. Cũng không thể tránh khỏi sai sót. Nếu anh em phát hiện nơi đâu không ổn hoặc giải thích khó hiểu. Hãy comment bên dưới để mình chỉnh sửa lại thích hợp hơn nhen. Cuối cùng, mình đính kèm bức ảnh tổng quan của 3 nội dung về hướng đối tượng.

Refs

Difference between Compile-time and Run-time Polymorphism in JavaMethod Hiding in C#Method Overriding in JavaC# | Method OverridingDifference between Method Overriding and Method Hiding in C#Abstraction is not a principle of Object-Oriented Programming

See also  LAPTOP FUJITSU LIFEBOOK T902 KÈM BÚT C/Ư