Thế nào là lập trình hướng đối tượng OOP?


Hoặc có thể vào ĐÂYđể tìm hiểu rõ hơn về principles of object oriented

Đó là theo quan điểm hàn lâm. Nói dễ hiểu hơn Object oriented programming(OOP) hay Lập trình hướng đối tượng. Được phát triển từ những năm 2000 nhằm thay thế cho lập trình thủ tục như C. Giúp người lập trình quản lý phát triển và quản lý code dễ dàng hơn.

Nhìn vào tên của nó bạn cũng có thể hình dung ra được. Ngôn ngữ lập trình này giải quyết các bài toán từ nhỏ đến lớn bằng cách quan sát và tưởng tượng những hành động, đặc điểm của thực thể thật ngoài đời sống và đem vào lập trình như một đối tượng ảo. Thể hiện qua các lớp (class), đối tượng (Object) mà hành động là các hàm(method) còn đặc điểm chính là các biến(variable)

Nói 1 cách hoa mỹ hơn

OOP is the art of observing of objects in their natural appearance and trying to capture the functional abstractnesses in descriptive programming way, and to visualize them as virtual-workable objects

OOP là nghệ thuật quan sát các đối tượng trong tự nhiên rồi cố gắng nắm bắt những hành động cùng đặc tính của chúng và biểu diễn dưới dạng đối tượng ảo trong ngôn ngữ lập trình

Ví dụ:

Xe (đời thực)-> class Xe(lập trình) -> Đối tượng Xe
Trong đó 
Xe thật thì có màu, số bánh, tên xe
-> Class Xe (Biến màu, biến Số bánh. biến Tên)
-> Đối tượng xe(Màu,Số bánh, tên)

Lập trình hướng đối tượng luôn đi kèm 4 đặc điểm chính

Abstraction : tính trừu tượng.
Encapsulation : tính đóng gói.
Inheritance : tính kế thừa.
Polymorphism : tính đa hình.

Các thuộc tính của lập trình hướng đối tượng

  • Tính đóng gói (encapsulation) và che giấu thông tin (information hiding)
    Trước khi tìm hiểu tính đóng gói và che giấu thông tin, các bạn nên tìm hiểu cú pháp ngôn ngữ các thể hiện tính chất public, private, protected …

Cú pháp ngôn ngữ

Hình ảnh trên là một ví dụ về tính đóng gói và che giấu thông tin trong lập trình OOP

Như bạn thấy, viên thuốc được bao quanh bởi lớp vỏ chính là Class
Method và Variable là những thành phần bên trong viên thuốc, được bao lại, che giấu đi và không thể nhìn hoặc sử dụng nếu Class bên ngoài không cho phép.

Như vậy tính đóng gói có thể hiểu: Gói dữ liệu (data, ~ biến, trạng thái) và mã chương trình (code, ~ phương thức) thành một cục gọi là lớp (class) để dễ quản lí. Trong cục này thường data rất rối rắm, không tiện cho người không có trách nhiệm truy cập trực tiếp, nên thường ta sẽ che dấu data đi, chỉ để lòi phương thức ra ngoài

Khái niệm: Kế thừa là cách tạo lớp mới từ các lớp đã được định nghĩa từ trước

Lớp cha có thể chia sẻ dữ liệu và phương thức cho các lớp con, các lớp con khỏi phải định nghĩa lại những logic chung, giúp chương trình ngắn gọn. Nếu lớp cha là interface, thì lớp con sẽ di truyền những contract trừu tượng từ lớp cha

Giả sử ta có 2 class A và B:
- Class A: lớp cơ sở (lớp cha) - supper class
- Class B: lớp dẫn xuất (lớp con) - sub class

Class B có thể dùng được hầu hết các phương thức (hàm) và các thuộc tính
(biến) của lớp A ngoại trừ các các phương thức và các hàm các tính chất private.

Cài đặt: để cài đặt lớp kế thừa ta dùng toán tử “:” (trong C++, C#) và Extends (Java). Ví dụ B kế thừa A viết là: B:A

class A
{
    ....
}

class B:A // hoặc B extends A (java)
{
    ...
}

Chú ý:

  • Một lớp cha có thể có nhiều lớp con
  • Đến lượt mình mỗi lớp con lại có thể có các con khác
  • Trong C++ cho phép đa kế thừa (một lớp con có thể nhận hơn 1 lớp cha)
  • Java chỉ cho phép mỗi lớp con kế thừa 1 và chỉ một lớp cha/ từ khóa implement sẽ thay thế cho đa kế thừa từ C++

Ví dụ: Code Java

Class nhân viên Employee có các thuộc tính name và salary

class Employee{  
     private String name;
     private float salary;  

    public Employee() {
    }

    public Employee(String name, float salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getSalary() {
        return salary;
    }

    public void setSalary(float salary) {
        this.salary = salary;
    }
    //method say     
    public void SayHello(){
        System.out.println("Xin chaof");
    }
}  
//class Programmer có thêm 1 thuộc tính bonus kế thừa clas Employee
public class Programmer extends Employee{  
    private float bonus;  

    public float getBonus() {
        return bonus;
    }

    public void setBonus(float bonus) {
        this.bonus = bonus;
    }
    
    public Programmer(String name, float salary, float bonus) {
        super(name, salary);
        this.bonus = bonus;
    }

    public static void main(String args[]){ 
        Programmer p=new Programmer("Đỗ Trung Quân", 1000, 500);  
        
        System.out.println("Tên lập trình viên : " + p.getName());
        System.out.println("Luong Lap trinh vien : "+p.getSalary());  
        System.out.println("Bonus cua Lap trinh vien :"+p.getBonus());  
    }  
}  

Output:

Tên lập trình viên : Đỗ Trung Quân
Lương Lập trình viên : 1000.0
Bonus của lập trình viên : 500.0

Giải thích:

  • Class Programmer mình không khai báo 2 biến name và salary nhưng nó vẫn sử dụng được. Vì Programmer đã kế thừa class Employee -> nó có thể sử dụng lại các constructor và get/set từ class cha. Kêu gọi chúng bằng từ khóa “super”

Tuy nhiên trong C++ không cho phép làm điều này nếu 2 biến ở class cha là private. Bạn có thể tham khảo link bên dưới để biết cách truy cập biến và hàm private lớp cha từ lớp con trong C++
-> Lớp bạn và lớp dẫn xuất trong C++

(chú ý, trong java không có override biến mà chỉ có method).

Nếu bạn khai báo 1 biến name hoặc salary khác trong clas Programmer. Nó sẽ là 2 biến hoàn toàn khác 2 biến trong class Employee

  • Tính trừu tượng, phương thức ảo:

Có câu “program to interfaces, not to concrete implementations”. Nghĩa là khi viết chương trình theo phong cách hướng đối tượng, khi thiết kế các đối tượng, ta cần rút tỉa ra những đặc trưng của chúng, rồi trừu tượng hóa thành các interface, và thiết kế xem chúng sẽ tương tác với nhau như thế nào. Nói cách khác, chúng ta định ra các interface và các contract mà chúng cần thỏa mãn.

Nói 1 cách đơn giản thì bạn chỉ cần khai báo hàm trong lớp cha (access modifier, phương thức trả về, tên hàm, tham số truyền vào). Còn định nghĩa trong phần thân hàm bạn không cần quan tâm đến. Vì nó sẽ được override lại từ class kế thừa.

Định nghĩa: Đây là khả năng của chương trình bỏ qua hay không chú ý đến một số khía cạnh của thông tin mà nó đang trực tiếp làm việc lên, nghĩa là nó có khả năng tập trung vào những cốt lõi cần thiết (chỉ khai báo). Mỗi đối tượng phục vụ như là một “động tử” có thể hoàn tất các công việc một cách nội bộ, báo cáo, thay đổi trạng thái của nó và liên lạc với các đối tượng khác mà không cần cho biết làm cách nào đối tượng tiến hành được các thao tác (sự override từ lớp con). Tính chất này thường được gọi là sự trừu tượng của dữ liệu.

  • Phương thức ảo là phương thức được định nghĩa ở lớp cơ sở (lớp cha) mà các lớp dẫn xuất (lớp con) muốn sử dụng phải định nghĩa lại. Dùng từ khoá virtual (c++) hay abstract (java)để khai báo phương thức ảo:
  • Trong Class ảo có thể có phương thức ảo hoặc không nhưng phương thức ảo bắt buộc phải ở trong class ảo
  • phương thức ảo không chứa body
  • Khi kế thừa class ảo, bắt buộc phải viết lại phương thức ảo của nó

Code C++

virtual <kiểu trả về> <tên phương thức >(<d/s tham số>)
{
    ....
}

Code java

abstract <kiểu trả về> <tên phương thức >(<d/s tham số>)
{
    ....
}

Ví dụ

//Phải thêm từ khóa abstract trước class nếu nó chưa method abstract
abstract class Employee{  

    //method Abstract không có body
    abstract public void SayHelloAbstract(String name);
    
    public void SayHello(){
        System.out.println("Xin chaof");
    }
}  

Viết lại class Programmer

class Programmer extends Employee{  
    private float bonus;  

    public float getBonus() {
        return bonus;
    }

    public void setBonus(float bonus) {
        this.bonus = bonus;
    }
    @Override // viết lại phương thức SayHelloAbstract
    public void SayHelloAbstract(String name){
        System.out.println("Xin chào : " + name);
    }
    public Programmer(String name, float salary, int bonus) {
        super(name, salary);
        this.bonus = bonus;
    }

    public static void main(String args[]){ 
        Programmer p=new Programmer("Đỗ Trung Quân", 1000, 500);  

        p.SayHelloAbstract(p.getName());
        System.out.println("Luong của bạn tháng này : "+p.getSalary());  
        System.out.println("Bonus cua bạn tháng này : "+p.getBonus());  
    }  
}  

Output:

Xin chào : Đỗ Trung Quân
Lương của bạn tháng này : 1000.0
Bonus cua bạn tháng này : 500.0

-Tính đa hình (polymorphism)

  • poly (nhiều) + phism(body) = nhiều hình thái. Tính đa hình được thể hiện qua việc viết lại các method(hàm) từ class cha thông qua class kế thừa nó hoặc việc triển khai các interface.

Giả sử ta có 1 class abstract Animals và 3 class Ducks, Cats, Dogs sẽ kế thừa animals
Trong hình ảnh vịt. chó và mèo đều kế thừa class Animals

abstract class Animals{
    private String name;

    public Animals() {
    }

    public Animals(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public void Speak(String name, String sound){
        System.out.println("Animals Speak!" );
    }
}

//Class Ducks kế thừa Animals

class Ducks extends Animals{

    public Ducks(String name) {
        super(name);
    }
    
    @Override //viết lại hàm peak
    public void Speak(String name, String sound){
        System.out.println(name + " speaks: " + sound);
    }
}

//Class Dogs

class Dogs extends Animals{

    public Dogs(String name) {
        super(name);
    }
    
    @Override //viết lại hàm peak
    public void Speak(String name, String sound){
        System.out.println(name + " speaks: " + sound);
    }
}

//Class Cat

class Cats extends Animals{

    public Cats(String name) {
        super(name);
    }
}

Và hàm main

public static void main(String[] args) {

        //Tạo ra 3 đối tượng dog, cat ,duck
        Dogs dog = new Dogs("Dog");
        Ducks duck = new Ducks("Duck");
        Cats cat = new Cats("Cat");
        // gọi hàm Speak
        dog.Speak(dog.getName(), "Woof");
        duck.Speak(duck.getName(), "Quack");
        cat.Speak(cat.getName(), "Meow");
    }

Output:

Dog speaks: Woof
Duck speaks: Quack
Animals Speak!

Ở trên mình đã không override lại method Speak cho class Cats và bạn thấy. Khi mình gọi hàm nó sẽ refer đến Speak ở class cha (Animals) và in ra “Animals Speake!” thay vì “Cat speak: Meow” Như Dog và Duck.

Và đó là 4 đặc điểm chính trong lập trình hướng đối tượng OOP

24 Likes

@htwap Bạn tham khảo nhé.

5 Likes

vâng ạ e đi học đã, tối về e sẽ đọc.
cám ơn a nha

5 Likes

đại ca sao e nhớ hùi trc xài cin hay cout đều phải khai báo using namespace std; hoặc mỗi hàm phải std::cin hay std::cout mà :frowning:

4 Likes
2 Likes

ý e ở đây là a @Is2IT k khai báo thư viện cho hàm cin cout a Đạt! chứ 2 cái đó e phân biệt đc từ video của a rồi ! ý định đen tối của e là bắt bẻ a @Is2IT để a ghét e! khi mà a ghét thì e post cái j đó a sẽ quan tâm và dập e( ví dụ giải ngu, hỏi ngu, code xấu ). Như thế mới ngon :blush:

5 Likes

oh, có iostream là dùng được cin, cout rồi mà :smile:

3 Likes

ủa sao lúc trước a làm trong visual studio a nói using namespace std; dùng khi ta cần nhiều lần sử dụng cin,cout,endl; mà ! ko khai báo thì trc mỗi cin, cout,endl; là phải thêm std:: mà! bjo là sao a? mới update hay là chỉ áp dụng cho ng mới học lập trình thôi a?

1 Like

Đây là code trên Turbo C dùng bản ISO khác có hỗ trợ viết thế này bạn nhé
Có đuôi .h thì không cần có using namespace vẫn dùng được cin vs cout. Bạn bỏ .h chỉ viết iostream là thư viện C++ thì cần khai báo không gian tên namespace.

Trong visual và blockcode dùng bản ISO C++ khác và compiler là mingw của gcc. Theo mình nhớ là như vậy. Bạn thử ở visual hay blockcode xem đúng không nhé.

2 Likes

cho mình hỏi public và protected khác nhau thế nào?

1 Like

Theo Wiki

C++ xây dựng tính đóng bằng cách cho phép mọi thành viên của một lớp có thể được khai báo bằng các từ khoá public, private, hay protected. (xem thêm các khái niệm cơ bản trong ngôn ngữ OOP). Một thành viên private chỉ có thể được truy cập từ các phương pháp (hàm nội tại) là thành viên của chính lớp đó hay được truy cập từ các hàm và các lớp được đặc biệt cho phép sử dụng bằng cách dùng từ khóa friend. Một thành viên protected của một lớp sẽ có thể truy cập được từ các thành viên (nào đó) của các lớp có tính kế thừa của nó hay cũng có thể truy cập được từ các thành viện của chính lớp đó và của mọi thành viên friend.

Theo ngu kiến của mình

private -> tao là bí mật, muốn biết thông tin gì từ tao phải thông qua tao (nghĩa là tạo thêm 1 hàm return bên trong)
ví dụ ta tạo 1 biến private int abc = 5 ở class songuyen, class khác class songuyen thì không thể truy cập vào class songuyen, mà phải tạo dựng phương thức trả về giá trị abc

int return_abc() {return abc;}

Còn public-> tên nó rồi đó, tao có cái này tao public ra, thằng nào muốn dùng thì dùng, tao khôg quan tâm

Còn protected thì nó hơi khác tý, kiểu “nhà ai nấy dùng”, đồ của “cha” thì con có thể dùng :smiley:

lâu rồi cũng không ôn lại OOP khôgng biết có nhớ nhầm không :smiley: Nếu nhớ nhầm thì mong các bác bỏ qua :smiley:

3 Likes

có ích ghê :smiley: đang tự tìm hiểu về OOP :smiley:

2 Likes

Cảm ơn bạn đã quan tâm!

1 Like

vậy mọi người có thể phân biệt cho t, lớp bạn và lớp dẫn xuất khác nhau ntn ko ạ? lúc nào thì sử dụng lớp bạn và lúc nào sử dụng lớp dẫn xuất ạ? :relaxed::relaxed::relaxed:

Vì câu hỏi của bạn liên quan đến vấn đề khác nên mình trả lời ở đây nhé
Lớp bạn và lớp dẫn xuất trong C++

cái khái niệm OOP, em thấy nó chung chung quá, hôm trước có người hỏi lập trình OOP là gì, mình không biết trả lời thế nào, chỉ có tác dụng của nó là mình thấy tường minh. và mấy cái đóng gói và trừu tượng thì định nghĩa là gì , anh @ltd ơi

OOP nghĩa là Object oriented programming. (Lập trình hướng đối tượng). Được phát triển từ những năm 2000.

Nhìn vào tên của nó em cũng có thể hình dung ra được. Ngôn ngữ lập trình này giải quyết các bài toán từ nhỏ đến lớn bằng cách hướng chúng đến từng đối tượng cụ thể và sử lý.

Ví dụ:
Xe (đời thực)-> class Xe(lập trình) -> Đối tượng Xe
Trong đó
Xe thật thì có màu, số bánh, tên xe
-> Class Xe (Biến màu, biến Số bánh. biến Tên)
-> Đối tượng xe(Màu,Số bánh, tên)

Nghĩa là sẽ biểu diễn các thực thể ở đời sống thật thành những đối tượng ở trong lập trình mà hành động của thực thể chính là các Hàm (method) đặc điểm của thực thể sẽ là các biến (variable) trong class.

Và lập trình hướng đối tượng luôn đi kèm 4 đặc điểm chính

  • Abstraction : tính trừu tượng.
  • Encapsulation : tính đóng gói.
  • Inheritance : tính kế thừa.
  • Polymorphism : tính đa hình.
3 Likes

Bài viết hay quá, giúp mình ôn lại mấy kiến thức cơ bản. Thank anh @qtd :kissing_heart:

Cơ mà nếu anh rảnh thì viết về vấn đề Pass-by-value trong Java nha anh. :grin:

1 Like

OK em, trước đó anh dự định viết 1 seri về Java + Spring framework with Restful service and Hibernate. Em theo dõi nhé!

3 Likes

Tính đa hình trong OOP thường dùng để thay đổi kiểu của object.
Ví dụ cat dog duck đc kế thừa từ animal. Mình muốn đưa nó vào 1 aray thì mình phải thay đổi các đối tượng thành một kiểu giữ liệu:
Animal[] list = new Animal[50];
Animal cat = new Cat();
Animal dog = new Dog();
Animal duck = new Duck();

Không biết mình phát biểu thế có đúng không?
Và cho mình hỏi 1 câu nữa là: đa hình java và c# khác nhau chổ nào?

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?