Day6 - Python基礎6 面向對象編程 --轉自金角大王

本節內容:
 
面向對象編程介紹
爲何要用面向對象進行開發?
面向對象的特性:封裝、繼承、多態
類、方法、
 
 

引子

你如今是一家遊戲公司的開發人員,如今須要你開發一款叫作<人狗大戰>的遊戲,你就思考呀,人狗做戰,那至少須要2個角色,一個是人, 一個是狗,且人和狗都有不一樣的技能,好比人拿棍打狗, 狗能夠咬人,怎麼描述這種不一樣的角色和他們的功能呢?
 
你搜羅了本身掌握的全部技能,寫出了下面的代碼來描述這兩個角色

上面兩個方法至關於造了兩個模子,遊戲開始,你得生成一我的和狗的實際對象吧,怎麼生成呢?html

 

兩個角色對象生成了,狗和人還有不一樣的功能呀,狗會咬人,人會打狗,對不對? 怎麼實現呢,。。想到了, 能夠每一個功能再寫一個函數,想執行哪一個功能,直接 調用 就能夠了,對不?python

上面的功能實現的簡直是完美!linux

可是仔細玩耍一會,你就不當心幹了下面這件事程序員

事實 上,這並沒出錯。很顯然,人是不能調用狗的功能的,如何在代碼級別實現這個限制呢?算法

你是如此的機智,這樣就實現了限制人只能用人本身的功能啦。數據庫

但,個人哥,不要高興太早,剛纔你只是阻止了兩個徹底 不一樣的角色 以前的功能混用, 但有沒有可能 ,同一個種角色,但有些屬性是不一樣的呢? 好比 ,你們都打過cs吧,cs裏有警察和恐怖份子,但由於都 是人, 因此你寫一個角色叫 person(), 警察和恐怖份子都 能夠 互相射擊,但警察不能夠殺人質,恐怖分子能夠,這怎麼實現呢? 你想了說想,說,簡單,只須要在殺人質的功能里加個判斷,若是是警察,就不讓殺不就ok了麼。 沒錯, 這雖然 解決了殺人質的問題,但其實你會發現,警察和恐怖分子的區別還有不少,同時又有不少共性,若是 在每一個區別處都 單獨作判斷,那得累死。 編程

你想了想說, 那就直接寫2個角色吧, 反正 這麼多區別, 個人哥, 不能寫兩個角色呀,由於他們還有不少共性 , 寫兩個不一樣的角色,就表明 相同的功能 也要重寫了,是否是個人哥? 。。。數據結構

好了, 話題就給你點到這, 再多說你的智商 也理解不了了!運維

 

 

  

 

  

面向過程 VS 面向對象 

編程範式

編程是 程序 員 用特定的語法+數據結構+算法組成的代碼來告訴計算機如何執行任務的過程 , 一個程序是程序員爲了獲得一個任務結果而編寫的一組指令的集合,正所謂條條大路通羅馬,實現一個任務的方式有不少種不一樣的方式, 對這些不一樣的編程方式的特色進行概括總結得出來的編程方式類別,即爲編程範式。 不一樣的編程範式本質上表明對各類類型的任務採起的不一樣的解決問題的思路, 大多數語言只支持一種編程範式,固然也有些語言能夠同時支持多種編程範式。 兩種最重要的編程範式分別是面向過程編程和麪向對象編程。編程語言

面向過程編程(Procedural Programming)
Procedural programming uses a list of instructions to tell the computer what to do step-by-step.
面向過程編程依賴 - 你猜到了- procedures,一個procedure包含一組要被進行計算的步驟, 面向過程又被稱爲top-down languages, 就是程序從上到下一步步執行,一步步從上到下,從頭至尾的解決問題 。基本設計思路就是程序一開始是要着手解決一個大的問題,而後把一個大問題分解成不少個小問題或子過程,這些子過程再執行的過程再繼續分解直到小問題足夠簡單到能夠在一個小步驟範圍內解決。

舉個典型的面向過程的例子, 數據庫備份, 分三步,鏈接數據庫,備份數據庫,測試備份文件可用性。

代碼以下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def db_conn():
     print ( "connecting db..." )
 
 
def db_backup(dbname):
     print ( "導出數據庫..." ,dbname)
     print ( "將備份文件打包,移至相應目錄..." )
 
def db_backup_test():
     print ( "將備份文件導入測試庫,看導入是否成功" )
 
 
def main():
     db_conn()
     db_backup( 'my_db' )
     db_backup_test()
 
 
 
if __name__ = = '__main__' :
     main()

  

這樣作的問題也是顯而易見的,就是若是你要對程序進行修改,對你修改的那部分有依賴的各個部分你都也要跟着修改, 舉個例子,若是程序開頭你設置了一個變量值 爲1 , 但若是其它子過程依賴這個值 爲1的變量才能正常運行,那若是你改了這個變量,那這個子過程你也要修改,假如又有一個其它子程序依賴這個子過程 , 那就會發生一連串的影響,隨着程序愈來愈大, 這種編程方式的維護難度會愈來愈高。
因此咱們通常認爲, 若是你只是寫一些簡單的腳本,去作一些一次性任務,用面向過程的方式是極好的,但若是你要處理的任務是複雜的,且須要不斷迭代和維護 的, 那仍是用面向對象最方便了。

 
 

面向對象編程

OOP編程是利用「類」和「對象」來建立各類模型來實現對真實世界的描述,使用面向對象編程的緣由一方面是由於它可使程序的維護和擴展變得更簡單,而且能夠大大提升程序開發效率 ,另外,基於面向對象的程序可使它人更加容易理解你的代碼邏輯,從而使團隊開發變得更從容。

面向對象的幾個核心特性以下

Class 類
一個類便是對一類擁有相同屬性的對象的抽象、藍圖、原型。在類中定義了這些對象的都具有的屬性(variables(data))、共同的方法

Object 對象
一個對象便是一個類的實例化後實例,一個類必須通過實例化後方可在程序中調用,一個類能夠實例化多個對象,每一個對象亦能夠有不一樣的屬性,就像人類是指全部人,每一個人是指具體的對象,人與人以前有共性,亦有不一樣

Encapsulation 封裝
在類中對數據的賦值、內部調用對外部用戶是透明的,這使類變成了一個膠囊或容器,裏面包含着類的數據和方法

Inheritance 繼承
一個類能夠派生出子類,在這個父類裏定義的屬性、方法自動被子類繼承

Polymorphism 多態
多態是面向對象的重要特性,簡單點說:「一個接口,多種實現」,指一個基類中派生出了不一樣的子類,且每一個子類在繼承了一樣的方法名的同時又對父類的方法作了不一樣的實現,這就是同一種事物表現出的多種形態。
編程其實就是一個將具體世界進行抽象化的過程,多態就是抽象化的一種體現,把一系列具體事物的共同點抽象出來, 再經過這個抽象的事物, 與不一樣的具體事物進行對話。
對不一樣類的對象發出相同的消息將會有不一樣的行爲。好比,你的老闆讓全部員工在九點鐘開始工做, 他只要在九點鐘的時候說:「開始工做」便可,而不須要對銷售人員說:「開始銷售工做」,對技術人員說:「開始技術工做」, 由於「員工」是一個抽象的事物, 只要是員工就能夠開始工做,他知道這一點就好了。至於每一個員工,固然會各司其職,作各自的工做。
多態容許將子類的對象看成父類的對象使用,某父類型的引用指向其子類型的對象,調用的方法是該子類型的方法。這裏引用和調用方法的代碼編譯前就已經決定了,而引用所指向的對象能夠在運行期間動態綁定

 

面向對象編程(Object-Oriented Programming )介紹

 
對於編程語言的初學者來說,OOP不是一個很容易理解的編程方式,你們雖然都按老師講的都知道OOP的三大特性是繼承、封裝、多態,而且你們也都知道了如何定義類、方法等面向對象的經常使用語法,可是一到真正寫程序的時候,仍是不少人喜歡用函數式編程來寫代碼,特別是初學者,很容易陷入一個窘境就是「我知道面向對象,我也會寫類,但我依然沒發如今使用了面向對象後,對咱們的程序開發效率或其它方面帶來什麼好處,由於我使用函數編程就能夠減小重複代碼並作到程序可擴展了,爲啥子還用面向對象?」。 對於此,我我的以爲緣由應該仍是由於你沒有充分了解到面向對象能帶來的好處,今天我就寫一篇關於面向對象的入門文章,但願能幫你們更好的理解和使用面向對象編程。  
 
不管用什麼形式來編程,咱們都要明確記住如下原則:
  1. 寫重複代碼是很是很差的低級行爲
  2. 你寫的代碼須要常常變動 
 
開發正規的程序跟那種寫個運行一次就扔了的小腳本一個很大不一樣就是,你的代碼老是須要不斷的更改,不是修改bug就是添加新功能等,因此爲了往後方便程序的修改及擴展,你寫的代碼必定要遵循易讀、易改的原則(專業數據叫可讀性好、易擴展)。
 
若是你把一段一樣的代碼複製、粘貼到了程序的多個地方以實如今程序的各個地方調用 這個功能,那往後你再對這個功能進行修改時,就須要把程序裏多個地方都改一遍,這種寫程序的方式是有問題的,由於若是你不當心漏掉了一個地方沒改,那可能會致使整個程序的運行都 出問題。 所以咱們知道 在開發中必定要努力避免寫重複的代碼,不然就至關於給本身再挖坑。
 
還好,函數的出現就能幫咱們輕鬆的解決重複代碼的問題,對於須要重複調用的功能,只須要把它寫成一個函數,而後在程序的各個地方直接調用這個函數名就行了,而且當須要修改這個功能時,只需改函數代碼,而後整個程序就都更新了。
 
其實OOP編程的主要做用也是使你的代碼修改和擴展變的更容易,那麼小白要問了,既然函數都能實現這個需求了,還要OOP幹毛線用呢? 呵呵,說這話就像,古時候,人們打仗殺人都用刀,後來出來了槍,它的主要功能跟刀同樣,也是殺人,而後小白就問,既然刀能殺人了,那還要槍幹毛線,哈哈,顯而易見,由於槍能更好更快更容易的殺人。函數編程與OOP的主要區別就是OOP可使程序更加容易擴展和易更改。
 
小白說,我讀書少,你別騙我,口說無憑,證實一下,好吧,那咱們就下面的例子證實給小白看。 
相信你們都打過CS遊戲吧,咱們就本身開發一個簡單版的CS來玩一玩。 
 
 
 
暫不考慮開發場地等複雜的東西,咱們先從人物角色下手, 角色很簡單,就倆個,恐怖份子、警察,他們除了角色不一樣,其它基本都 同樣,每一個人都有生命值、武器等。 我們先用非OOP的方式寫出遊戲的基本角色 
?
1
2
3
4
5
6
7
8
9
10
11
#role 1
name = 'Alex'
role = 'terrorist'
weapon = 'AK47'
life_value = 100
 
#rolw 2
name2 = 'Jack'
role2 = 'police'
weapon2 = 'B22'
life_value2 = 100

 

上面定義了一個恐怖份子Alex和一個警察Jack,但只2我的很差玩呀,一干就死了,沒意思,那咱們再分別一個恐怖分子和警察吧,
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#role 1
name = 'Alex'
role = 'terrorist'
weapon = 'AK47'
life_value = 100
money = 10000
 
#rolw 2
name2 = 'Jack'
role2 = 'police'
weapon2 = 'B22'
life_value2 = 100
money2 = 10000
 
#role 3
name3 = 'Rain'
role3 = 'terrorist'
weapon3 = 'C33'
life_value3 = 100
money3 = 10000
 
#rolw 4
name4 = 'Eric'
role4 = 'police'
weapon4 = 'B51'
life_value4 = 100
money4 = 10000

 

4個角色雖然建立好了,可是有個問題就是,每建立一個角色,我都要單獨命名,name1,name2,name3,name4…,後面的調用的時候這個變量名你還都得記着,要是再讓多加幾個角色,估計調用時就很容易弄混啦,因此咱們想想,可否全部的角色的變量名都是同樣的,但調用的時候又能區分開分別是誰? 
 
固然能夠,咱們只須要把上面的變量改爲字典的格式就能夠啦。 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
roles = {
     1 :{ 'name' : 'Alex' ,
        'role' : 'terrorist' ,
        'weapon' : 'AK47' ,
        'life_value' : 100 ,
        'money' : 15000 ,
        },
     2 :{ 'name' : 'Jack' ,
        'role' : 'police' ,
        'weapon' : 'B22' ,
        'life_value' : 100 ,
         'money' : 15000 ,
        },
     3 :{ 'name' : 'Rain' ,
        'role' : 'terrorist' ,
        'weapon' : 'C33' ,
        'life_value' : 100 ,
        'money' : 15000 ,
        },
     4 :{ 'name' : 'Eirc' ,
        'role' : 'police' ,
        'weapon' : 'B51' ,
        'life_value' : 100 ,
        'money' : 15000 ,
        },
}
 
print (roles[ 1 ]) #Alex
print (roles[ 2 ]) #Jack

 

很好,這個之後調用這些角色時只須要roles[1],roles[2]就能夠啦,角色的基本屬性設計完了後,咱們接下來爲每一個角色開發如下幾個功能
  1. 被打中後就會掉血的功能
  2. 開槍功能
  3. 換子彈
  4. 買槍
  5. 跑、走、跳、下蹲等動做
  6. 保護人質(僅適用於警察)
  7. 不能殺同伴
  8. 。。。
咱們能夠把每一個功能寫成一個函數,相似以下: 
?
1
2
3
4
5
6
7
8
9
10
11
def shot(by_who):
     #開了槍後要減子彈數
     pass
def got_shot(who):
     #中槍後要減血
     who[‘life_value’] - = 10
     pass
def buy_gun(who,gun_name):
     #檢查錢夠不夠,買了槍後要扣錢
     pass
...

 

so far so good, 繼續按照這個思路設計,再完善一下代碼,遊戲的簡單版就出來了,可是在往下走以前,咱們來看看上面的這種代碼寫法有沒有問題,至少從上面的代碼設計中,我看到如下幾點缺陷:
  1. 每一個角色定義的屬性名稱是同樣的,但這種命名規則是咱們本身約定的,從程序上來說,並無進行屬性合法性檢測,也就是說role 1定義的表明武器的屬性是weapon, role 2 ,3,4也是同樣的,不過若是我在新增一個角色時不當心把weapon 寫成了wepon , 這個程序自己是檢測 不到的
  2. terrorist 和police這2個角色有些功能是不一樣的,好比police是不能殺人質的,可是terrorist可能,隨着這個遊戲開發的更復雜,咱們會發現這2個角色後續有更多的不一樣之處, 但如今的這種寫法,咱們是沒辦法 把這2個角色適用的功能區分開來的,也就是說,每一個角色均可以直接調用任意功能,沒有任何限制。
  3. 咱們在上面定義了got_shot()後要減血,也就是說減血這個動做是應該經過被擊中這個事件來引發的,咱們調用get_shot(),got_shot()這個函數再調用每一個角色裏的life_value變量來減血。 但其實我不經過got_shot(),直接調用角色roles[role_id][‘life_value’] 減血也能夠呀,可是若是這樣調用的話,那能夠就是簡單粗暴啦,由於減血以前其它還應該判斷此角色是否穿了防彈衣等,若是穿了的話,傷害值確定要減小,got_shot()函數裏就作了這樣的檢測,你這裏直接繞過的話,程序就亂了。 所以這裏應該設計 成除了經過got_shot(),其它的方式是沒有辦法給角色減血的,不過在上面的程序設計裏,是沒有辦法實現的。 
  4. 如今須要給全部角色添加一個能夠穿防彈衣的功能,那很顯然你得在每一個角色裏放一個屬性來存儲此角色是否穿 了防彈衣,那就要更改每一個角色的代碼,給添加一個新屬性,這樣太low了,不符合代碼可複用的原則
 
 
上面這4點問題若是不解決,之後確定會引出更大的坑,有同窗說了,解決也不復雜呀,直接在每一個功能調用時作一下角色判斷啥就行了,沒錯,你要非得這麼霸王硬上弓的搞也確定是能夠實現的,那你本身就開發相應的代碼來對上面提到的問題進行處理好啦。 但這些問題其實能過OOP就能夠很簡單的解決。 
 
以前的代碼改爲用OOP中的「類」來實現的話以下: 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Role( object ):
     def __init__( self ,name,role,weapon,life_value = 100 ,money = 15000 ):
         self .name = name
         self .role = role
         self .weapon = weapon
         self .life_value = life_value
         self .money = money
 
     def shot( self ):
         print ( "shooting..." )
 
     def got_shot( self ):
         print ( "ah...,I got shot..." )
 
     def buy_gun( self ,gun_name):
         print ( "just bought %s" % gun_name)
 
r1 = Role( 'Alex' , 'police' ,'AK47’) #生成一個角色
r2 = Role( 'Jack' , 'terrorist' ,'B22’)  #生成一個角色

 

先不考慮語法細節,相比靠函數拼湊出來的寫法,上面用面向對象中的類來寫最直接的改進有如下2點:
  1. 代碼量少了近一半
  2. 角色和它所具備的功能能夠一目瞭然看出來
 

在真正開始分解上面代碼含義之以前,咱們現來了解一些類的基本定義
類的語法
?
1
2
3
4
5
6
7
8
9
class Dog( object ):
 
     print ( "hello,I am a dog!" )
 
 
d = Dog() #實例化這個類,
#此時的d就是類Dog的實例化對象
 
#實例化,其實就是以Dog類爲模版,在內存裏開闢一塊空間,存上數據,賦值成一個變量名

上面的代碼其實有問題,想給狗起名字傳不進去。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
class Dog( object ):
 
     def __init__( self ,name,dog_type):
         self .name = name
         self . type = dog_type
 
     def sayhi( self ):
 
         print ( "hello,I am a dog, my name is " , self .name)
 
 
d = Dog( 'LiChuang' , "京巴" )
d.sayhi()

爲何有__init__? 爲何有self? 此時的你一臉蒙逼,相信不畫個圖,你的智商是理解不了的!  

畫圖以前, 你先註釋掉這兩句

?
1
2
3
4
# d = Dog('LiChuang', "京巴")
# d.sayhi()
 
print (Dog)

沒實例直接打印Dog輸出以下

?
1
< class '__main__.Dog' >

這表明什麼?表明 即便不實例化,這個Dog類自己也是已經存在內存裏的對不對, yes, cool,那實例化時,會產生什麼化學反應呢?

 根據上圖咱們得知,其實self,就是實例自己!你實例化時python會自動把這個實例自己經過self參數傳進去。

你說好吧,僞裝懂了, 但下面這段代碼你又不明白了, 爲什麼sayhi(self),要寫個self呢? 

?
1
2
3
4
5
class Dog( object ):
     ...
     def sayhi( self ):
 
         print ( "hello,I am a dog, my name is " , self .name)

這個緣由,我課上在講。。。    

?
1
<br><br><br><br>
 

 
好了,明白 了類的基本定義,接下來咱們一塊兒分解一下上面的代碼分別 是什麼意思 
?
1
2
3
4
5
6
7
class Role( object ): #定義一個類, class是定義類的語法,Role是類名,(object)是新式類的寫法,必須這樣寫,之後再講爲何
     def __init__( self ,name,role,weapon,life_value = 100 ,money = 15000 ): #初始化函數,在生成一個角色時要初始化的一些屬性就填寫在這裏
         self .name = name #__init__中的第一個參數self,和這裏的self都 是什麼意思? 看下面解釋
         self .role = role
         self .weapon = weapon
         self .life_value = life_value
         self .money = money

 

上面的這個__init__()叫作初始化方法(或構造方法), 在類被調用時,這個方法(雖然它是函數形式,但在類中就不叫函數了,叫方法)會自動執行,進行一些初始化的動做,因此咱們這裏寫的__init__(self,name,role,weapon,life_value=100,money=15000)就是要在建立一個角色時給它設置這些屬性,那麼這第一個參數self是幹毛用的呢? 
 
初始化一個角色,就須要調用這個類一次: 
?
1
2
r1 = Role( 'Alex' , 'police' ,'AK47’) #生成一個角色 , 會自動把參數傳給Role下面的__init__(...)方法
r2 = Role( 'Jack' , 'terrorist' ,'B22’)  #生成一個角色

咱們看到,上面的建立角色時,咱們並無給__init__傳值,程序也沒未報錯,是由於,類在調用它本身的__init__(…)時本身幫你給self參數賦值了, 

?
1
2
r1 = Role( 'Alex' , 'police' , 'AK47’) #此時self 至關於 r1 ,  Role(r1,' Alex ',' police ',' AK47’)
r2 = Role( 'Jack' , 'terrorist' , 'B22’)#此時self 至關於 r2, Role(r2,' Jack ',' terrorist ',' B22’)

 

爲何這樣子?你拉着我說你有些猶豫,怎麼會這樣子?
你執行 r1 = Role('Alex','police','AK47)時,python的解釋器其實幹了兩件事:
  1. 在內存中開闢一塊空間指向r1這個變量名
  2. 調用Role這個類並執行其中的__init__(…)方法,至關於Role.__init__(r1,'Alex','police',AK47’),這麼作是爲何呢? 是爲了把'Alex','police',’AK47’這3個值跟剛開闢的r1關聯起來,是爲了把'Alex','police',’AK47’這3個值跟剛開闢的r1關聯起來,是爲了把'Alex','police',’AK47’這3個值跟剛開闢的r1關聯起來,重要的事情說3次, 由於關聯起來後,你就能夠直接r1.name, r1.weapon 這樣來調用啦。因此,爲實現這種關聯,在調用__init__方法時,就必須把r1這個變量也傳進去,不然__init__不知道要把那3個參數跟誰關聯呀。
  3. 明白了麼哥?因此這個__init__(…)方法裏的,self.name = name , self.role = role 等等的意思就是要把這幾個值 存到r1的內存空間裏。
若是還不明白的話,哥,去測試一下智商吧, 應該不會超過70,哈哈。
爲了暴露本身的智商,此時你僞裝懂了,但又問, __init__(…)我懂了,但後面的那幾個函數,噢 不對,後面那幾個方法 爲何也還須要self參數麼? 不是在初始化角色的時候 ,就已經把角色的屬性跟r1綁定好了麼? 
good question, 先來看一下上面類中的一個buy_gun的方法: 
?
1
2
def buy_gun( self ,gun_name):
     print (「 % s has just bought % s」 % ( self .name,gun_name) )

 

上面這個方法經過類調用的話要寫成以下: 
 
?
1
2
r1 = Role( 'Alex' , 'police' , 'AK47' )
r1.buy_gun( "B21」) #python 會自動幫你轉成 Role.buy_gun(r1,」B21" )

 

執行結果
#Alex has just bought B21 
依然沒給self傳值 ,但Python仍是會自動的幫你把r1 賦值給self這個參數, 爲何呢? 由於,你在buy_gun(..)方法中可能要訪問r1的一些其它屬性呀, 好比這裏就訪問 了r1的名字,怎麼訪問呢?你得告訴這個方法呀,因而就把r1傳給了這個self參數,而後在buy_gun裏調用 self.name 就至關於調用r1.name 啦,若是還想知道r1的生命值 有多少,直接寫成self.life_value就能夠了。 說白了就是在調用類中的一個方法時,你得告訴人家你是誰。
 
好啦, 總結一下2點:
  1. 上面的這個r1 = Role('Alex','police','AK47)動做,叫作類的「實例化」, 就是把一個虛擬的抽象的類,經過這個動做,變成了一個具體的對象了, 這個對象就叫作實例
  2. 剛纔定義的這個類體現了面向對象的第一個基本特性,封裝,其實就是使用構造方法將內容封裝到某個具體對象中,而後經過對象直接或者self間接獲取被封裝的內容
 

面向對象的特性:

封裝

封裝最好理解了。封裝是面向對象的特徵之一,是對象和類概念的主要特性。

封裝,也就是把客觀事物封裝成抽象的類,而且類能夠把本身的數據和方法只讓可信的類或者對象操做,對不可信的進行信息隱藏。

繼承

面向對象編程 (OOP) 語言的一個主要功能就是「繼承」。繼承是指這樣一種能力:它可使用現有類的全部功能,並在無需從新編寫原來的類的狀況下對這些功能進行擴展。

經過繼承建立的新類稱爲「子類」或「派生類」。

被繼承的類稱爲「基類」、「父類」或「超類」。

繼承的過程,就是從通常到特殊的過程。

要實現繼承,能夠經過「繼承」(Inheritance)和「組合」(Composition)來實現。

在某些 OOP 語言中,一個子類能夠繼承多個基類。可是通常狀況下,一個子類只能有一個基類,要實現多重繼承,能夠經過多級繼承來實現。

繼承概念的實現方式主要有2類:實現繼承、接口繼承。

Ø         實現繼承是指使用基類的屬性和方法而無需額外編碼的能力;
Ø         接口繼承是指僅使用屬性和方法的名稱、可是子類必須提供實現的能力(子類重構爹類方法);
在考慮使用繼承時,有一點須要注意,那就是兩個類之間的關係應該是「屬於」關係。例如,Employee 是一我的,Manager 也是一我的,所以這兩個類均可以繼承 Person 類。可是 Leg 類卻不能繼承 Person 類,由於腿並非一我的。
 
抽象類僅定義將由子類建立的通常屬性和方法。

OO開發範式大體爲:劃分對象→抽象類→將類組織成爲層次化結構(繼承和合成) →用類與實例進行設計和實現幾個階段。

繼承示例

  

 

多態

 
多態性(polymorphisn)是容許你將父對象設置成爲和一個或更多的他的子對象相等的技術,賦值以後,父對象就能夠根據當前賦值給它的子對象的特性以不一樣的方式運做。簡單的說,就是一句話:容許將子類類型的指針賦值給父類類型的指針。
那麼,多態的做用是什麼呢?咱們知道,封裝能夠隱藏實現細節,使得代碼模塊化;繼承能夠擴展已存在的代碼模塊(類);它們的目的都是爲了——代碼重用。而多態則是爲了實現另外一個目的——接口重用!多態的做用,就是爲了類在繼承和派生的時候,保證使用「家譜」中任一類的實例的某一屬性時的正確調用。
 
Pyhon 不少語法都是支持多態的,好比 len(),sorted(), 你給len傳字符串就返回字符串的長度,傳列表就返回列表長度。
 
Python多態示例
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#_*_coding:utf-8_*_
 
 
class Animal( object ):
     def __init__( self , name):  # Constructor of the class
         self .name = name
 
     def talk( self ):              # Abstract method, defined by convention only
         raise NotImplementedError( "Subclass must implement abstract method" )
 
 
class Cat(Animal):
     def talk( self ):
         print ( '%s: 喵喵喵!' % self .name)
 
 
class Dog(Animal):
     def talk( self ):
         print ( '%s: 汪!汪!汪!' % self .name)
 
 
 
def func(obj): #一個接口,多種形態
     obj.talk()
 
c1 = Cat( '小晴' )
d1 = Dog( '李磊' )
 
func(c1)
func(d1)

  

 

領域模型 

好了,你如今會了面向對象的各類語法了, 那請看下本章最後的做業需求,我相信你多是矇蔽的, 不少同窗都是學會了面向對象的語法,卻依然寫不出面向對象的程序,緣由是什麼呢?緣由就是由於你還沒掌握一門面向對象設計利器, 你說我讀書少別騙我, 什麼利器? 

答案就是:領域建模。 從領域模型開始,咱們就開始了面向對象的分析和設計過程,能夠說,領域模型是完成從需求分析到面向 對象設計的一座橋樑。 

 

領域模型,顧名思義,就是需求所涉及的領域的一個建模,更通俗的講法是業務模型。 參考百度百科(http://baike.baidu.cn/view/757895.htm ),領域模型定義以下: 

從這個定義咱們能夠看出,領域模型有兩個主要的做用:

  1. 發掘重要的業務領域概念
  2. 創建業務領域概念之間的關係 

 

 

領域建模三字經 

領域模型如此重要,不少同窗可能會認爲領域建模很複雜,須要很高的技巧。然而事實上領域建模很是簡 單,簡單得有點難以讓人相信,領域建模的方法歸納一下就是「找名詞」! 許多同窗看到這個方法後估計都會笑出來:太假了吧,這麼簡單,找個初中生都會啊,那咱們公司那些分 析師和設計師還有什麼用哦?

分析師和設計師固然有用,後面咱們會看到,即便是簡單的找名詞這樣的操做,也涉及到分析和提煉,而 不是簡單的摘取出來就可,這種狀況下分析師和設計師的經驗和技能就可以派上用場了。但領域模型分析 也確實相對簡單,即便沒有豐富的經驗和高超的技巧,至少也能完成一個能用的領域模型。 

雖然咱們說「找名詞」很簡單,但一個關鍵的問題尚未說明:從哪裏找? 若是你還記得領域模型是「需求到面向對象的橋樑」,那麼你確定一會兒就能想到:從需求模型中找,具 體來講就是從用例中找。 

 

概括一下域建模的方法就是「從用例中找名詞」。 固然,找到名詞後,爲了可以更加符合面向對象的要求和特色,咱們還須要對這些名詞進一步完善,這就 是接下來的步驟:加屬性,連關係

 

 

最後咱們總結出領域建模的三字經方法:找名詞、加屬性、連關係。 

  

 

找名詞

 

who : 學員、講師、管理員

 

用例:

1. 管理員 建立了 北京 和 上海 兩個校區

2. 管理員 建立了 Linux \ Python \ Go 3個課程 

3. 管理員 建立了 北京校區的Python 16期, Go開發第一期,和上海校區的Linux 36期 班級

4. 管理員 建立了 北京校區的 學員 小晴 ,並將其 分配 在了 班級  python 16期 

5. 管理員 建立了 講師 Alex , 並將其分配 給了 班級 python 16期 和全棧脫產5期

6. 講師 Alex 建立 了一條 python 16期的 上課紀錄 Day6 

7. 講師 Alex 爲Day6這節課 全部的學員 批了做業 ,小晴得了A, 李磊得了C-, 嚴帥得了B

8. 學員小晴 在 python 16 的 day6裏 提交了做業 

9. 學員李磊 查看了本身所報的全部課程 

10 學員 李磊  在 查看了 本身在 py16期 的 成績列表 ,而後自殺了

11. 學員小晴  跟 講師 Alex 表白了

 

 

名詞列表:

管理員、校區、課程、班級、上課紀錄、做業、成績、講師、學員

 
 

加屬性

 

 

 

連關係 

有了類,也有了屬性,接下來天然就是找出它們的關係了。

 


 

 
 
 

本節做業: 選課系統

角色:學校、學員、課程、講師
要求:
1. 建立北京、上海 2 所學校
2. 建立linux , python , go 3個課程 , linux\py 在北京開, go 在上海開
3. 課程包含,週期,價格,經過學校建立課程
4. 經過學校建立班級, 班級關聯課程、講師
5. 建立學員時,選擇學校,關聯班級
5. 建立講師角色時要關聯學校,
6. 提供兩個角色接口
6.1 學員視圖, 能夠註冊, 交學費, 選擇班級,
6.2 講師視圖, 講師可管理本身的班級, 上課時選擇班級, 查看班級學員列表 , 修改所管理的學員的成績
6.3 管理視圖,建立講師, 建立班級,建立課程

7. 上面的操做產生的數據都經過pickle序列化保存到文件裏

相關文章
相關標籤/搜索