面向對象與面向過程:首先我們要明確一定:無論是面向對象還是面向過程他們都是解決同一個問題,只是方式不同而已。1.面向過程將程式看作一系列函數的集合,而面向對象將程式看作一種在程式中包含各種獨立而又互相調用的對象的集合。2.面向過程就是分析出解決問題所需的步驟,面向對象則是把問題中存在關鍵的事物抽取....
面向對象與面向過程:
首先我們要明確一定:
無論是面向對象還是面向過程他們都是解決同一個問題,只是方式不同而已。
1.面向過程將程式看作一系列函數的集合,而面向對象將程式看作一種在程式中包含各種獨立而又互相調用的對象的集合。
2.面向過程就是分析出解決問題所需的步驟,面向對象則是把問題中存在關鍵的事物抽取成對象。
抽取對象的目的在於將這些步驟分配到對應的對象中,由對象管理自己的行為。
比如:如何大象裝進冰箱?
面向過程:
為了把大象裝進冰箱,需要3個過程。
a.把冰箱門打開(得到打開門的冰箱)
b.把大象裝進去(打開門後,得到裡面裝著大象的冰箱)
c.把冰箱門關上(打開門、裝好大象後,獲得關好門的冰箱)
每個過程有一個階段性的目標,依次完成這些過程,就能把大象裝進冰箱。
面向對象:
為了把大象裝進冰箱,需要做三個動作(或者叫行為)。
每個動作有一個執行者,它就是對象。
a.冰箱,你給我把門打開
b.冰箱,你給我把大象裝進去(或者說,大象,你給我鑽到冰箱里去)
c.冰箱,你給我把門關上依次做這些動作,就能把大象裝進冰箱。
3.面向過程要關註每一個步驟的實現細節,關註度比較高,每次都要打開函數去理解每一步的含義。
面向對象不關註實現細節,只會關註這個步驟由哪個對象負責提供,對象對自己提供的服務負責。
4.面向對象編程是一種設計思想,OOP把對象作為程式的基本單元對象包含了屬性以及操作這些屬性的方法。
我認為兩者並不是對立的,而是兩者相互促進,oo思想能夠幫助我們以符合人類思維的方式進行思考問題,
面向過程的思想可以幫助我們在實現對象中提供的介面時保證邏輯順序的正確。
對象是什麼
面向對象是一種程式的設計思想,可以讓我們更符合人們的思維習慣。
面向對象的關鍵是在於“對象”,那什麼是對象呢?
相信大家一定聽過“一切皆對象”,這裡的對象指的是我們思考問題的目標物體,
比如:動物,地鐵,人,車這些都是我們關註的目標物體,也就是我們要思考的對象。
所有程式中存在的對象都是基於業務需求的環境下抽取的,否則將是無邊界的抽取,
它不僅能表示具體的事物,還能表示抽象的規則、計劃或事件。
所有說對象是面向對象的構建模塊,思考和設計的時候都是以對象為基本單元。
對象都有哪些構成
對象是屬性和行為集合的載體,從某種程度上說對象必須有屬性和對自身屬性操作的行為。
經常看到一些存放一堆函數沒有任何屬性的對象,嚴格意義上不能稱之為對象。
每一個對象都有一種實際的意義,賦予它的職責,對象只對屬於自己屬性和行為負責,也就我們
常說的“單一職責”。
對象之間通過消息進行交互
對象的存在必然是為了和其他對象進行交互,沒有任何交互的對象可以放棄啦。
比如:你踢貓,你是一個對象;你踢的那隻貓也是一個對象。你們兩個對象之間就是一種交互。
那這種交互又是如何發生的呢?在你踢貓這個例子中,你踢貓是你這個對象使用自身的踢這個行為,
這個行為作用到了貓這個對象身上。貓在被踢後,喵喵叫著跑離開你。
在這裡,貓這個對象的叫和跑這兩個行為得到了執行。
那試想,是誰執行了這兩個行為?顯然是貓,但這裡與其說是貓,還不如說是你在執行踢行為的過程中執行了貓的叫行為和跑行為。
不是麽?難道你踢貓不正是想讓貓走開,或者聽幾聲貓的慘叫來取樂?
所以假如你的名字叫Jason,你的貓叫Jack.那麼我們可以認為:Jason在踢方法內,調用了Jack的叫方法和跑方法。
用面向對象的記號記作:“Jason.踢(Jack)”調用了“Jack.叫()”和“Jack.跑()”。括弧中的Jack是Jason對象踢方法的參數,表示踢行為的作用對象。
用另外一種說法,我們認為:Jason在執行踢方法的過程中,給Jack發送了兩個消息以作為命令,Jack收到此兩消息後,執行了自己的方法。
這正是對象之間交互的實質所在,也即對象之間通過發送消息來進行交互。
類和對象的關係:
類是描述某些具有共性事物的一個抽象概念,它不是一個客觀存在的東西,它就是一個模板。
類可以定義對象
簡單說:類是對象的模板,對象是類的實例。
類的抽取過程是從眾多對象中提取出相似特征和動作進行封裝的,不是憑空捏造的,比如:
小明是一個學生,小花是一個學生,經過我們從這一個個對象中我們發現他們都有相同的屬性和行為,
這個時候就可以使用學生類進行承載。思考的過程是先有對象再有類,使用的時候現有類再創建對象。
需要強調一點:每個對象都擁有相同的方法,但各自的數據可能不同。
類都有哪些構成?
一個類通常有哪些元素構成,這些元素都有什麼存在價值,換句話我們可以通過這些元素可以乾什麼。
【類名】描述類的職責,知名達意
類名的好壞直接影響我們對這個類職責的定義,所以最好花點時間去琢磨一下類的命名。
【註釋】使用註釋說明方法,屬性,類職責
【屬性】用來表示對象狀態
【構造函數】 可以為創建對象提供多種形態的入口,可以初始化對象
構造函數是在對象初始化的時候使用的,通常某個對象在提供自己的對外服務的時候,必須讓其他對象傳遞相關的數據,
在構造函數中進行接受這些數據是經常的做法,如果某些數據是無關緊要的可以不用再對象創建時就提供的建議不要通過構造,
從某種意義上來說構造函數的用途就是為對象獲取整個生命周期重要的數據提供一個入口,對外表達一個意義: 你要想使用我(對象),就必須提供給我這些數據,否則我(對象)的服務是不能提供給你的。
比如:資料庫持久對象,在使用它提供操作數據服務之前必須要給它提供數據源連接的信息,不然是不可以使用的,具有強制性。
構造函數經常用來:
1.初始化本對象的所有屬性。
2.強制用戶類提供相關數據,並提供多種創建該對象的方法(構造函數重載)
3.也可以在類初始化的時候做其他的業務操作。
【訪問方法 setter】保證訪問屬性的安全,修改屬性保證只有一處
這也是面向對象又一特性"封裝"的體現,控制自身屬性的安全訪問。
【公共方法】 提供類對外的服務
提供公共方法也是類存在的重要職責,一個沒有任何對外服務的類可以放棄啦。
類內部提供方法的實現細節,而類的使用者不用關心具體謝姐。
提公共方法的命名一樣很重要,
【私有方法】 將不想用戶關心的實現,放到私有方法中。
【作用域】 控制屬性的訪問範圍。
怎麼創建一個合理的類
1.提供合適的構造函數。
2.讓類屬性的作用域儘可能小。
3.對類中的元素要合理添加上註釋,沒有任何註釋的類如果命名再不合理就會造成維護理解困難。
4.提供合適的對外介面,這個下麵有專門講。
5.設計的類必然是要和其他的類進行交互的,要麼是調用其他的服務,要麼本身提供服務,沒有這兩點的類可以幹掉啦。
6.要知道一個原則:類應該只對自己負責
自身狀態的變更:
比如:商品的狀態由上架,下架。如果要變更商品的狀態應該有商品自己去提供變更狀態的公共介面。
自身提供的服務:
比如:畫圖形,方形,圓形,星型它們內容應該是知道如何畫出自己的圖形。這就是對象負責自己的行為。
7.減少對象之間的依賴
相互依賴的比較少,也就是說一個類的修改不會對其他類產生影響,或者影響很小。
我們經常看到使用hibernate的對象之間關係維護都是對象中直接持有另外一個對象,或集合。
甚至將屬於另外一個類職責的行為放到了本類中。
這樣的設計會增加對象之間的依賴。
如何以面向對象的方式提供對外介面
這裡的對外介面指的是公共的函數。
一.設計介面要使用抽象思維,不體現實現的任何細節。
比如:做計程車去飛機場
計程車提供一個介面,叫做:去目的地就行啦
人只需要使用這個介面告訴計程車目的地就可以啦。不需要人關係有多遠,怎麼走。。。等細節信息。
這就要求我們在方法的命名上以及註釋上都必須保持抽象的思維用自然的語言去描述自己的函數。
二.為用戶提供最小的介面,用戶關註越小越好
1.以用戶需求為驅動編寫需要的介面,不要意淫要通過用例圖和流程圖來確定各個對象需要提供的介面。
比如:筆和筆記本,筆只想筆記本提供空白頁讓筆書寫,
但是筆記本覺得應該給筆提供一個畫好的格子頁面,這就是自作多情。
2.確定受眾用戶
註意: 有兩種身份關註介面,一種是最終用戶(互聯網用戶),一種是開發者之間的類介面。
互聯網用戶需要什麼服務我們就提供什麼介面,以此為驅動開發者,去理清這個服務的所有相關的業務,去抽象,
最終可能抽取出多個對象之間相互通信完成這個業務,其中對象之間的通信也叫介面,只不過這個介面是開發者關註的。
比如:各種service之間的介面它的受眾是開發者,module層中的module類就是互聯網用戶。
3.每個介面都要有明確的職責定義
確定的介面一定是只做一件事,單一的。
比如:下訂單是一個對外介面,你不能包含付錢的邏輯。
4.不要輕易修改你的公共介面(服務)
因為你不知道有多少類已經使用了你的這個介面,排查比較複雜
如何利用對象實現系統設計:
一、根據需求抽取出合適的類。
列舉需求或者產品原型中出現的所有名詞,根據名詞列表以及需求描述迭代分析找出真正的類(屬性和行為)
二、確定這些類的職責
1.從這個類需要負責的動作中抽象出這個類職責,能夠用一句話描述清楚。
2.找出這個類必須的屬性。
3.找出這個類必須的操作(只關註類的介面,不會關註實現)
三、確定這些類的關係
根據需求描述使用類去模擬實現的過程,找出對象之間的關係。
對象之間都有哪些關係可以參考另外一個博客:我對uml類圖的理解
四、利用建模語言:uml
使用uml的好處就是通過uml可以將你理解的對象關係向別人講解,也能幫助我們梳理和回顧。
對象的封裝:
封裝的本質:將不需要對外提供的內容都隱藏起來。
那我怎麼做才能算得上封裝呢?
1.將對象(類)中的屬性全部私有,並提供公有訪問該屬性的方法(就是咱們通常說的getter(),setter())
控制自身屬性對外界的訪問。【屬性的封裝】
2.只提供用戶關係的介面,隱藏實現的細節。【方法的封裝】
從上面就可以看出對象只封裝屬於自身內部的屬性和行為,所以她是獨立不依賴其他對象就可以實現自我管理。【好處】
對象之間的繼承:
繼承是指這樣一種能力:它可以使用現有類的所有功能,併在無需重新編寫原來的類的情況下對這些功能進行擴展。
在java中繼承的概念不僅限於"extend",其中我們經常使用的實現“implement”介面也是一種繼承。
子類和父類的關係一定是:屬於的關係即is-a
在java中繼承只能是單繼承不能多繼承但是可以多級繼承。
在java判斷對象是否存在is-a的關係可以使用關鍵字:instanceof判斷。
特別註意:
1.構造方法不能被繼承。
2.方法和屬性可以被繼承。
3.子類的構造方法隱式地調用父類的無參構造方法。
4.當父類沒有不帶參數的構造方法時,子類需要使用super關鍵字來顯示調用父類的構造方法。super指的是父類的引用。
5.super關鍵字必須是構造方法中的第一句。
6.生成子類對象時,Java預設地首先調用父類的不帶參數的構造方法,並執行該構造方法,生成父類的對象。
接下來才是調用子類的構造方法,生成子類的對象。(即是想要生成子類對象,必須先生成父類的對象,沒有父類的對象就沒有子類的對象)
繼承在實際的應用:
1.一般不會直接繼承一個已經編寫好的類,針對已有的類擴展我們都是採用組合的方式,對現有的舊介面進行包裝提供新的介面。
2.在模板模式中使用抽象類繼承介面,為真正的子類提供共有的預設實現。
對象的多態:
多態給我們帶來的好處是消除了類之間的耦合關係,使程式更容易擴展。
Java實現多態有三個必要條件:繼承(實現)、重寫、向上轉型。
繼承:在多態中必須存在有繼承關係的子類和父類以及實現關係的介面和實現類。
重寫:子類對父類中某些方法進行重新定義,在調用這些方法時就會調用子類的方法。
向上轉型:在多態中需要將子類的引用賦給父類對象,只有這樣該引用才能夠具備技能調用父類的方法和子類的方法。
只有滿足了上述三個條件,我們才能夠���同一個繼承結構中使用統一的邏輯實現代碼處理不同的對象,從而達到執行不同的行為。
先有繼承關係或者實現關係後才有多態。
其中,不同的對象可以執行相同的行為,但是他們都需要通過自己的實現方式來執行,這就要得益於向上轉型了。
我們需要知道:當子類重寫父類的方法被調用時,只有對象繼承鏈中的最末端的方法才會被調用。
多態的好處就是,當我們需要傳入Dog、Cat、Tortoise……時,我們只需要接收Animal類型就可以了,因為Dog、Cat、Tortoise……都是Animal類型,然後,按照Animal類型進行操作即可。由於Animal類型有run()方法,因此,傳入的任意類型,只要是Animal類或者子類,就會自動調用實際類型的run()方法,這就是多態。
實際應用:
1.依賴父類或者介面作為方法的參數。
2.依賴的對象如果有繼承關係我們就引用父類類型作為變數的數據類型。
方法重載也是一種多態:
可以實現對象對外提供多種形態(不同種類,不同數量的參數)的相同服務。
比如:訂單類有一個下訂單的介面叫create(),業務需求是:通過商品可以下訂單,通過某個活動也可以下定單;
這個時候我們對外暴露介面都是"下訂單",我們不想改變下訂單這個服務(介面)的定義,我們就可以通過方法的重載去實現。
create(商品) create(活動) 用戶可以根據自己的需求去選擇使用。