大一的時候學校就開了C語言這門課,最開始糊里糊塗無從下手,後來慢慢寫了幾個幾百行的小程序就逐漸明白編程是怎麼一回事了,還覺得本身都懂了(too young 啊),但是後來蹭了一節java公選課,才知道還有面向對象編程這麼一回事。老師說C是面向過程的,代碼超過十萬行就很差組織管理了,還得要面向對象的語言才能解決這個問題。我當時彷彿發現了一個新大陸,因而就開啓了自學java面向對象編程的路程。html
我感受這個問題還真的很差回答,不一樣的人有不一樣的理解,下面就談談個人理解,望你們指正。java
要說面向對象首先就得說說面向過程(出現的前後順序),面向過程就是分析要解決的問題A,而後把這個問題劃分爲不一樣的步驟A1,A2,...(或者問題足夠小就不用劃分)。而後用函數把這些子問題一個一個地實現,最終解決A。程序員
而面向對象是把問題中出現的角色獨立出來,讓他們互相通訊來完成最終的問題。算法
無論是面向過程仍是面向對象,都是咱們認識世界的一種方法。那你可能會問了,既然面向過程先出現並且能解決問題,那麼面向對象爲何會出現呢?首先面向過程既然出現就確定有它的道理,由於它符合咱們最多見的邏輯,咱們現實生活中趕上問題大多會採用這種思惟,好比我有個目標A:「我要成爲科學家」 ,那麼很天然我就會想到A1:完成小學,A2:完成中學,A3:完成大學,A4:完成研究生,...,An:得到科研機構承認。把這一系列子問題挨個解決,那麼個人問題就解決了,如圖:數據庫
代碼:編程
A(baby){ primary = A1(baby); middle = A2(primary); university = A3(middle); graduate = A4(university); //... scientist = An(graduate); return scientist; }
這是一種很天然的思路,因此面向過程最先出現。可是這種思路有個問題就是當系統龐大了龐大起來事後,各個步驟之間協調起來比較複雜,修改一個步驟可能引發不少的步驟的改變,好比那一天中國在中學和大學之間增長了一個學歷preUniversity,那麼至少就得改動兩個函數,若是美國沒有小學那麼這個函數就得搞一個美國版去掉A1,修改A2。等等等等。小程序
這既不利於維護,也沒有達到代碼可重用的目的。面向對象就應運而生了,用面向對象的思路就能夠以下解決:設計模式
代碼:數組
//人 class People(){ } //教育機構 class Education(People people){ private People people = people; public void primary(People people); public void middle (People people); public void university (People people); public void graduate (People people); //... public void scientist (People people); public bool finish(){ primary(people); middle (people); university (people); graduate (people); scientist (people); retrun true; } } //科研機構 class Institution(){ public void Recognize(Education education){ if(education.finish()&&others){//完成學業以及科研機構的其餘判斷條件 print "Scientist"; }else{ print "Not Scientist"; } } public static void main(Sting args[]){ People people = new People(); Education education = new Education(people); Recognize(education); } }
這樣的話,即便學習歷程有變化均可以只在Education類中修改,不一樣國別學歷不一樣也能夠繼承這個基類來實現,大大提升了維護性和重用性。框架
概念上來講,若是一個問題反覆發生,那麼這個問題的解決方案就會被反覆的使用,這種被頻繁使用的解決方案就是模式。設計模式是語言獨立的,主要用來解決程序設計的通常性問題(簡單說來就是組織代碼)。好比咱們作菜,不管是西紅柿炒雞蛋仍是紅燒排骨,作多了就發現這個行爲是有着固定模式的:點火,切菜,入鍋,裝進盤子。因而乎炒菜便成爲了一種模式,之後每種菜(菜品獨立)均可以沿襲這種模式,而後不一樣的菜實施細節不一樣而已。
按我理解,設計模式存在的終極意義就是代碼的可重用性(也就順帶了可維護性)。其實不僅是設計模式,面向過程和麪向對象自己其實也有這個做用,只不過面向過程是經過函數來實現,而面向對象是經過類來實現,並且面向對象把這個目的完成的更好一些。更完全一些。
設計模式通常來講包含以下幾個方面:
下面我就經過一些常見的設計模式來講明設計模式是怎樣起到這種效果的。
在面向對象編程中, 最一般的方法是一個 new 操做符產生一個對象實例,new 操做符就是用來構造對象實例的。可是在一些狀況下 , new操做符直接生成對象會帶來一些問題。舉例來講, 許多類型對象的創造須要一系列的步驟: 你可能須要計算或取得對象的初始設置 ;選擇生成哪一個子對象實例 ; 或在生成你須要的對象以前必須先生成一些輔助功能的對象。在這些狀況, 新對象的創建就是一個 「過程」 ,不只是一個操做,像一部大機器中的一個齒輪傳動。你如何能輕鬆方便地創建這麼" 複雜 " 的對象即操做中不須要粘貼複製呢?
定義一個用於建立對象的接口,讓子類決定實例化哪個類。工廠方法使一個類的實例化延遲到其子類。
工廠方法模式通用類圖:
在工廠方法模式中,抽象產品類Product負責定義產品的共性,實現對事物最抽象的定義;Creator爲抽象建立類,也就是抽象工廠,具體如何建立產品類是由具體的實現工廠ConcreteCreator完成的。咱們來看一個比較實用的通用源碼:
//抽象產品類 public abstract class Product { //產品類的公共方法 public void method1(){ //業務邏輯處理 } //抽象方法 public abstract void method2(); } //具體產品類,能夠有多個 public class ConcreteProduct1 extends Product { public void method2() { //業務邏輯處理 } } public class ConcreteProduct2 extends Product { public void method2() { //業務邏輯處理 } } //抽象工廠類 public abstract class Creator { //建立一個產品對象,其輸入參數類型能夠自行設置 //一般爲String、Enum、Class等,固然也能夠爲空 public abstract <T extends Product> T createProduct(Class<T> c); } //具體工廠類 public class ConcreteCreator extends Creator { public <T extends Product> T createProduct(Class<T> c){ Product product=null; try { product = (Product)Class.forName(c.getName()).newInstance(); } catch (Exception e) { //異常處理 } return (T)product; } } //場景類 public class Client { public static void main(String[] args) { Creator creator = new ConcreteCreator(); Product product = creator.createProduct(ConcreteProduct1.class); //繼續業務處理 } }
首先,代碼具備了良好的封裝性,結構清晰。一個對象建立是有條件約束的,一個調用者須要一個具體的產品對象,只要知道這個產品的類名(或約束字符串)就能夠了,不用知道建立對象的艱辛過程,下降模塊間的耦合。
其次,工廠方法模式的擴展性很是好。在增長產品類的狀況下,只要適當地修改具體的工廠類或擴展一個工廠類就好了。
再次,屏蔽產品類。這一特色很是重要,產品類的實現如何變化,調用者都不須要關心,它只須要關心產品的接口,只要接口保持不變,系統中的上層模塊就不要發生變化。由於產品類的實例化工做是由工廠類負責的,一個產品對象具體由哪個產品生成是由工廠類決定的。在數據庫開發中,你們應該可以深入體會到工廠方法模式的好處:若是使用JDBC鏈接數據庫,數據庫從My SQL 切換到Oracle,須要改動的地方就是切換一下驅動名稱(前提
條件是SQL 語句是標準語句),其餘的都不須要修改,這是工廠方法模式靈活性的一個直接案例。
最後,工廠方法模式是典型的解耦框架。
若是一個事件A發生了須要觸發另外一個事件B之時該怎麼辦呢?直接修改A?顯然不是科學的程序設計,由於這樣就達不到A,B解耦合的目的,若是要修改B就得直接修改A;若是A同時還要觸發事件C怎麼辦?又繼續往A裏面添加?顯然這樣作最後會致使A臃腫不堪難以維護,這個時候就須要用到觀察者模式了。
定義對象間一種一對多的依賴關係,使得每當一個對象改變狀態,則全部依賴於它的對象都會獲得通知並被自動更新。
觀察者模式通用類圖:
Subject被觀察者,定義被觀察者必須實現的職責,它必須可以動態地增長、取消觀察者。它通常是抽象類或者是實現類,僅僅完成做爲被觀察者必須實現的職責:管理觀察者並通知觀察者。
Observer觀察者,觀察者接收到消息後,即進行update(更新方法)操做,對接收到的信息進行處理。
ConcreteSubject具體的被觀察者,定義被觀察者本身的業務邏輯,同時定義對哪些事件進行通知。
ConcreteObserver具體的觀察者,每一個觀察在接收到消息後的處理反應是不一樣,各個觀察者有本身的處理邏輯。
通用代碼以下:
//被觀察者 public abstract class Subject { //定義一個觀察者數組 private Vector<Observer> obsVector = new Vector<Observer>(); //增長一個觀察者 public void addObserver(Observer o){ this.obsVector.add(o); } //刪除一個觀察者 public void delObserver(Observer o){ this.obsVector.remove(o); } //通知全部觀察者 public void notifyObservers(){ for(Observer o:this.obsVector){ o.update(); } } } //具體被觀察者 public class ConcreteSubject extends Subject { //具體的業務 public void doSomething(){ //do something super.notifyObservers(); } } //觀察者 public interface Observer { //更新方法 public void update(); } //具體觀察者 public class ConcreteObserver implements Observer { //實現更新方法 public void update() { System.out.println("接收到信息,並進行處理!"); } } //場景類 public class Client { public static void main(String[] args) { //建立一個被觀察者 ConcreteSubject subject = new ConcreteSubject(); //定義一個觀察者 Observer obs= new ConcreteObserver(); //觀察者觀察被觀察者 subject.addObserver(obs); //觀察者開始活動了 subject.doSomething(); } }
首先,觀察者和被觀察者之間是抽象耦合。如此設計,則無論是增長觀察者仍是被觀察者都很是容易擴展,並且在Java中都已經實現的抽象層級的定義,在系統擴展方面更是駕輕就熟。
其次,創建了一套觸發機制根據單一職責原則,每一個類的職責是單一的,那麼怎麼把各個單一的職責串聯成真實世
界的複雜的邏輯關係呢?好比,咱們去打獵,打死了一隻母鹿,母鹿有三個幼崽,因失去了母鹿而餓死,屍體又被兩隻禿鷹爭搶,因分配不均,禿鷹開始鬥毆,而後羸弱的禿鷹死掉,生存下來的禿鷹,則所以擴大了地盤……這就是一個觸發機制,造成了一個觸發鏈。觀察者模式能夠完美地實現這裏的鏈條形式。
若是你有不少的算法,他們只是有些許不一樣,你須要在不一樣場景使用不一樣的算法,這個時候是否是須要不少的if語句來判斷呢?但是if語句寫多了又很差維護;
若是你的算法規則不但願被別人看見,該怎麼辦;
這一切都交給策略模式吧。
定義一組算法,將每一個算法都封裝起來,而且使它們之間能夠互換。
策略模式的通用類圖:
Context 封裝角色,它也叫作上下文角色,起承上啓下封裝做用,屏蔽高層模塊對策略、算法的直接訪問,封裝可能存在的變化。
Strategy 抽象策略角色,策略、算法家族的抽象,一般爲接口,定義每一個策略或算法必須具備的方法和屬性。
ConcreteStrategy 具體策略角色,實現抽象策略中的操做,該類含有具體的算法。
通用代碼:
//抽象的策略角色 public interface Strategy { //策略模式的運算法則 public void doSomething(); } //具體策略角色 public class ConcreteStrategy1 implements Strategy { public void doSomething() { System.out.println("具體策略1的運算法則"); } } public class ConcreteStrategy2 implements Strategy { public void doSomething() { System.out.println("具體策略2的運算法則"); } } //封裝角色 public class Context { //抽象策略 private Strategy strategy = null; //構造函數設置具體策略 public Context(Strategy _strategy){ this.strategy = _strategy; } //封裝後的策略方法 public void doAnythinig(){ this.strategy.doSomething(); } } //場景類 public class Client { public static void main(String[] args) { //聲明一個具體的策略 Strategy strategy = new ConcreteStrategy1(); //聲明上下文對象 Context context = new Context(strategy); //執行封裝後的方法 context.doAnythinig(); } }
首先,算法能夠自由切換。這是策略模式自己定義的,只要實現抽象策略,它就成爲策略家族的一個成員,經過封
裝角色對其進行封裝,保證對外提供「可自由切換」的策略。
其次,避免使用多重條件判斷。若是沒有策略模式,咱們想一想看會是什麼樣子?一個策略家族有5個策略算法,一會要
使用A策略,一會要使用B策略,怎麼設計呢?使用多重的條件語句?多重條件語句不易維護,並且出錯的機率大大加強。使用策略模式後,能夠由其餘模塊決定採用何種策略,策略家族對外提供的訪問接口就是封裝類,簡化了操做,同時避免了條件語句判斷。
最後,擴展性良好。在現有的系統中增長一個策略太容易了,只要實現接口就能夠了,其餘都不用修改,相似於一個可反覆拆卸的插件,這大大地符合了OCP原則。
還有不少設計模式我就不一一闡述了。你們能夠找找資料看看。
我感受設計模式說白了就是對面向對象編程的一種補充,把面向對象方法所但願達到的效果進一步完善罷了。可是這些模式還得在實戰中慢慢體會,之後編程過程當中慢慢加深運用設計模式,相信會對咱們寫出可複用、易維護的代碼有幫助的。
本文參考了《大話設計模式》,這本書通俗易懂,風趣幽默,強烈推薦給你們~
歡迎訪問個人主頁(http://mageek.cn)