CodeGym /Blog Java /Ngẫu nhiên /Nguyên tắc của OOP

Nguyên tắc của OOP

Xuất bản trong nhóm
Java là một ngôn ngữ hướng đối tượng. Điều này có nghĩa là bạn cần viết các chương trình Java bằng cách sử dụng mô hình hướng đối tượng. Và mô hình này đòi hỏi phải sử dụng các đối tượng và lớp trong chương trình của bạn. Hãy thử sử dụng các ví dụ để hiểu lớp và đối tượng là gì, cũng như cách áp dụng các nguyên tắc OOP cơ bản (trừu tượng, kế thừa, đa hình và đóng gói) trong thực tế.

Đối tượng là gì?

Thế giới chúng ta đang sống được tạo thành từ các đối tượng. Nhìn xung quanh, chúng ta có thể thấy xung quanh mình là nhà cửa, cây cối, ô tô, đồ đạc, bát đĩa và máy tính. Tất cả những thứ này đều là đối tượng và mỗi thứ có một tập hợp các đặc điểm, hành vi và mục đích cụ thể. Chúng ta đã quen với các đối tượng và chúng ta luôn sử dụng chúng cho những mục đích rất cụ thể. Ví dụ, nếu chúng ta cần đi làm, chúng ta sử dụng ô tô. Nếu chúng ta muốn ăn, chúng ta dùng bát đĩa. Và nếu chúng tôi muốn nghỉ ngơi, chúng tôi tìm một chiếc ghế sofa thoải mái. Con người đã quen suy nghĩ dưới dạng các đối tượng để giải quyết các vấn đề trong cuộc sống hàng ngày. Đây là một lý do tại sao các đối tượng được sử dụng trong lập trình. Cách tiếp cận này được gọi là lập trình hướng đối tượng. Hãy đưa ra một ví dụ. Hãy tưởng tượng rằng bạn đã phát triển một chiếc điện thoại mới và muốn bắt đầu sản xuất hàng loạt. Là nhà phát triển điện thoại, bạn biết nó dùng để làm gì, nó hoạt động như thế nào và các bộ phận của nó là gì (thân máy, micrô, loa, dây, nút, v.v.). Hơn nữa, chỉ có bạn mới biết cách kết nối các bộ phận này. Nhưng bạn không có kế hoạch tự mình sản xuất điện thoại — bạn có cả một nhóm công nhân để làm việc này. Để không cần phải giải thích nhiều lần cách kết nối các bộ phận của điện thoại và để đảm bảo rằng tất cả các điện thoại đều được sản xuất theo cùng một cách, trước khi bắt đầu sản xuất chúng, bạn cần tạo một bản vẽ mô tả cách tổ chức điện thoại. Trong OOP, chúng tôi gọi một mô tả, bản vẽ, sơ đồ hoặc mẫu như vậy là một lớp. Nó tạo cơ sở để tạo các đối tượng khi chương trình đang chạy. Một lớp là một mô tả về các đối tượng thuộc loại nhất định — giống như một mẫu chung bao gồm các trường, phương thức và hàm tạo. Một đối tượng là một thể hiện của một lớp.

trừu tượng

Bây giờ chúng ta hãy nghĩ về cách chúng ta có thể di chuyển từ một đối tượng trong thế giới thực sang một đối tượng trong chương trình. Chúng tôi sẽ sử dụng điện thoại làm ví dụ. Phương tiện liên lạc này có lịch sử kéo dài hơn 100 năm. Điện thoại hiện đại là một thiết bị phức tạp hơn nhiều so với thiết bị tiền nhiệm thế kỷ 19 của nó. Khi sử dụng điện thoại, chúng tôi không nghĩ về tổ chức của nó và các quy trình xảy ra bên trong nó. Chúng tôi chỉ cần sử dụng các chức năng do nhà phát triển điện thoại cung cấp: các nút hoặc màn hình cảm ứng để nhập số điện thoại và thực hiện cuộc gọi. Một trong những giao diện điện thoại đầu tiên là một tay quay cần được xoay để thực hiện cuộc gọi. Tất nhiên, điều này không thuận tiện lắm. Nhưng nó đã hoàn thành chức năng của mình một cách hoàn hảo. Nếu bạn so sánh những chiếc điện thoại hiện đại nhất và những chiếc điện thoại đầu tiên, bạn có thể xác định ngay các chức năng quan trọng nhất đối với thiết bị cuối thế kỷ 19 và đối với điện thoại thông minh hiện đại. Đó là khả năng thực hiện cuộc gọi và khả năng nhận cuộc gọi. Trên thực tế, đây là thứ khiến điện thoại trở thành điện thoại chứ không phải thứ gì khác. Bây giờ chỉ cần áp dụng một nguyên tắc của OOP: xác định thông tin và đặc điểm quan trọng nhất của đối tượng. Nguyên tắc này được gọi là trừu tượng hóa. Trong OOP, tính trừu tượng cũng có thể được định nghĩa là một phương thức biểu diễn các phần tử của tác vụ trong thế giới thực dưới dạng các đối tượng trong chương trình. Tính trừu tượng luôn gắn liền với việc khái quát hóa các thuộc tính nhất định của một đối tượng, vì vậy điều chính yếu là tách thông tin có ý nghĩa khỏi thông tin không đáng kể trong bối cảnh của nhiệm vụ hiện tại. Ngoài ra, có thể có một số mức độ trừu tượng. Cho phép' Hãy thử áp dụng nguyên tắc trừu tượng cho điện thoại của chúng ta. Để bắt đầu, chúng ta sẽ xác định các loại điện thoại phổ biến nhất — từ những chiếc điện thoại đầu tiên cho đến những chiếc điện thoại ngày nay. Ví dụ, chúng ta có thể biểu diễn chúng dưới dạng biểu đồ trong Hình 1. Nguyên tắc của OOP - 2Sử dụng sự trừu tượng hóa, giờ đây chúng ta có thể xác định thông tin chung trong hệ thống phân cấp đối tượng này: đối tượng trừu tượng chung (điện thoại), các đặc điểm chung của điện thoại (ví dụ: năm tạo ra nó) và giao diện chung (tất cả các điện thoại đều có thể nhận và thực hiện cuộc gọi). Đây là giao diện của nó trong Java:

public abstract class AbstractPhone {
    private int year;

    public AbstractPhone(int year) {
        this.year = year;
    }
    public abstract void call(int outgoingNumber);
    public abstract void ring(int incomingNumber);
}
Trong một chương trình, chúng ta có thể tạo các loại điện thoại mới bằng cách sử dụng lớp trừu tượng này và áp dụng các nguyên tắc cơ bản khác của OOP mà chúng ta sẽ khám phá bên dưới.

đóng gói

Với sự trừu tượng, chúng tôi xác định những gì là chung cho tất cả các đối tượng. Nhưng mỗi loại điện thoại là duy nhất, bằng cách nào đó khác với những loại khác. Trong một chương trình, làm thế nào để chúng ta vẽ ranh giới và xác định tính cá nhân này? Chúng tôi làm như thế nào để không ai có thể vô tình hay cố ý làm hỏng điện thoại của chúng tôi hoặc cố chuyển đổi kiểu máy này sang kiểu máy khác? Trong thế giới thực, câu trả lời rất rõ ràng: bạn cần đặt tất cả các bộ phận vào vỏ điện thoại. Rốt cuộc, nếu bạn không — thay vì để tất cả các bộ phận bên trong của điện thoại và dây kết nối ở bên ngoài — một số người thử nghiệm tò mò chắc chắn sẽ muốn "cải tiến" điện thoại của chúng tôi. Để ngăn chặn việc mày mò như vậy, nguyên tắc đóng gói được sử dụng trong thiết kế và hoạt động của một đối tượng. Nguyên tắc này nói rằng các thuộc tính và hành vi của một đối tượng được kết hợp trong một lớp duy nhất, đối tượng' triển khai nội bộ của s bị ẩn khỏi người dùng và giao diện chung được cung cấp để làm việc với đối tượng. Nhiệm vụ của lập trình viên là xác định thuộc tính và phương thức nào của đối tượng sẽ có sẵn để truy cập công khai và chi tiết triển khai nội bộ nào sẽ không thể truy cập được.

Đóng gói và kiểm soát truy cập

Giả sử rằng thông tin về một chiếc điện thoại (năm sản xuất hoặc logo của nhà sản xuất) được khắc ở mặt sau khi sản xuất. Thông tin (trạng thái của nó) là dành riêng cho mô hình cụ thể này. Có thể nói rằng nhà sản xuất đã đảm bảo rằng thông tin này là bất biến — không ai có thể nghĩ đến việc xóa hình khắc. Trong thế giới Java, một lớp mô tả trạng thái của các đối tượng trong tương lai bằng cách sử dụng các trường và hành vi của chúng được mô tả bằng các phương thức. Quyền truy cập vào trạng thái và hành vi của đối tượng được kiểm soát bằng cách sử dụng các công cụ sửa đổi được áp dụng cho các trường và phương thức: riêng tư, được bảo vệ, công khai và mặc định. Ví dụ: chúng tôi đã quyết định rằng năm sản xuất, tên nhà sản xuất và một trong các phương thức là chi tiết triển khai nội bộ của lớp và không thể thay đổi bởi các đối tượng khác trong chương trình. Trong mã,

public class SomePhone {

    private int year;
    private String company;
    public SomePhone(int year, String company) {
        this.year = year;
        this.company = company;
    }
private void openConnection(){
    // findSwitch
    // openNewConnection...
}
public void call() {
    openConnection();
    System.out.println("Calling");
}

public void ring() {
    System.out.println("Ring-ring");
}

 }
Công cụ sửa đổi riêng cho phép các trường và phương thức của lớp chỉ được truy cập trong lớp này. Điều này có nghĩa là không thể truy cập các trường riêng tư từ bên ngoài, vì không thể gọi các phương thức riêng tư. Việc hạn chế quyền truy cập vào phương thức openConnection cũng giúp chúng ta có khả năng tự do thay đổi cách triển khai bên trong của phương thức, vì phương thức này được đảm bảo không bị sử dụng bởi hoặc làm gián đoạn công việc của các đối tượng khác. Để làm việc với đối tượng của chúng tôi, chúng tôi để lại các phương thức gọiđổ chuông bằng công cụ sửa đổi công khai. Cung cấp các phương thức công khai để làm việc với các đối tượng cũng là một phần của đóng gói, vì nếu quyền truy cập bị từ chối hoàn toàn, nó sẽ trở nên vô dụng.

Di sản

Chúng ta hãy xem sơ đồ của điện thoại. Bạn có thể thấy rằng đó là một hệ thống phân cấp trong đó một mô hình có tất cả các tính năng của các mô hình nằm ở vị trí cao hơn dọc theo nhánh của nó và thêm một số tính năng của riêng nó. Ví dụ: điện thoại thông minh sử dụng mạng di động để liên lạc (có các thuộc tính của điện thoại di động), không dây và di động (có các thuộc tính của điện thoại không dây) và có thể nhận và thực hiện cuộc gọi (có các thuộc tính của điện thoại). Những gì chúng ta có ở đây là sự kế thừa các thuộc tính của đối tượng. Trong lập trình, kế thừa có nghĩa là sử dụng các lớp hiện có để định nghĩa các lớp mới. Hãy xem xét một ví dụ về việc sử dụng tính kế thừa để tạo một lớp điện thoại thông minh. Tất cả các điện thoại không dây đều được cấp nguồn bằng pin sạc, có thời lượng pin nhất định. Theo đó, chúng tôi thêm thuộc tính này vào lớp điện thoại không dây:

public abstract class CordlessPhone extends AbstractPhone {

    private int hour;

    public CordlessPhone (int year, int hour) {
        super(year);
        this.hour = hour;
    }
    }
Điện thoại di động kế thừa các thuộc tính của điện thoại không dây và chúng tôi triển khai các phương thức gọiđổ chuông trong lớp này:

public class CellPhone extends CordlessPhone {
    public CellPhone(int year, int hour) {
        super(year, hour);
    }

    @Override
    public void call(int outgoingNumber) {
        System.out.println("Calling " + outgoingNumber);
    }

    @Override
    public void ring(int incomingNumber) {
        System.out.println("Incoming call from " + incomingNumber);
    }
}
Và cuối cùng, chúng ta có lớp điện thoại thông minh, không giống như điện thoại di động cổ điển, có một hệ điều hành chính thức. Bạn có thể mở rộng chức năng của điện thoại thông minh bằng cách thêm các chương trình mới có thể chạy trên hệ điều hành của nó. Trong mã, lớp có thể được mô tả như sau:

public class Smartphone extends CellPhone {
    
    private String operationSystem;

    public Smartphone(int year, int hour, String operationSystem) {
        super(year, hour);
        this.operationSystem = operationSystem;
    }
public void install(String program) {
    System.out.println("Installing " + program + " for " + operationSystem);
}

}
Như bạn có thể thấy, chúng tôi đã tạo khá nhiều mã mới để mô tả lớp Điện thoại thông minh , nhưng chúng tôi có một lớp mới với chức năng mới. Nguyên tắc này của OOP giúp giảm đáng kể số lượng mã Java cần thiết, do đó làm cho cuộc sống của lập trình viên trở nên dễ dàng hơn.

đa hình

Mặc dù có sự khác biệt về hình thức và thiết kế của nhiều loại điện thoại, nhưng chúng ta có thể xác định một số hành vi chung: tất cả chúng đều có thể nhận và thực hiện cuộc gọi và tất cả đều có một bộ điều khiển khá rõ ràng và đơn giản. Về mặt lập trình, nguyên tắc trừu tượng (mà chúng ta đã quen thuộc) cho phép chúng ta nói rằng các đối tượng điện thoại có một giao diện chung. Đó là lý do tại sao mọi người có thể dễ dàng sử dụng các kiểu điện thoại khác nhau có cùng nút điều khiển (nút cơ hoặc màn hình cảm ứng) mà không cần đi sâu vào các chi tiết kỹ thuật của thiết bị. Do đó, bạn sử dụng điện thoại di động liên tục và bạn có thể dễ dàng thực hiện cuộc gọi từ điện thoại cố định của bạn mình. Nguyên tắc của OOP nói rằng một chương trình có thể sử dụng các đối tượng có giao diện chung mà không có bất kỳ thông tin nào về cấu trúc bên trong của đối tượng được gọi là đa hình. Cho phép' Hãy tưởng tượng rằng chúng ta cần chương trình của mình để mô tả một người dùng có thể sử dụng bất kỳ điện thoại nào để gọi cho một người dùng khác. Đây là cách chúng ta có thể làm điều đó:

public class User {
    private String name;

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

    public void callAnotherUser(int number, AbstractPhone phone){
// And here's polymorphism: using the AbstractPhone type in the code!
        phone.call(number);
    }
}
 }
Bây giờ chúng tôi sẽ mô tả một số loại điện thoại. Một trong những chiếc điện thoại đầu tiên:

public class ThomasEdisonPhone extends AbstractPhone {

public ThomasEdisonPhone(int year) {
    super(year);
}
    @Override
    public void call(int outgoingNumber) {
        System.out.println("Crank the handle");
        System.out.println("What number would you like to connect to?");
    }

    @Override
    public void ring(int incomingNumber) {
        System.out.println("The phone is ringing");
    }
}
Điện thoại cố định thông thường:

public class Phone extends AbstractPhone {

    public Phone(int year) {
        super(year);
    }

    @Override
    public void call(int outgoingNumber) {
        System.out.println("Calling " + outgoingNumber);
    }

    @Override
    public void ring(int incomingNumber) {
        System.out.println("The phone is ringing");
    }
}
Và cuối cùng, một chiếc điện thoại video thú vị:

public class VideoPhone extends AbstractPhone {

    public VideoPhone(int year) {
        super(year);
    }
    @Override
    public void call(int outgoingNumber) {
        System.out.println("Connecting video call to " + outgoingNumber);
    }
    @Override
    public void ring(int incomingNumber) {
        System.out.println("Incoming video call from " + incomingNumber);
    }
  }
Chúng ta sẽ tạo các đối tượng trong phương thức main() và kiểm tra phương thức callAnotherUser() :

AbstractPhone firstPhone = new ThomasEdisonPhone(1879);
AbstractPhone phone = new Phone(1984);
AbstractPhone videoPhone=new VideoPhone(2018);
User user = new User("Jason");
user.callAnotherUser(224466, firstPhone);
// Crank the handle
// What number would you like to connect to?
user.callAnotherUser(224466, phone);
// Calling 224466
user.callAnotherUser(224466, videoPhone);
// Connecting video call to 224466
Gọi cùng một phương thức trên đối tượng người dùng sẽ tạo ra các kết quả khác nhau. Việc triển khai phương thức gọi cụ thể được chọn động bên trong phương thức callAnotherUser() dựa trên loại đối tượng cụ thể được truyền khi chương trình đang chạy. Đây là lợi thế chính của tính đa hình – khả năng chọn một triển khai trong thời gian chạy. Trong các ví dụ về các lớp điện thoại được đưa ra ở trên, chúng tôi đã sử dụng ghi đè phương thức — một thủ thuật trong đó chúng tôi thay đổi cách triển khai của một phương thức được xác định trong lớp cơ sở mà không thay đổi chữ ký của phương thức. Về cơ bản, điều này thay thế phương thức: phương thức mới được định nghĩa trong lớp con được gọi khi chương trình được thực thi. Thông thường, khi chúng ta ghi đè một phương thức, @Overridechú thích được sử dụng. Nó báo cho trình biên dịch kiểm tra chữ ký của các phương thức được ghi đè và ghi đè. Cuối cùng, để đảm bảo các chương trình Java của bạn nhất quán với các nguyên tắc của OOP, hãy làm theo các mẹo sau:
  • xác định các đặc điểm chính của đối tượng;
  • xác định các thuộc tính và hành vi chung và sử dụng tính kế thừa khi tạo các lớp;
  • sử dụng các loại trừu tượng để mô tả các đối tượng;
  • cố gắng luôn ẩn các phương thức và trường liên quan đến triển khai nội bộ của lớp.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION