來源:The Geek Asian 讓我們來看看物件導向程式設計的四個基本原理,並試著了解它們是如何運作的。物件導向程式設計(OOP)是主要的程式設計範式之一。它可以很簡單,也可以相反,非常複雜。這完全取決於您決定如何開發應用程式。 OOP 有 4 個支柱:
- 封裝。
- 遺產。
- 抽象。
- 多態性。
1. 封裝
我們都研究過封裝,即隱藏資料元素並允許使用者使用公共方法存取資料。我們稱這些為 getter 和 setter。現在讓我們忘記這一點並找到一個更簡單的定義。封裝是一種限制使用者直接更改資料成員或類別變數以維護資料完整性的方法。我們如何做到這一點?我們透過將存取修飾符切換為私有並公開可用於存取資料的公共方法來限制對變數的存取。下面我們來看看具體的例子。這將幫助我們理解如何使用封裝來維護資料完整性。不含封裝:/**
* @author thegeekyasian.com
*/
public class Account {
public double balance;
public static void main(String[] args) {
Account theGeekyAsianAccount = new Account();
theGeekyAsianAccount.balance = -54;
}
}
在上面的程式碼片段中,main()方法直接存取balance變數。這允許用戶將任何雙精度值設定為Account類別的餘額變數。如果允許任何人將餘額設為任何無效數字(例如本例中的 -54),我們可能會失去資料完整性。附封裝:
/**
* @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
}
}
在此程式碼中,我們限制了對balance變數的訪問,並添加了一個setBalance()方法,允許使用者設定Account的餘額值。setter 在將提供的值分配給變數之前檢查它。如果該值小於零,則會引發異常。這確保了資料的完整性不會受到損害。在解釋了上面的例子之後,我希望封裝作為 OOP 四大支柱之一的價值是顯而易見的。
2. 繼承
繼承是一種取得具有共同特徵的另一個類別的屬性的方法。這使我們能夠提高可重用性並減少程式碼重複。當子元素繼承其父元素的屬性時,該方法也具有子父互動的原理。讓我們深入研究兩個簡單的範例,看看繼承如何讓程式碼更簡單、更可重複使用。沒有繼承:/**
* @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;
}
}
這兩個相似的類別共用width屬性和getArea()方法。我們可以透過進行一些重構來增加程式碼重用,其中Square類別最終繼承自Rectangle類別。具有繼承性:
/**
* @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
}
}
透過簡單地擴展Rectangle類,我們得到了Square類作為Rectangle類型。這意味著它繼承了Square和Rectangle共有的所有屬性。在上面的範例中,我們看到繼承如何在使程式碼可重複使用方面發揮重要作用。它還允許類別繼承其父類別的行為。
3. 抽象
抽像是一種透過隱藏物件不必要或不相關的細節來僅向使用者呈現基本細節的技術。它有助於降低用戶側的操作複雜性。抽象化允許我們向使用者提供一個簡單的介面,而不需要複雜的細節來執行操作。簡而言之,它使用戶能夠駕駛汽車,而無需他們確切了解引擎的工作原理。讓我們先來看一個例子,然後再討論抽像如何幫助我們。/**
* @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
}
}
在上面的程式碼中, lock()、unlock()和startCar() 方法是公共的,其餘方法是類別的私有方法。我們讓使用者「駕駛汽車」變得更加容易。當然,他可以在使用 startCar()啟動汽車之前手動檢查checkFuel()和checkBattery(),但這只會使過程變得複雜。透過上面的程式碼,使用者所需要做的就是使用startCar(),該類別將處理其餘的事情。這就是我們所說的抽象。
4.多態性
OOP 四大支柱中的最後一個也是最重要的是多態性。多態性意味著「多種形式」。顧名思義,它是一個允許您以多種或不同方式執行操作的函數。當我們談論多態性時,除非我們談論它的類型,否則沒有太多可討論的。多態性有兩種類型:- 方法重載-靜態多型(Static Binding)。
- 方法重寫-動態多型(動態綁定)。
方法重載-靜態多態:
方法重載或靜態多態性,也稱為靜態綁定或編譯時綁定,是一種在編譯時決定方法呼叫的類型。方法重載允許我們擁有多個具有相同名稱、不同參數資料類型或不同數量參數或兩者兼而有之的方法。但問題是,為什麼方法重載(或靜態多態性)有用?讓我們看一下下面的範例,以便更好地理解方法重載。沒有方法重載:/**
* @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);
}
}
在上面的範例中,我們建立了兩個具有不同名稱的方法,只是為了將兩種不同類型的數字相加。如果我們繼續進行類似的實現,我們將有多個具有不同名稱的方法。這將降低程式碼的品質和可用性。為了改善這一點,我們可以透過對不同的方法使用相同的名稱來使用方法重載。這將允許用戶有一個選項作為對不同類型的數字求和的入口點。當兩個或多個方法具有相同名稱但不同參數時,方法重載就會起作用。傳回類型可以相同或不同。但是如果兩個方法具有相同的名稱、相同的參數,但返回類型不同,那麼這將導致重載和編譯錯誤!使用方法重載:
/**
* @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);
}
}
在相同的程式碼中,透過一些小的更改,我們能夠重載這兩個方法,使兩個方法的名稱相同。用戶現在可以將其特定資料類型指定為方法參數。然後它將根據所提供的資料類型執行操作。此方法綁定是在編譯時完成的,因為編譯器知道將使用指定的參數類型呼叫哪個方法。這就是為什麼我們稱之為編譯時綁定。
方法重寫 - 動態多態性:
與方法重載不同,方法重寫允許您具有與多個方法完全相同的簽名,但它們必須位於多個不同的類別中。問題是,它有什麼特別之處?這些類別具有 IS-A 關係,即它們必須相互繼承。換句話說,在方法重寫或動態多態中,方法在執行時間被呼叫時動態處理。這是基於對其初始化的物件的引用來完成的。這是方法重寫的一個小例子:/**
* @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
}
}
在這個最重要的範例中,我們動態地將「Dog」和「Cat」類型的物件指派給「Animal」類型。這允許我們在運行時動態地呼叫引用實例的walk()方法。我們可以使用方法重寫(或動態多態性)來做到這一點。我們對 OOP 四大支柱的簡短討論到此結束,我希望您發現它有用。
GO TO FULL VERSION