我一直對電腦充滿好奇,總是會想:「好吧,我知道怎麼用,但它到底是怎麼運作的?」在這個過程中,我常常會做個思想實驗:如果讓我從零開始實現它,我會怎麼做?在本文中,我們將探討介面在物件導向程式設計中的工作原理(使用Java),然後在C語言中實作一個簡陋的介面版本。
我們的例子很簡單:計算車輛的價格。如果是汽車,價格將根據其最高速度計算;如果是摩托車,價格將根據其排氣量計算。我們先用介面定義車輛的行為:
<code class="language-java">public class Main { public interface Vehicle { Integer price(); } }</code>
這裡沒什麼特別的,只是一個傳回整數的方法。現在讓我們實作汽車類別:
<code class="language-java">public class Main { // ... public static class Car implements Vehicle { private final Integer speed; public Car(Integer speed) { this.speed = speed; } @Override public Integer price() { return speed * 60; } } }</code>
很經典:一個建構函式和price方法的實現,將速度乘以60。現在,讓我們實作摩托車類別:
<code class="language-java">public class Main { // ... public static class Motorcycle implements Vehicle { private final Integer cc; public Motorcycle(Integer cc) { this.cc = cc; } @Override public Integer price() { return cc * 10; } } }</code>
幾乎一樣,唯一的差別是現在我們將排氣量乘以10。然後,我們實現一個列印車輛價格的方法:
<code class="language-java">public class Main { // ... public static void printVehiclePrice(Vehicle vehicle) { System.out.println("$" + vehicle.price() + ".00"); } }</code>
沒什麼秘密。最後,我們的main方法:
<code class="language-java">public class Main { // ... public static void main(String[] args) { Car car = new Car(120); Motorcycle motorcycle = new Motorcycle(1000); printVehiclePrice(car); printVehiclePrice(motorcycle); } }</code>
<code>$ java Main.java 00.00 000.00</code>
這就是我們想要達到的模型,但是現在要在C語言中從零開始實作。
當我想到物件時,首先想到的是一組表示狀態的資料和操作和管理該狀態的方法。在C語言中表示資料集合最直接的方法是結構體。對於方法,最接近的方法是接收狀態作為參數的函數。此狀態將對應於類別中的this,例如:
<code class="language-c">typedef struct { int height_in_cm; int weight_in_kg; } Person; float person_bmi(Person *person) { float height_in_meters = (float)person->height_in_cm / 100; float bmi = (float)person->weight_in_kg / (height_in_meters * height_in_meters); return bmi; }</code>
在這裡,我們在Person結構體中定義了一個人的數據,並使用這些數據進行簡單的計算。這是我們在C語言中可以擁有的最接近類別的結構。也許在結構體中使用函數指標也是一個好主意?好吧,這留到下一篇文章再討論。
好的,我們有了一種類似類別的結構。現在,我們如何在C語言中定義介面?如果仔細想想,編譯器/解釋器不會做魔法來猜測哪些類別實作了介面。它可以在編譯時確定這一點,並將我們使用介面的所有部分替換為特定的類型。在編譯後的程式中,介面甚至不存在。
由於C語言編譯器沒有提供這種可能性,我們必須自己實作這個方案。我們需要知道所有實作我們介面的類型,並想辦法使用這些實作的函數。
首先,讓我們定義我們簡陋介面的框架。我們將創建一個枚舉,其中包含不同的實作和我們函數的簽名。
<code class="language-java">public class Main { public interface Vehicle { Integer price(); } }</code>
在這裡,我們定義了我們的枚舉,其中包含我們稍後將要實現的實作。這看起來可能不像,但這部分非常重要。接下來,我們聲明了vehicle_free函數(稍後將解釋)和vehicle_price函數,我們希望在我們的「類別」中實作這些函數。現在讓我們來看看汽車的實現:
<code class="language-java">public class Main { // ... public static class Car implements Vehicle { private final Integer speed; public Car(Integer speed) { this.speed = speed; } @Override public Integer price() { return speed * 60; } } }</code>
car_init函數在記憶體中初始化一個新的「物件」Car。在Java中,這將透過new自動完成。在這裡,我們需要手動完成。 vehicle_free函數將用於釋放先前初始化的任何「物件」分配的內存,使用car_free等實作。摩托車的實現非常相似:
<code class="language-java">public class Main { // ... public static class Motorcycle implements Vehicle { private final Integer cc; public Motorcycle(Integer cc) { this.cc = cc; } @Override public Integer price() { return cc * 10; } } }</code>
幾乎一樣,只是現在我們用VEHICLE_MOTORCYCLE初始化,並乘以10。現在讓我們來看看列印車輛價格的函數:
<code class="language-java">public class Main { // ... public static void printVehiclePrice(Vehicle vehicle) { System.out.println("$" + vehicle.price() + ".00"); } }</code>
如此簡單……這樣看來,我們似乎沒有做太多工作。現在,最後也是最重要的一點,我們必須實現我們在上面介面定義中聲明的函數,還記得嗎?幸運的是,我們甚至不需要考慮這個實作。我們總會有一個簡單的窮舉switch/case,僅此而已。
<code class="language-java">public class Main { // ... public static void main(String[] args) { Car car = new Car(120); Motorcycle motorcycle = new Motorcycle(1000); printVehiclePrice(car); printVehiclePrice(motorcycle); } }</code>
現在我們可以使用我們所做的一切:
<code>$ java Main.java 00.00 000.00</code>
<code class="language-c">typedef struct { int height_in_cm; int weight_in_kg; } Person; float person_bmi(Person *person) { float height_in_meters = (float)person->height_in_cm / 100; float bmi = (float)person->weight_in_kg / (height_in_meters * height_in_meters); return bmi; }</code>
成功了!但你可能會想:「好吧,這有什麼用?」
我最喜歡的專案類型之一是解析器,從解釋器到簡單的數學表達式解析器。通常,當您實作這些解析器時,會遇到稱為AST(抽象語法樹)的東西。顧名思義,它是一棵樹,它將表示您正在處理的語法,例如,變數聲明int foo = 10; 是AST的一個節點,它包含另外三個節點,一個類型節點,用於int,一個標識符節點,用於foo,以及一個表達式節點,用於10,該節點包含另一個值為10的整數節點。看到它有多複雜了嗎?
當我們在C語言中這樣做時,我們必須在包含多個字段的巨大結構體之間進行選擇,以表示任何可能的AST節點,或者使用多個小型結構體實現抽象定義,每個結構體表示不同的節點,就像我們在這裡用我們的「介面」所做的那樣。如果您想查看一個簡單的範例,在這個數學表達式解析器中,我實作了第二種方法。
編譯器或解釋器所做的任何事情都不是魔法。嘗試自己實現一些東西總是一個有趣的練習。希望這是一篇有益的閱讀。謝謝!
以上是C語言中的物件導向?從頭開始實作介面。的詳細內容。更多資訊請關注PHP中文網其他相關文章!