一、面向?qū)ο缶幊痰母拍?/h2>
面向?qū)ο缶幊?,是一種程序設(shè)計(jì)范式,也是一種編程語言的分類。它以對(duì)象作為程序的基本單元,將算法和數(shù)據(jù)封裝其中,程序可以訪問和修改對(duì)象關(guān)聯(lián)的數(shù)據(jù)。這就像我們?cè)谡鎸?shí)世界中操作各種物體一樣,比如我們可以打開電視、調(diào)整音量、切換頻道,而不需要知道電視的內(nèi)部如何工作。同樣,在面向?qū)ο缶幊讨?,我們可以操作?duì)象,而不需要關(guān)心對(duì)象的內(nèi)部結(jié)構(gòu)和實(shí)現(xiàn)。
面向?qū)ο缶幊痰闹饕M成部分是類和對(duì)象。類是一組具有相同屬性和功能的對(duì)象的抽象,就好比我們說的“汽車”這個(gè)概念,它具有顏色、型號(hào)、速度等屬性,有啟動(dòng)、加速、剎車等功能。而對(duì)象則是類的實(shí)例,它是具體的,就像你家那輛紅色的奔馳車,它就是汽車這個(gè)類的一個(gè)實(shí)例。
二、面向?qū)ο缶幊痰奶匦?/h2>
面向?qū)ο缶幊逃腥筇匦?,封裝、繼承和多態(tài)。
1. 封裝
封裝是把客觀事物封裝成抽象的類,并隱藏實(shí)現(xiàn)細(xì)節(jié),使得代碼模塊化。比如,我們可以把“汽車”這個(gè)客觀事物封裝成一個(gè)類,這個(gè)類有顏色、型號(hào)等屬性,有啟動(dòng)、加速、剎車等方法,而這些屬性和方法的具體實(shí)現(xiàn)則被隱藏起來,使用者只需要知道這個(gè)類有哪些屬性和方法,不需要知道這些方法是如何實(shí)現(xiàn)的。
2. 繼承
繼承是面向?qū)ο缶幊痰牧硪粋€(gè)重要特性,它提供了一種無需重新編寫,使用現(xiàn)有類的所有功能并進(jìn)行擴(kuò)展的能力。比如,我們可以定義一個(gè)“電動(dòng)車”類,它繼承了“汽車”類,就自動(dòng)擁有了“汽車”類的所有屬性和方法,比如顏色、型號(hào)等屬性,啟動(dòng)、加速、剎車等方法,然后我們還可以在“電動(dòng)車”類上增加一些新的屬性和方法,比如電池容量、充電方法等。
3. 多態(tài)
多態(tài)是指同一操作作用于不同的對(duì)象,可以有不同的解釋,產(chǎn)生不同的執(zhí)行結(jié)果。比如,我們定義了一個(gè)“汽車”類,它有一個(gè)“啟動(dòng)”方法,然后我們又定義了一個(gè)“電動(dòng)車”類,它繼承了“汽車”類,也有一個(gè)“啟動(dòng)”方法,但是“電動(dòng)車”類的“啟動(dòng)”方法的實(shí)現(xiàn)可能與“汽車”類的不同,這就是多態(tài)。
三、面向?qū)ο缶幊痰睦砟?/h2>
面向?qū)ο缶幊逃袃蓚€(gè)主要的理念,基于接口編程和組合優(yōu)于繼承。
1. 基于接口編程
基于接口編程的理念是,使用者不需要知道數(shù)據(jù)類型、結(jié)構(gòu)和算法的細(xì)節(jié),只需要知道調(diào)用接口能夠?qū)崿F(xiàn)功能。這就像我們使用電視遙控器一樣,我們不需要知道遙控器內(nèi)部的電路設(shè)計(jì)和工作原理,只需要知道按哪個(gè)按鈕可以打開電視,按哪個(gè)按鈕可以調(diào)節(jié)音量。
基于接口編程有很多好處,這里簡單列幾條。
首先,基于接口編程可以提高代碼的靈活性。因?yàn)槲覀兊拇a不依賴于具體的實(shí)現(xiàn),所以當(dāng)實(shí)現(xiàn)變化時(shí),我們的調(diào)用代碼不需要做任何修改。比如有一個(gè)程序需要讀取數(shù)據(jù),數(shù)據(jù)可能來自于數(shù)據(jù)庫、文件或者網(wǎng)絡(luò),無論數(shù)據(jù)來自哪里,調(diào)用方只訪問“數(shù)據(jù)讀取”接口,實(shí)現(xiàn)可以根據(jù)場景任意調(diào)整。
其次,基于接口編程可以提高代碼的可測試性。因?yàn)榻涌谥皇且粋€(gè)規(guī)范,沒有具體的實(shí)現(xiàn),所以我們可以方便地為接口創(chuàng)建模擬對(duì)象(Mock Object),這樣就可以在沒有實(shí)際環(huán)境的情況下進(jìn)行單元測試。比如說,我們可以創(chuàng)建一個(gè)模擬的“數(shù)據(jù)讀取”接口,讓它返回一些預(yù)設(shè)的數(shù)據(jù),然后我們就可以在沒有數(shù)據(jù)庫或者文件的情況下測試我們的代碼。
最后,基于接口編程也可以提高代碼的可讀性。因?yàn)榻涌谇逦囟x了功能,所以只要看接口,就可以知道代碼應(yīng)該做什么,而不需要關(guān)心代碼是怎么做的。這就像我們使用電視遙控器,我們不需要知道遙控器是怎么工作的,只需要知道按這個(gè)按鈕可以換臺(tái),按那個(gè)按鈕可以調(diào)節(jié)音量。
使用接口有利于抽象、封裝和多態(tài)。
2. 組合優(yōu)于繼承
盡管繼承可以使我們更容易地重用和擴(kuò)展代碼,但是如果繼承層次過深、繼承關(guān)系過于復(fù)雜,就會(huì)嚴(yán)重影響代碼的可讀性和可維護(hù)性。比如我們修改了基類,就可能影響到繼承它的子類,這會(huì)增加迭代的風(fēng)險(xiǎn)。因此,我們更傾向于使用組合而不是繼承。比如,我們可以定義一個(gè)“電動(dòng)車”類,它包含“電池系統(tǒng)”、“制動(dòng)系統(tǒng)”、“車身系統(tǒng)”、“轉(zhuǎn)向系統(tǒng)”等組件,而不是繼承“汽車”類。
這里我們?cè)倭信e下組合的幾個(gè)好處:
首先,組合可以讓我們的代碼更加靈活。因?yàn)槲覀兛梢噪S時(shí)添加、刪除或者替換組件,而不需要修改組件的內(nèi)部實(shí)現(xiàn)。比如,如果我們想要改變汽車的發(fā)動(dòng)機(jī),只需要換掉發(fā)動(dòng)機(jī)這個(gè)組件就可以了,而不需要修改汽車或者發(fā)動(dòng)機(jī)的代碼。
其次,組合可以讓我們的代碼更容易理解。因?yàn)槊總€(gè)組件都是獨(dú)立的,有明確的功能,所以我們可以分別理解和測試每個(gè)組件,而不需要理解整個(gè)系統(tǒng)。
最后,組合可以減少代碼的復(fù)雜性。因?yàn)槲覀儾恍枰獎(jiǎng)?chuàng)建復(fù)雜的類層次結(jié)構(gòu),所以我們的代碼會(huì)更簡單,更易于維護(hù)。
總的來說,“組合優(yōu)于繼承”是一種編程實(shí)踐,它鼓勵(lì)我們使用更簡單、更靈活的組合,而不是更復(fù)雜、更脆弱的繼承。這并不是說繼承是壞的,而是說在許多情況下,組合可能是一個(gè)更好的選擇。
3.控制反轉(zhuǎn)代碼示例
具體到編程中,很多同學(xué)可能使用過控制反轉(zhuǎn)或者依賴注入,控制反轉(zhuǎn)就是一種基于接口的組合編程思想。在傳統(tǒng)的編程模式中,我們通常是在需要的地方創(chuàng)建對(duì)象,然后調(diào)用對(duì)象的方法來完成一些任務(wù)。但是在使用了控制反轉(zhuǎn)之后,對(duì)象的創(chuàng)建和管理工作不再由我們自己控制,而是交給了一個(gè)外部的容器(也就是所謂的平臺(tái)),我們只需要在需要的地方聲明我們需要什么,然后容器會(huì)自動(dòng)為我們創(chuàng)建和注入需要的對(duì)象。這就是所謂的依賴注入(Dependency Injection,簡稱DI),它是實(shí)現(xiàn)控制反轉(zhuǎn)的一種方法。
為了讓大家更好理解依賴注入,我這里貼一個(gè)Java的例子,程序基于 Spring Boot 框架。
在這個(gè)例子中,我們有一個(gè) MessageService 接口和一個(gè)實(shí)現(xiàn)類 EmailService。然后我們有一個(gè)MessageClient類,它依賴于MessageService來發(fā)送消息。
首先,定義一個(gè)MessageService接口:
public interface MessageService {
void sendMessage(String message, String receiver);
}
然后,創(chuàng)建實(shí)現(xiàn)類,在Spring Boot中,我們可以使用@Component或@Service等注解來讓Spring自動(dòng)創(chuàng)建Bean。然后在需要注入的地方,使用@Autowired注解來自動(dòng)注入Bean。
我們將MessageService的實(shí)現(xiàn)類標(biāo)記為@Service:
@Service
public class EmailService implements MessageService {
public void sendMessage(String message, String receiver) {
System.out.println("Email sent to " + receiver + " with Message=" + message);
}
}
我們?cè)贛essageClient中使用@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,而不需要手動(dòng)創(chuàng)建:
@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", "test@example.com");
}
}
在這個(gè)例子中,Spring Boot會(huì)自動(dòng)掃描@Service和@Component注解的類,并創(chuàng)建對(duì)應(yīng)的Bean。然后在需要注入的地方,Spring Boot會(huì)自動(dòng)找到對(duì)應(yīng)的Bean并注入。
控制反轉(zhuǎn)是一種非常強(qiáng)大的設(shè)計(jì)原則,它可以幫助我們寫出更靈活、更易于維護(hù)和測試的代碼。如果你還沒有嘗試過,我強(qiáng)烈建議你試試!
四、面向?qū)ο缶幊痰脑瓌t
面向?qū)ο缶幊逃形鍌€(gè)基本原則,也被稱為SOLID原則。
1. 單一原則
單一原則是指一個(gè)類應(yīng)該僅具有只與他職責(zé)相關(guān)的東西,這樣可以降低類的復(fù)雜度,提高可讀性和可維護(hù)性。
這個(gè)原則就像是你在廚房里做飯,你有各種各樣的廚具,每個(gè)廚具都有它特定的用途,比如刀用來切菜,鍋用來煮食物,勺子用來攪拌。你不會(huì)用刀去攪拌,也不會(huì)用勺子去切菜。這樣每個(gè)廚具都只負(fù)責(zé)一項(xiàng)任務(wù),使得廚房的運(yùn)作更加順暢。
2. 開閉原則
開閉原則是指軟件中的類、屬性和函數(shù)對(duì)擴(kuò)展是開放的,對(duì)修改是封閉的。這樣可以避免對(duì)原有代碼的修改導(dǎo)致的很多工程工作。
這個(gè)原則就像是你的房子,你可以在房子里面添加更多的家具,比如椅子、桌子、床等,但你不會(huì)去改變房子的結(jié)構(gòu),比如拆掉墻壁或者增加門窗。這樣你的房子對(duì)于添加家具是開放的,對(duì)于修改結(jié)構(gòu)是關(guān)閉的。
在計(jì)算機(jī)體系中,最符合開閉原則的就是馮諾依曼體系架構(gòu),在這個(gè)架構(gòu)中,CPU是封閉的、穩(wěn)定的,然后通過IO操作對(duì)外開放,支持各種無窮無盡的輸入輸出設(shè)備。這是開閉原則的最好最基礎(chǔ)的體現(xiàn)。
3. 里氏替換原則
里氏替換原則是指子類可以實(shí)現(xiàn)父類的抽象方法,但不能覆蓋父類的非抽象方法。這樣可以讓高層次模塊能夠依賴抽象類,而不是具體的實(shí)現(xiàn)。
這個(gè)原則就像是你的電視遙控器,無論你的電視是老款的CRT電視,還是新款的LED電視,你都可以用同一個(gè)遙控器來控制。這是因?yàn)樗械碾娨暥甲裱送瑯拥慕涌?,即遙控器可以發(fā)送的信號(hào)。所以你可以用新的電視來替換老的電視,而不需要改變遙控器。
4. 接口隔離原則
接口隔離原則是指類間的依賴關(guān)系應(yīng)該建立在最小的接口之上,這樣可以減少類間的耦合度。
舉個(gè)例子,假設(shè)我們有一個(gè)Animal接口,它包含了eat(), sleep(), fly()等方法?,F(xiàn)在我們要設(shè)計(jì)一個(gè)Dog類來實(shí)現(xiàn)這個(gè)接口,但是狗并不能飛,所以fly()方法對(duì)于Dog類來說是不需要的。如果我們按照接口隔離原則來設(shè)計(jì),那么我們可以將Animal接口拆分為AnimalBasic(包含eat()和sleep()方法)和AnimalFly(包含fly()方法)兩個(gè)接口,然后讓Dog類只實(shí)現(xiàn)AnimalBasic接口,這樣就避免了實(shí)現(xiàn)不需要的方法。
5. 依賴反轉(zhuǎn)原則
依賴反轉(zhuǎn)原則是指高層次模塊不應(yīng)該依賴于低層次模塊的具體實(shí)現(xiàn),兩者都應(yīng)該依賴其抽象。這樣可以提高代碼的可擴(kuò)展性。
舉個(gè)例子,假設(shè)我們有一個(gè)高級(jí)模塊HighLevelModule和一個(gè)低級(jí)模塊LowLevelModule。HighLevelModule直接依賴于LowLevelModule的具體實(shí)現(xiàn)?,F(xiàn)在,如果我們遵循依賴反轉(zhuǎn)原則,我們可以定義一個(gè)抽象的接口AbstractModule,然后讓HighLevelModule依賴于AbstractModule,同時(shí)讓LowLevelModule也實(shí)現(xiàn)AbstractModule。這樣,無論是HighLevelModule還是LowLevelModule,它們都只依賴于抽象,而不再直接依賴于對(duì)方的具體實(shí)現(xiàn)。這樣就可以提高代碼的可擴(kuò)展性和可維護(hù)性。
五、面向?qū)ο缶幊痰膬?yōu)缺點(diǎn)
面向?qū)ο缶幊痰膬?yōu)點(diǎn)主要有兩個(gè):
- 一是能和真實(shí)的世界交相呼應(yīng),符合人的直覺。對(duì)象是基于真實(shí)世界實(shí)體的抽象,比如學(xué)生、書籍、車輛等,這些對(duì)象都有其屬性(如學(xué)生的名字、年齡)和行為(如學(xué)生的學(xué)習(xí)、閱讀)。這樣的設(shè)計(jì)方式使得我們能夠更直觀地理解和操作代碼,因?yàn)樗c我們?nèi)粘I钪械睦斫夥绞绞且恢碌摹?/span>
- 二是代碼的可重用性、可擴(kuò)展性和靈活性很好。這主要得益于OOP的幾個(gè)主要特性,包括封裝、繼承和多態(tài)。封裝可以隱藏對(duì)象的內(nèi)部實(shí)現(xiàn),只暴露出必要的接口,這樣可以防止外部的不恰當(dāng)操作。繼承允許我們創(chuàng)建子類來復(fù)用和擴(kuò)展父類的功能,這大大提高了代碼的可重用性。多態(tài)則允許我們使用同一個(gè)接口來操作不同的對(duì)象,這提高了代碼的靈活性。
然而,面向?qū)ο缶幊桃膊⒎峭昝?,它也有一些缺點(diǎn),比如:
- 首先,由于代碼需要通過對(duì)象來抽象,這就增加了一層“代碼粘合層”,也就是我們需要?jiǎng)?chuàng)建對(duì)象、管理對(duì)象的生命周期、處理對(duì)象之間的關(guān)系等,這使得代碼變得更加復(fù)雜。對(duì)于一些簡單的問題,使用面向?qū)ο缶幊炭赡軙?huì)有點(diǎn)“殺雞用牛刀”。
- 其次,面向?qū)ο缶幊讨械膶?duì)象通常都有一些內(nèi)部狀態(tài),而這些狀態(tài)在并發(fā)環(huán)境下需要被正確地管理,否則就可能會(huì)出現(xiàn)數(shù)據(jù)不一致、死鎖等問題。比如,如果兩個(gè)線程同時(shí)操作同一個(gè)對(duì)象,而這個(gè)對(duì)象的狀態(tài)沒有被正確地保護(hù),那么就可能會(huì)出現(xiàn)數(shù)據(jù)不一致的問題。
總的來說,面向?qū)ο缶幊淌且环N強(qiáng)大而靈活的編程范式,它可以幫助我們更好地組織和管理代碼,提高代碼的可讀性和可維護(hù)性,這使得它特別適合用在大型工程項(xiàng)目中。然而,我們也需要注意其可能帶來的問題,尤其是在并發(fā)和復(fù)雜系統(tǒng)中。文章來源:http://www.zghlxwxcb.cn/news/detail-750792.html
關(guān)注螢火架構(gòu),技術(shù)提升不迷路!文章來源地址http://www.zghlxwxcb.cn/news/detail-750792.html
到了這里,關(guān)于面向?qū)ο缶幊?,看這篇就夠了的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!