面向對象編程,看這篇就夠了

来源:https://www.cnblogs.com/bossma/archive/2023/12/12/17897584.html
-Advertisement-
Play Games

面向對象編程是一種強大而靈活的編程範式,它可以幫助我們更好地組織和管理代碼,提高代碼的可讀性和可維護性,這使得它特別適合用在大型工程項目中。然而,我們也需要註意其可能帶來的問題,尤其是在併發和複雜系統中。 ...


一、面向對象編程的概念

面向對象編程,是一種程式設計範式,也是一種編程語言的分類。它以對象作為程式的基本單元,將演算法和數據封裝其中,程式可以訪問和修改對象關聯的數據。這就像我們在真實世界中操作各種物體一樣,比如我們可以打開電視、調整音量、切換頻道,而不需要知道電視的內部如何工作。同樣,在面向對象編程中,我們可以操作對象,而不需要關心對象的內部結構和實現。

面向對象編程的主要組成部分是類和對象。類是一組具有相同屬性和功能的對象的抽象,就好比我們說的“汽車”這個概念,它具有顏色、型號、速度等屬性,有啟動、加速、剎車等功能。而對象則是類的實例,它是具體的,就像你家那輛紅色的賓士車,它就是汽車這個類的一個實例。

二、面向對象編程的特性

面向對象編程有三大特性,封裝、繼承和多態。

1. 封裝

封裝是把客觀事物封裝成抽象的類,並隱藏實現細節,使得代碼模塊化。比如,我們可以把“汽車”這個客觀事物封裝成一個類,這個類有顏色、型號等屬性,有啟動、加速、剎車等方法,而這些屬性和方法的具體實現則被隱藏起來,使用者只需要知道這個類有哪些屬性和方法,不需要知道這些方法是如何實現的。

2. 繼承

繼承是面向對象編程的另一個重要特性,它提供了一種無需重新編寫,使用現有類的所有功能併進行擴展的能力。比如,我們可以定義一個“電動車”類,它繼承了“汽車”類,就自動擁有了“汽車”類的所有屬性和方法,比如顏色、型號等屬性,啟動、加速、剎車等方法,然後我們還可以在“電動車”類上增加一些新的屬性和方法,比如電池容量、充電方法等。

3. 多態

多態是指同一操作作用於不同的對象,可以有不同的解釋,產生不同的執行結果。比如,我們定義了一個“汽車”類,它有一個“啟動”方法,然後我們又定義了一個“電動車”類,它繼承了“汽車”類,也有一個“啟動”方法,但是“電動車”類的“啟動”方法的實現可能與“汽車”類的不同,這就是多態。

三、面向對象編程的理念

面向對象編程有兩個主要的理念,基於介面編程和組合優於繼承。

1. 基於介面編程

基於介面編程的理念是,使用者不需要知道數據類型、結構和演算法的細節,只需要知道調用介面能夠實現功能。這就像我們使用電視遙控器一樣,我們不需要知道遙控器內部的電路設計和工作原理,只需要知道按哪個按鈕可以打開電視,按哪個按鈕可以調節音量。

基於介面編程有很多好處,這裡簡單列幾條。

首先,基於介面編程可以提高代碼的靈活性。因為我們的代碼不依賴於具體的實現,所以當實現變化時,我們的調用代碼不需要做任何修改。比如有一個程式需要讀取數據,數據可能來自於資料庫、文件或者網路,無論數據來自哪裡,調用方只訪問“數據讀取”介面,實現可以根據場景任意調整。

其次,基於介面編程可以提高代碼的可測試性。因為介面只是一個規範,沒有具體的實現,所以我們可以方便地為介面創建模擬對象(Mock Object),這樣就可以在沒有實際環境的情況下進行單元測試。比如說,我們可以創建一個模擬的“數據讀取”介面,讓它返回一些預設的數據,然後我們就可以在沒有資料庫或者文件的情況下測試我們的代碼。

最後,基於介面編程也可以提高代碼的可讀性。因為介面清晰地定義了功能,所以只要看介面,就可以知道代碼應該做什麼,而不需要關心代碼是怎麼做的。這就像我們使用電視遙控器,我們不需要知道遙控器是怎麼工作的,只需要知道按這個按鈕可以換台,按那個按鈕可以調節音量。

使用介面有利於抽象、封裝和多態。

2. 組合優於繼承

儘管繼承可以使我們更容易地重用和擴展代碼,但是如果繼承層次過深、繼承關係過於複雜,就會嚴重影響代碼的可讀性和可維護性。比如我們修改了基類,就可能影響到繼承它的子類,這會增加迭代的風險。因此,我們更傾向於使用組合而不是繼承。比如,我們可以定義一個“電動車”類,它包含“電池系統”、“制動系統”、“車身系統”、“轉向系統”等組件,而不是繼承“汽車”類。

這裡我們再列舉下組合的幾個好處:

首先,組合可以讓我們的代碼更加靈活。因為我們可以隨時添加、刪除或者替換組件,而不需要修改組件的內部實現。比如,如果我們想要改變汽車的發動機,只需要換掉發動機這個組件就可以了,而不需要修改汽車或者發動機的代碼。

其次,組合可以讓我們的代碼更容易理解。因為每個組件都是獨立的,有明確的功能,所以我們可以分別理解和測試每個組件,而不需要理解整個系統。

最後,組合可以減少代碼的複雜性。因為我們不需要創建複雜的類層次結構,所以我們的代碼會更簡單,更易於維護。

總的來說,“組合優於繼承”是一種編程實踐,它鼓勵我們使用更簡單、更靈活的組合,而不是更複雜、更脆弱的繼承。這並不是說繼承是壞的,而是說在許多情況下,組合可能是一個更好的選擇。

3.控制反轉代碼示例

具體到編程中,很多同學可能使用過控制反轉或者依賴註入,控制反轉就是一種基於介面的組合編程思想。在傳統的編程模式中,我們通常是在需要的地方創建對象,然後調用對象的方法來完成一些任務。但是在使用了控制反轉之後,對象的創建和管理工作不再由我們自己控制,而是交給了一個外部的容器(也就是所謂的平臺),我們只需要在需要的地方聲明我們需要什麼,然後容器會自動為我們創建和註入需要的對象。這就是所謂的依賴註入(Dependency Injection,簡稱DI),它是實現控制反轉的一種方法。

為了讓大家更好理解依賴註入,我這裡貼一個Java的例子,程式基於 Spring Boot 框架。

在這個例子中,我們有一個 MessageService 介面和一個實現類 EmailService。然後我們有一個MessageClient類,它依賴於MessageService來發送消息。

首先,定義一個MessageService介面:

public interface MessageService {
    void sendMessage(String message, String receiver);
}

然後,創建實現類,在Spring Boot中,我們可以使用@Component或@Service等註解來讓Spring自動創建Bean。然後在需要註入的地方,使用@Autowired註解來自動註入Bean。

我們將MessageService的實現類標記為@Service:

@Service
public class EmailService implements MessageService {
    public void sendMessage(String message, String receiver) {
        System.out.println("Email sent to " + receiver + " with Message=" + message);
    }
}

我們在MessageClient中使用@Autowired來註入MessageService:

@Component
public class MessageClient {
    private MessageService messageService;

    @Autowired
    public MessageClient(MessageService messageService) {
        this.messageService = messageService;
    }

    public void processMessage(String message, String receiver){
        this.messageService.sendMessage(message, receiver);
    }
}

最後,在主程式中,我們可以直接獲取MessageClient的Bean,而不需要手動創建:

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Main.class, args);
        MessageClient emailClient = context.getBean(MessageClient.class);
        emailClient.processMessage("Hello", "[email protected]");
    }
}

在這個例子中,Spring Boot會自動掃描@Service和@Component註解的類,並創建對應的Bean。然後在需要註入的地方,Spring Boot會自動找到對應的Bean並註入。

控制反轉是一種非常強大的設計原則,它可以幫助我們寫出更靈活、更易於維護和測試的代碼。如果你還沒有嘗試過,我強烈建議你試試!

四、面向對象編程的原則

面向對象編程有五個基本原則,也被稱為SOLID原則。

1. 單一原則

單一原則是指一個類應該僅具有隻與他職責相關的東西,這樣可以降低類的複雜度,提高可讀性和可維護性。

這個原則就像是你在廚房裡做飯,你有各種各樣的廚具,每個廚具都有它特定的用途,比如刀用來切菜,鍋用來煮食物,勺子用來攪拌。你不會用刀去攪拌,也不會用勺子去切菜。這樣每個廚具都只負責一項任務,使得廚房的運作更加順暢。

2. 開閉原則

開閉原則是指軟體中的類、屬性和函數對擴展是開放的,對修改是封閉的。這樣可以避免對原有代碼的修改導致的很多工程工作。

這個原則就像是你的房子,你可以在房子裡面添加更多的傢具,比如椅子、桌子、床等,但你不會去改變房子的結構,比如拆掉牆壁或者增加門窗。這樣你的房子對於添加傢具是開放的,對於修改結構是關閉的。

在電腦體系中,最符合開閉原則的就是馮諾依曼體系架構,在這個架構中,CPU是封閉的、穩定的,然後通過IO操作對外開放,支持各種無窮無盡的輸入輸出設備。這是開閉原則的最好最基礎的體現。

3. 里氏替換原則

里氏替換原則是指子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法。這樣可以讓高層次模塊能夠依賴抽象類,而不是具體的實現。

這個原則就像是你的電視遙控器,無論你的電視是老款的CRT電視,還是新款的LED電視,你都可以用同一個遙控器來控制。這是因為所有的電視都遵循了同樣的介面,即遙控器可以發送的信號。所以你可以用新的電視來替換老的電視,而不需要改變遙控器。

4. 介面隔離原則

介面隔離原則是指類間的依賴關係應該建立在最小的介面之上,這樣可以減少類間的耦合度。

舉個例子,假設我們有一個Animal介面,它包含了eat(), sleep(), fly()等方法。現在我們要設計一個Dog類來實現這個介面,但是狗並不能飛,所以fly()方法對於Dog類來說是不需要的。如果我們按照介面隔離原則來設計,那麼我們可以將Animal介面拆分為AnimalBasic(包含eat()和sleep()方法)和AnimalFly(包含fly()方法)兩個介面,然後讓Dog類只實現AnimalBasic介面,這樣就避免了實現不需要的方法。

5. 依賴反轉原則

依賴反轉原則是指高層次模塊不應該依賴於低層次模塊的具體實現,兩者都應該依賴其抽象。這樣可以提高代碼的可擴展性。

舉個例子,假設我們有一個高級模塊HighLevelModule和一個低級模塊LowLevelModule。HighLevelModule直接依賴於LowLevelModule的具體實現。現在,如果我們遵循依賴反轉原則,我們可以定義一個抽象的介面AbstractModule,然後讓HighLevelModule依賴於AbstractModule,同時讓LowLevelModule也實現AbstractModule。這樣,無論是HighLevelModule還是LowLevelModule,它們都只依賴於抽象,而不再直接依賴於對方的具體實現。這樣就可以提高代碼的可擴展性和可維護性。

五、面向對象編程的優缺點

面向對象編程的優點主要有兩個:

  • 一是能和真實的世界交相呼應,符合人的直覺。對象是基於真實世界實體的抽象,比如學生、書籍、車輛等,這些對象都有其屬性(如學生的名字、年齡)和行為(如學生的學習、閱讀)。這樣的設計方式使得我們能夠更直觀地理解和操作代碼,因為它與我們日常生活中的理解方式是一致的。
  • 二是代碼的可重用性、可擴展性和靈活性很好。這主要得益於OOP的幾個主要特性,包括封裝、繼承和多態。封裝可以隱藏對象的內部實現,只暴露出必要的介面,這樣可以防止外部的不恰當操作。繼承允許我們創建子類來複用和擴展父類的功能,這大大提高了代碼的可重用性。多態則允許我們使用同一個介面來操作不同的對象,這提高了代碼的靈活性。

然而,面向對象編程也並非完美,它也有一些缺點,比如:

  • 首先,由於代碼需要通過對象來抽象,這就增加了一層“代碼粘合層”,也就是我們需要創建對象、管理對象的生命周期、處理對象之間的關係等,這使得代碼變得更加複雜。對於一些簡單的問題,使用面向對象編程可能會有點“殺雞用牛刀”。
  • 其次,面向對象編程中的對象通常都有一些內部狀態,而這些狀態在併發環境下需要被正確地管理,否則就可能會出現數據不一致、死鎖等問題。比如,如果兩個線程同時操作同一個對象,而這個對象的狀態沒有被正確地保護,那麼就可能會出現數據不一致的問題。

總的來說,面向對象編程是一種強大而靈活的編程範式,它可以幫助我們更好地組織和管理代碼,提高代碼的可讀性和可維護性,這使得它特別適合用在大型工程項目中。然而,我們也需要註意其可能帶來的問題,尤其是在併發和複雜系統中。

關註螢火架構,技術提升不迷路!

  • 本文作者: 螢火架構
  • 本文鏈接: https://www.cnblogs.com/bossma/p/17897584.html
  • 關於博主: 使用微信掃描左側二維碼關註我的訂閱號,每天獲取新知識
  • 版權聲明: 本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!

  • 您的分享是我們最大的動力!

    -Advertisement-
    Play Games
    更多相關文章
    • 1. Performance Schema Lock Tables MySQL安裝以後,我們會看到有這麼兩個資料庫:information_schema 和 performance_schema ,它們對於排查問題是非常有用的。 Performance Schema 是一種存儲引擎,預設情況下,它是 ...
    • 問題 最近有好幾個朋友問,如何將 performance_schema.events_statements_xxx 中的 TIMER 欄位(主要是TIMER_START和TIMER_END)轉換為日期時間。 因為 TIMER 欄位的單位是皮秒(picosecond),所以很多童鞋會嘗試直接轉換,但轉 ...
    • 關聯發散是開發常用的獲取特定彙總數據的方法,但是使用這類方法意味著承擔數據爆炸的風險。本篇通過一個典型案例,給出了“求所有值中大於本行值的最小值”的一個調優方案。 ...
    • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 Echarts繪製氣泡圖 氣泡圖是一種用於可視化三維數據的圖表類型,其中兩個變數用於確定數據點在平面上的位置,另一個變數用於確定氣泡的大小。Echarts是一款基於JavaScript的數據可視化庫,它提供了豐富的圖表類型,包括靈活多變的 ...
    • 人在身處逆境時,適應環境的能力實在驚人。人可以忍受不幸,也可以戰勝不幸,因為人有著驚人的潛力,只要立志發揮它,就一定能渡過難關。 Hooks 是 React 16.8 的新增特性。它可以讓你在不編寫 class 組件的情況下使用 state 以及其他的 React 特性。 React Hooks 表 ...
    • 本文並不是介紹如何將一個網頁配置成離線應用並支持安裝下載的。研究PWA的目的僅僅是為了保證用戶的資源可以直接從本地載入,來忽略全國或者全球網路質量對頁面載入速度造成影響。當然,如果頁面上所需的資源,除了資源文件外並不需要任何的網路請求,那它除了不支持**安裝到桌面**,已經算是一個離線應用了。 ...
    • static:靜態定位(預設)。依據文檔流定位。 relative:相對定位。以自身為基準,設置坐標(left、top、right、bottom),不脫離文檔流。 absolute:絕對定位。以父元素為基準,設置坐標(left、top、right、bottom),脫離文檔流。 fixed:固定定位。... ...
    • 介紹 在使用nest創建項目時,預設使用webpack進行打包,有時候啟動項目需要1-2分鐘。所以希望採用vite進行快速啟動項目進行開發。 本文主要使用NestJs、Vite和swc進行配置。文章實操較多,概念性的東西可訪問對應的官方文檔進行瞭解。tips: 個人認為概念性的東西,在文章中指出。對 ...
    一周排行
      -Advertisement-
      Play Games
    • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
    • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
    • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
    • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
    • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
    • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
    • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
    • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
    • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
    • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...