面向對象編程的十大原則
譯文【51CTO.com快譯】
眾所周知,面向對象的設計原則(Object-Oriented Design Principles)是面向對象編程(OOP)的核心。但是,如今有許多Java程序員在追求諸如Singleton、Decorator或Observer等設計模式的同時,卻忽略了面向對象的分析和設計。我們除了要學習諸如抽象、封裝、多態和繼承之類的基礎知識,還需要了解面向對象的設計原則。據此,我們可以創建出簡潔的模塊化設計,以便后期輕松地開展測試,調試和維護。
不知您是否聽說過OOP的SOLID設計原則(請參見-- https://javarevisited.blogspot.com/2018/02/top-5-java-design-pattern-courses-for-developers.html)?作為一種面向對象設計原則,它具體包括如下十個部分。
1.DRY(Don’t repeat yourself)
顧名思義,DRY表示不要編寫重復的代碼,應盡量使用抽象類(Abstraction)來抽象出目標事物。例如:如果您有兩個以上的代碼塊,那么就應當考慮使其成為一個單獨的方法。如果您多次用到某個硬編程(hard-coded)的值,那么就應當將它們設為public final constant(請參見-- http://javarevisited.blogspot.com/2011/12/final-variable-method-class-java.html)。顯然,這樣的面向對象設計原則將有益于后期的維護。
不過,在您使用標準化的代碼去驗證OrderId和SSN(譯者注:美國社會安全號碼),這兩種不同的功能或事物時,您需要避免將它們聯系得過于緊密,否則當OrderId更改其格式時,SSN的驗證代碼將會受阻。這就是我們常說的耦合。請不要合并任何使用了相似代碼,但并實際聯系的事物。您可以在Udemy(譯者注:一家開放式的在線教育網站)上的“Java課程”中進一步學習《軟件架構和設計模式的基礎知識》,以了解有關編寫正確的代碼和在設計系統時,需要遵循的最佳實踐。
2.封裝變更(Encapsulate What Changes)
資深碼農經常會仰天長嘆:“在軟件領域,唯一不變的就是變化!”可見,您應當盡量封裝那些您期望,或可能在將來變更的代碼。此舉的好處同樣是易于后期的測試與維護。
如果您使用Java進行編程,那么請在默認情況下將各種變量和方法設為私有,之后可逐步增加訪問權限。同時在Java中,有不少設計模式都使用到了封裝。其中Factory設計模式(Factory design pattern,請參見--http://javarevisited.blogspot.com/2011/12/factory-design-pattern-java-example.html)就是一個很好的例子。它通過封裝對象的代碼創建,提供了在不影響現有代碼的情況下,在后期引入新功能的靈活性。
在Pluralsight(譯者注:一個軟件開發的在線教育網站平臺)上的《設計模式庫》課程(請參見--https://pluralsight.pxf.io/c/1193463/424552/7490?u=https%3A%2F%2Fwww.pluralsight.com%2Fcourses%2Fpatterns-library),可謂最好的設計模式集。它同時提供了有關如何在真實環境中使用的建議。
3.開放式封閉設計原則(Open Closed Design Principle)
根據OOP的設計原則:“類、方法或功能都應當為新功能的擴展而開放,但是要為修改而封閉(防止他人更改已經測試過的代碼)。”在理想情況下,我們只需要測試那些能夠帶來新功能的程序代碼。這便是開放式封閉設計原則的目標。此處的Open-Closed正是SOLID設計原則中的字母“O”的縮略。
我們來看一個違反“開放式封閉設計原則”的Java示例:在某段代碼中,GraphicEditor與Shape緊密結合,如果需要新的Shape,則需要在drawShape(Shape s)方法的內部,修改已通過測試的系統。顯然,這樣既容易出錯,也不可取。
您可以在Udemy那里學習到《面向對象設計和架構的SOLID原則》的相關課程,以加深對該原則的理解。
4.單一責任原則(Single Responsibility Principle,SRP)
單一責任原則要求:導致類發生變更的原因不應多于一個,或者說在一個級別上應始終只實現一項功能。其好處在于:有效地減少了軟件的各個組件與代碼之間的耦合。此處的SRP便是SOLID設計原則中的字母“S”的縮寫。
例如,如果您在Java的一個類中設置了一個以上的功能,那么就會導致兩個功能之間產生耦合。只要您更改了一個功能,那么就可能破壞耦合,進而引發新的一輪測試,以避免在生產環境中出現任何異常。
您可以在Udemy那里學習到《從0到1:設計模式》的相關課程,以加深對該原則的理解。
5.依賴性注入或反轉原則(Dependency Injection or Inversion Principle)
該原則要求:不要自行增加依賴性,請把它交給框架。例如:作為編寫實際應用最流行的Java框架之一,Spring框架就提供了各種依賴性。該設計原則的優點在于:由DI框架注入的任何類,都易于使用模擬對象來進行測試,且易于后期維護。由于對象的創建代碼集中于框架之中,因此客戶端的代碼則不會被散落在各處。該此Dependency Injection原則便是SOLID中字母“D”的縮寫。
我們可以用多種方法來實現依賴項注入,例如:使用似于AspectJ的面向切面編程(Aspect Oriented Programming,AOP)框架,進行字節碼檢測;或通過使用Spring中的各種代理來實現。
我們來看一個違反了依賴性注入原則的Java代碼示例:EventLogWriter與AppManager有著緊密的耦合關系。如果您需要使用其他的方式(例如:通過推送短信或郵件通知)來通知客戶端的話,則需要更改AppManager類。為此,我們可以通過使用依賴關系反轉原則,來予以解決。也就是說,為了避免AppManager去請求EventLogWriter,我們可以使用由框架注入或提供的AppManager。
您可以在Udemy那里學習到《使用SOLID原則寫更好的代碼—速成班》的相關課程,以加深對該原則的理解。
6. 優先使用(對象)組合,而非(類)繼承(Favor Composition over Inheritance)
繼承和合成是兩種通用的方法,可用于重用已編寫好的代碼。兩者雖然各有優、缺點,但是如果可能的話,您應當盡量使用組合而不是繼承。畢竟組合比繼承要靈活得多。
此處的組合是指:通過設置屬性,以更改運行時(run-time)類的行為,并使用各種接口來組成一個類。這就是我們常說的多態性(polymorphism)。它可以隨時、且靈活地替換成為更好的實現方式。
如果您有興趣學習更多有關組合、繼承、關聯、聚合等面向對象編程的概念和知識,請在Coursera(譯者注:一個與世界頂尖大學合作的大型公開在線課程項目)上的《Java面向對象編程》課程中深入學習。
7. Liskov替代原則(Liskov Substitution Principle,LSP)
根據Liskov替換原則,子類型必須可以替代父類型。也就是說,那些使用父類型的方法或函數,必須能夠與子類的對象無障礙地協作。即:派生類或子類必須在父類的基礎上增強功能,而不是減少功能。相反,如果一個類具有比其子類更多的功能,那么它就不應該支持這些更多的功能,也就是違反了LSP。其實,LSP與單一職責原則(Single responsibility principle)、以及接口隔離原則(Interface Segregation Principle,見下文)有著密切的相關性。而且,LSP正是SOLID中字母“L”的縮寫。
我們來看一個違反了Liskov替換原則的Java代碼示例:如果您設計了一個area(Rectangle r)方法來計算Rectangle的面積,那么當您傳入Square時,由于Square并非真正的Rectangle,該代碼就會產生中斷。
如果您對更多真實環境的示例感興趣的話,請在Pluralsight上的《面向對象設計的SOLID原則》課程中進行深入學習。
8.接口隔離原則(Interface Segregation Principle,ISP)
接口隔離原則規定:如果一個接口包含了多個功能,而某個客戶端只需要其中的一項功能,那么我們就不應該去實現那些用不到的接口。
毫無疑問,接口設計是一項比較棘手的工作。畢竟我們一旦發布了某個接口,就無法在不破壞其現有實現的情況下,對其進行更改。ISP在Java中的另一個好處是:如果不同的類都需要使用某個接口去實現各種方法的話,不如用更少的方法去實現單一的功能。
如果您對接口編程感興趣的話,請參考博文--《Java接口的實戰用法》,以了解更多的信息。
9.為接口編程,而非為實現編程(Programming for Interface not implementation)
程序員應該始終為接口編程,而不是為實現而編程。根據該原則所創建的代碼,將能夠靈活地被用于接口的任何一種新的實現上。
確切地說,我們應該在各種變量上使用接口類型、方法的返回類型、以及類似于Java的參數類型。例如:您可以使用SuperClasstype來存儲對象,而不必使用SubClass。同時,您可以用List numbers= getNumbers();來代替ArrayList numbers = getNumbers();。當然,諸如:《Java高效編程(Effective Java)》(請參見-- https://www.amazon.com/Effective-Java-3rd-Joshua-Bloch/dp/0134685997/?tag=javamysqlanta-20)和《入淺出的設計模式(Head First design pattern)》(請參見-- http://www.amazon.com/dp/0596007124/?tag=javamysqlanta-20)之類的Java書籍也是這么建議的。
如果您對提高程序的代碼質量感興趣的話,我建議您學習Udemy上的《設計模式重構》課程。該課程將教會您如何使用C#中的重構技術,以及設計模式來改進內部設計。
10.委托原則(Delegation Principles)
委托原則建議:請不要自己做所有的事,要學會將不同的實現委托給相應的類。該原則的經典示例是Java中的equals()和hashCode()方法(請參見-- http://javarevisited.blogspot.com/2011/02/how-to-write-equals-method-in-java.html)。事件委托(Event delegation)則是另一種示例,它將事件委托給處理程序(handlers)進行處理。可見,此設計原則的主要好處是:無需重復編寫代碼,便可輕松地修改程序的行為。
總結與其他資源
可以說,上述所有面向對象的設計原則,都會有利于程序代碼的高內聚和低耦合性,也都有助于您編寫出靈活、簡潔的代碼。您需要通過反復的練習,來實踐這些理論原則,進而解決應用程序開發和軟件工程中的各種常見問題。
與此同時,您可以從Apache和Google處尋找各種開源代碼,以便學習Java和OOP的設計原則。另外,Java開發工具包(Java Development Kit,JDK)也包含有許多設計原則,例如:BorderFactory類中的Factory模式(請參見--http://javarevisited.blogspot.sg/2011/12/factory-design-pattern-java-example.html#axzz51cvxH5kW)、java.lang.Runtime類中的Singleton模式(請參見--https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html)、以及各種java.io類中的Decorator模式(請參見--http://www.java67.com/2013/07/decorator-design-pattern-in-java-real-life-example-tutorial.html)。
如果您對于學習面向對象的原則和模式興趣不減的話,我推薦您閱讀《深入淺出學習面向對象的分析和設計(Head First Object-Oriented Analysis and Design)》一書。
當然,如果您想了解更多有關SOLID設計原則的具體內容,請參考如下實用的資源:
- Robert Martin的《整潔的代碼(Clean Code)》
- 《面向對象設計的SOLID原則(SOLID Principles of Object-Oriented Design)》
- 《面向對象設計和架構的SOLID原則(SOLID Principles of Object-Oriented Design and Architecture)》
- Martin Fowler的《重構(Refactoring)》
原標題:10 Coding Principles Every Programmer Should Learn ,作者:Javin Paul
【51CTO譯稿,合作站點轉載請注明原文譯者和出處為51CTO.com】