JavaRush /Blog Java /Random-VI /Nghỉ giải lao #90. 4 trụ cột của lập trình hướng đối tượn...

Nghỉ giải lao #90. 4 trụ cột của lập trình hướng đối tượng

Xuất bản trong nhóm
Nguồn: The Geek Asian Chúng ta hãy xem xét bốn nguyên tắc cơ bản của lập trình hướng đối tượng và cố gắng hiểu cách chúng hoạt động. Lập trình hướng đối tượng (OOP) là một trong những mô hình lập trình chính. Nó có thể dễ dàng và đơn giản hoặc ngược lại, rất phức tạp. Tất cả phụ thuộc vào cách bạn quyết định phát triển ứng dụng của mình. Nghỉ giải lao #90.  4 trụ cột của lập trình hướng đối tượng - 1Có 4 trụ cột của OOP:
  1. Đóng gói.
  2. Di sản.
  3. Trừu tượng.
  4. Đa hình.
Bây giờ chúng ta sẽ thảo luận từng vấn đề với lời giải thích ngắn gọn và ví dụ mã thực tế.

1. Đóng gói

Tất cả chúng ta đều đã nghiên cứu tính đóng gói để ẩn các phần tử dữ liệu và cho phép người dùng truy cập dữ liệu bằng các phương thức công khai. Chúng tôi gọi đây là getters và setters. Bây giờ chúng ta hãy quên điều này đi và tìm một định nghĩa đơn giản hơn. Đóng gói là một phương pháp hạn chế người dùng thay đổi trực tiếp các thành viên dữ liệu hoặc biến lớp để duy trì tính toàn vẹn của dữ liệu. Chung ta se lam như thê nao? Chúng tôi hạn chế quyền truy cập vào các biến bằng cách chuyển công cụ sửa đổi quyền truy cập sang chế độ riêng tư và hiển thị các phương thức công khai có thể được sử dụng để truy cập dữ liệu. Hãy cùng xem những ví dụ cụ thể dưới đây. Điều này sẽ giúp chúng tôi hiểu cách chúng tôi có thể sử dụng tính năng đóng gói để duy trì tính toàn vẹn của dữ liệu. Không có đóng gói:
/**
 * @author thegeekyasian.com
 */
public class Account {

  public double balance;

  public static void main(String[] args) {

  	Account theGeekyAsianAccount = new Account();

  	theGeekyAsianAccount.balance = -54;
  }
}
Trong đoạn mã trên, phương thức main() truy cập trực tiếp vào biến số dư . Điều này cho phép người dùng đặt bất kỳ giá trị kép nào cho biến số dư của lớp Tài khoản . Chúng tôi có thể làm mất tính toàn vẹn dữ liệu bằng cách cho phép bất kỳ ai đặt số dư thành bất kỳ số không hợp lệ nào, chẳng hạn như -54 trong trường hợp này. Với sự đóng gói:
/**
 * @author thegeekyasian.com
 */
public class Account {

  private double balance;

  public void setBalance(double balance) {

    if(balance >= 0) { // Validating input data in order to maintain data integrity
	  this.balance = balance;
    }

    throw new IllegalArgumentException("Balance cannot be less than zero (0)");
  }

  public static void main(String[] args) {

  	Account theGeekyAsianAccount = new Account();

  	theGeekyAsianAccount.setBalance(1); // Valid input - Allowed
  	theGeekyAsianAccount.setBalance(-55); // Stops user and throws exception
  }
}
Trong mã này, chúng tôi đã hạn chế quyền truy cập vào biến số dư và thêm phương thức setBalance() cho phép người dùng đặt giá trị số dư cho Tài khoản . Trình thiết lập sẽ kiểm tra giá trị được cung cấp trước khi gán nó cho biến. Nếu giá trị nhỏ hơn 0, một ngoại lệ sẽ được đưa ra. Điều này đảm bảo rằng tính toàn vẹn của dữ liệu không bị xâm phạm. Sau khi giải thích các ví dụ trên, tôi hy vọng giá trị của việc đóng gói như một trong bốn trụ cột của OOP là rõ ràng.

2. Thừa kế

Kế thừa là một phương pháp lấy các thuộc tính của một lớp khác có chung các đặc điểm chung. Điều này cho phép chúng tôi tăng khả năng sử dụng lại và giảm sự trùng lặp mã. Phương thức này còn có nguyên tắc tương tác giữa con và cha, khi phần tử con kế thừa các thuộc tính của phần tử cha. Chúng ta hãy đi sâu vào hai ví dụ nhanh và xem tính kế thừa làm cho mã đơn giản hơn và có thể tái sử dụng nhiều hơn như thế nào. Không có sự kế thừa:
/**
 * @author thegeekyasian
 */
public class Rectangle {

  private int width;
  private int height;

  public Rectangle(int width, int height) {
	this.width = width;
	this.height = height;
  }

  public int getArea() {
	return width * height;
  }
}

public class Square {

  private int width; // Duplicate property, also used in class Rectangle

  public Square(int width) {
	this.width = width;
  }

  public int getArea() { // Duplicate method, similar to the class Rectangle
	return this.width * this.width;
  }
}
Hai lớp tương tự có chung thuộc tính chiều rộng và phương thức getArea() . Chúng ta có thể tăng khả năng sử dụng lại mã bằng cách thực hiện một chút tái cấu trúc trong đó lớp Square kế thừa từ lớp Rectangle . Với sự kế thừa:
/**
 * @author thegeekyasian
 */
public class Rectangle {

  private int width;
  private int height;

  public Rectangle(int width, int height) {
	this.width = width;
	this.height = height;
  }

  public int getArea() {
	return width * height;
  }
}

public class Square extends Rectangle {

  public Square(int width) {
	super(width, width); // A rectangle with the same height as width is a square
  }
}
Chỉ cần mở rộng lớp Hình chữ nhật , chúng ta sẽ có được lớp Hình vuông thuộc loại Hình chữ nhật . Điều này có nghĩa là nó kế thừa tất cả các thuộc tính chung của Hình vuôngHình chữ nhật . Trong các ví dụ trên, chúng ta thấy tính kế thừa đóng vai trò quan trọng như thế nào trong việc tạo mã có thể sử dụng lại được. Nó cũng cho phép một lớp kế thừa hành vi của lớp cha của nó.

3. Trừu tượng

Trừu tượng hóa là một kỹ thuật chỉ trình bày các chi tiết cần thiết cho người dùng bằng cách ẩn các chi tiết không cần thiết hoặc không liên quan của một đối tượng. Nó giúp giảm độ phức tạp trong hoạt động về phía người dùng. Tính trừu tượng cho phép chúng ta cung cấp một giao diện đơn giản cho người dùng mà không yêu cầu các chi tiết phức tạp để thực hiện một hành động. Nói một cách đơn giản, nó mang lại cho người dùng khả năng lái ô tô mà không yêu cầu họ phải hiểu chính xác cách thức hoạt động của động cơ. Trước tiên hãy xem một ví dụ và sau đó thảo luận xem sự trừu tượng giúp ích cho chúng ta như thế nào.
/**
* @author thegeekyasian.com
*/
public class Car {

  public void lock() {}
  public void unlock() {}

  public void startCar() {

	checkFuel();
	checkBattery();
	whatHappensWhenTheCarStarts();
  }

  private void checkFuel() {
	// Check fuel level
  }

  private void checkBattery() {
	// Check car battery
  }

  private void whatHappensWhenTheCarStarts() {
	// Magic happens here
  }
}
Trong đoạn mã trên, các phương thức lock() , unlock()startCar() là công khai và các phương thức còn lại là riêng tư đối với lớp. Chúng tôi đã giúp người dùng “lái xe” dễ dàng hơn. Tất nhiên, anh ấy có thể kiểm tra thủ công checkFuel()checkBattery() trước khi khởi động xe bằng startCar() , nhưng điều đó sẽ chỉ làm phức tạp thêm quy trình. Với đoạn mã trên, tất cả những gì người dùng cần làm là sử dụng startCar() và lớp sẽ lo phần còn lại. Đây là những gì chúng ta gọi là trừu tượng.

4. Đa hình

Trụ cột cuối cùng và quan trọng nhất trong bốn trụ cột của OOP là tính đa hình. Đa hình có nghĩa là “nhiều hình thức”. Như tên cho thấy, đây là một chức năng cho phép bạn thực hiện một hành động theo nhiều cách khác nhau. Khi chúng ta nói về tính đa hình, không có nhiều điều để thảo luận trừ khi chúng ta nói về các loại của nó. Có hai loại đa hình:
  1. Nạp chồng phương thức - đa hình tĩnh (Static Binding).
  2. Ghi đè phương thức - đa hình động (Dynamic Binding).
Chúng ta hãy thảo luận về từng loại này và xem sự khác biệt giữa chúng là gì.

Nạp chồng phương thức - đa hình tĩnh:

Quá tải phương thức hoặc đa hình tĩnh, còn được gọi là Liên kết tĩnh hoặc liên kết thời gian biên dịch, là một kiểu trong đó các lệnh gọi phương thức được xác định tại thời gian biên dịch. Nạp chồng phương thức cho phép chúng ta có nhiều phương thức có cùng tên, có các kiểu dữ liệu tham số khác nhau hoặc số lượng tham số khác nhau hoặc cả hai. Nhưng câu hỏi đặt ra là tại sao nạp chồng phương thức (hoặc đa hình tĩnh) lại hữu ích? Hãy xem các ví dụ dưới đây để hiểu rõ hơn về nạp chồng phương thức. Không nạp chồng phương thức:
/**
* @author thegeekyasian.com
*/
public class Number {

  public void sumInt(int a, int b) {
	System.out.println("Sum: " + (a + b));
  }

  public void sumDouble(double a, double b) {
	System.out.println("Sum: " + (a + b));
  }

  public static void main(String[] args) {

	Number number = new Number();

	number.sumInt(1, 2);
	number.sumDouble(1.8, 2.5);
  }
}
Trong ví dụ trên, chúng ta đã tạo hai phương thức có tên khác nhau, chỉ để cộng hai loại số khác nhau. Nếu tiếp tục triển khai tương tự, chúng ta sẽ có nhiều phương thức với các tên khác nhau. Điều này sẽ làm giảm chất lượng và tính sẵn có của mã. Để cải thiện điều này, chúng ta có thể sử dụng nạp chồng phương thức bằng cách sử dụng cùng tên cho các phương thức khác nhau. Điều này sẽ cho phép người dùng có một tùy chọn làm điểm vào để tính tổng các loại số khác nhau. Nạp chồng phương thức hoạt động khi hai hoặc nhiều phương thức có cùng tên nhưng tham số khác nhau. Kiểu trả về có thể giống hoặc khác nhau. Nhưng nếu hai phương thức có cùng tên, cùng tham số, nhưng kiểu trả về khác nhau thì điều này sẽ gây ra tình trạng quá tải và lỗi biên dịch! Với quá tải phương thức:
/**
* @author thegeekyasian.com
*/
public class Number {

  public void sum(int a, int b) {
	System.out.println("Sum: " + (a + b));
  }

  public void sum(double a, double b) {
	System.out.println("Sum: " + (a + b));
  }

  public static void main(String[] args) {

	Number number = new Number();

	number.sum(1, 2);
	number.sum(1.8, 2.5);
  }
}
Trong cùng một mã, với một vài thay đổi nhỏ, chúng tôi có thể nạp chồng cả hai phương thức, làm cho tên của cả hai đều giống nhau. Bây giờ người dùng có thể chỉ định các loại dữ liệu cụ thể của họ làm tham số phương thức. Sau đó, nó sẽ thực hiện một hành động dựa trên kiểu dữ liệu được cung cấp. Liên kết phương thức này được thực hiện tại thời điểm biên dịch vì trình biên dịch biết phương thức nào sẽ được gọi với kiểu tham số đã chỉ định. Đó là lý do tại sao chúng tôi gọi nó là ràng buộc thời gian biên dịch.

Ghi đè phương thức - đa hình động:

Không giống như nạp chồng phương thức, ghi đè phương thức cho phép bạn có chữ ký giống hệt như nhiều phương thức, nhưng chúng phải thuộc nhiều lớp khác nhau. Câu hỏi là, nó có gì đặc biệt? Các lớp này có mối quan hệ IS-A, nghĩa là chúng phải kế thừa lẫn nhau. Nói cách khác, trong ghi đè phương thức hoặc đa hình động, các phương thức được xử lý động trong thời gian chạy khi phương thức đó được gọi. Điều này được thực hiện dựa trên tham chiếu đến đối tượng mà nó được khởi tạo. Đây là một ví dụ nhỏ về ghi đè phương thức:
/**
* @author thegeekyasian.com
*/
public class Animal {

  public void walk() {
	System.out.println("Animal walks");
  }
}

public class Cat extends Animal {

  @Override
  public void walk() {
	System.out.println("Cat walks");
  }
}

public class Dog extends Animal {

  @Override
  public void walk() {
	System.out.println("Dog walks");
  }
}

public class Main {

  public static void main(String[] args) {

	Animal animal = new Animal();
	animal.walk(); // Animal walks

	Cat cat = new Cat();
	cat.walk(); // Cat walks

	Dog dog = new Dog();
	dog.walk(); // Dog walks

	Animal animalCat = new Cat(); // Dynamic Polymorphism
	animalCat.walk(); // Cat walks

	Animal animalDog = new Dog(); // Dynamic Polymorphism
	animalDog.walk(); //Dog walks
  }
}
Trong ví dụ quan trọng hơn này, chúng tôi đã gán động các đối tượng thuộc loại “Chó” và “Mèo” thành loại “Động vật”. Điều này cho phép chúng ta gọi phương thức walk() trên các phiên bản được tham chiếu một cách linh hoạt khi chạy. Chúng ta có thể thực hiện việc này bằng cách ghi đè phương thức (hoặc đa hình động). Điều này kết thúc cuộc thảo luận ngắn gọn của chúng ta về bốn trụ cột của OOP và tôi hy vọng bạn thấy nó hữu ích.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION