大話JS面向對象之開篇萬物皆對象------(ATM取款機引發的深思)


一,總體概要

OO(面向對象)概念的提出是軟件開發工程發展的一次革命,多年來我們借助它使得很多大型應用程序得以順利實現。如果您還沒有掌握並使用OO進行程序設計和開發,那么您無疑還停留在軟件開發的石器時代。大多數編程語言,尤其是近年問世的一些語言,都很好的支持了面向對象,您可能對此了如執掌,但是一些語言在OO方面卻無法與其它高級語言相比,在這些語言上進行面向對象程序設計和開發會有些困難,例如本文要討論的JavaScript。JavaScript是一門古老的語言,但是隨着近期Web2.0 技術的熱捧,這門語言又重新煥發出青春的光輝,借助於JavaScript客戶端技術,我們的Web體驗變得豐富而又精彩,為了設計和開發更加完善、復雜的客戶端應用,我們必須掌握JavaScript上的OO方法,這正是本文要討論的。

當今 JavaScript 大行其道,各種應用對其依賴日深。web 程序員已逐漸習慣使用各種優秀的 JavaScript 框架快速開發 Web 應用,從而忽略了對原生 JavaScript 的學習和深入理解。所以,經常出現的情況是,很多做了多年 JS 開發的程序員對閉包、函數式編程、原型總是說不清道不明,即使使用了框架,其代碼組織也非常糟糕。這都是對原生 JavaScript 語言特性理解不夠的表現。要掌握好 JavaScript,首先一點是必須摒棄一些其他高級語言如 Java、C# 等類式面向對象思維的干擾,全面地從函數式語言的角度理解 JavaScript 原型式面向對象的特點。把握好這一點之后,才有可能進一步使用好這門語言。本文適合群體:使用過 JS 框架但對 JS 語言本質缺乏理解的程序員,具有 Java、C++ 等語言開發經驗,准備學習並使用 JavaScript 的程序員,以及一直對 JavaScript 是否面向對象模棱兩可,但希望知道真相的 JS 愛好者。

言歸正傳,我們切入主題------Javascript的面向對象編程。要談Javascript的面向對象編程,我們第一步要做的事情就是忘記我們所學的面向對象編程。傳統C++或Java的面向對象思維來學習Javascript的面向對象會給你帶來不少困惑,讓我們先忘記我們所學的,從新開始學習這門特殊的面向對象編程。既然是OO編程,要如何來理解OO編程呢,記得以前學Java,學了很久都不入門,后來有幸讀了《Java編程思想》這本大作,頓時豁然開朗,因此本文也將以對象模型的方式來探討的Javascript的OO編程。因為Javascript 對象模型的特殊性,所以使得Javascript的繼承和傳統的繼承非常不一樣,同時也因為Javascript里面沒有類,這意味着Javascript里面沒有extends,implements。那么Javascript到底是如何來實現OO編程的呢?好吧,讓我們開始吧,一起在Javascript的OO世界里來一次漫游。

 

二,案例引入

(1)場景人物簡介

主人公甲------大耳文老師 (軟件行業領域的骨灰級程序猿)

主人公乙------大熊君 (初出茅廬的菜鳥程序員)

關系------ 工作上的上下級,私底下的師徒

(2)話題開展

有一天這師徒二人午飯后閑聊起來,突然聊起ATM取款機,大耳文老師說取款機對人們的日常生活幫助真是很大,同時也提高了銀行的生產力,這項發明和我們的軟件行業也是分不開的,大熊君說是啊老師,一些實用的操作提供了便捷的功能,這時大耳文老師微微一笑說道,那么你知道這些功能是如何實現的嗎,滿滿自信的大熊君回答說:簡單啊就是一些像存錢,取錢,轉賬的簡單功能,老師我現在就寫給你看。。。。未完待續

(3)實例講解,循序漸進

十分鍾過后大熊君把一份轉賬操作部分的代碼給老師看,老師笑了(呵呵)

 1 function TransferTransaction(fromAccount,toAccount,balance){
 2     this.fromAccount = fromAccount ;
 3     this.toAccount = toAccount ;
 4     this.balance = balance ;
 5 } ;
 6 TransferTransaction.prototype = {
 7     transfer : function(){
 8         this.toAccount = this.fromAccount - balance ;
 9     } ,
10     getFromAccount : function(){
11         return this.fromAccount ;
12     } ,
13     getToAccount : function(){
14         return this.toAccount ;
15     } ,
16     getBalance : function(){
17         return this.balance ;
18     }
19 } ;
20 
21 var tt = new TransferTransaction(1000,3000,100) ;
22 tt.transfer() ;
23 tt.getToAccount() ;
View Code

老師說道大熊你這代碼是有問題的,大熊沒有想太多直接回答說,沒有啊老師我這是面向對象寫的啊,老師很有耐心的說道:先別着急聽我給你講。

首先先不說面向對象就功能而言也是有問題的,假設我的轉出賬戶就是fromAccount要是余額為0那,不就會出現問題了嗎,大熊,大熊看了一下很難為情地說奧 是啊  您稍等一下 大熊回去又作修改。。。。未完待續

改完了這下沒問題了吧

 1 function TransferTransaction(fromAccount,toAccount,balance){
 2     this.fromAccount = fromAccount ;
 3     this.toAccount = toAccount ;
 4     this.balance = balance ;
 5 } ;
 6 TransferTransaction.prototype = {
 7     transfer : function(){
 8         if(this.fromAccount < balance){
 9             throw new Error("余額不足!") ;
10         }
11         this.toAccount = this.fromAccount - balance ;
12     } ,
13     getFromAccount : function(){
14         return this.fromAccount ;
15     } ,
16     getToAccount : function(){
17         return this.toAccount ;
18     } ,
19     getBalance : function(){
20         return this.balance ;
21     }
22 } ;
23 
24 var tt = new TransferTransaction(1000,3000,100) ;
25 tt.transfer() ;
26 tt.getToAccount() ;
View Code

 老師看了看說道基本功能是做到,但這樣設計是很低效的,並且很多設計原則 也違反了。

大熊疑惑的問到為什么那,老師說如果現在新的需求來了比如根據賬戶的等級划分會有不同級別的轉賬金額你如何處理那?比如vip級別最多可以轉賬5000,Normal級別至多為1000你試試看,好的老師,您稍等。。。。未完待續

 1 function TransferTransaction(fromAccount,toAccount,balance,rank){
 2     this.fromAccount = fromAccount ;
 3     this.toAccount = toAccount ;
 4     this.balance = balance ;
 5     this.rank = rank ;
 6 } ;
 7 TransferTransaction.prototype = {
 8     transfer : function(){
 9         var transBalance = 1000 ;
10         if("vpi" == this.rank){
11             transBalance = 5000 ;
12         }
13         if(this.balance > transBalance){
14             throw new Error("您的轉賬金額超出了規定范圍!") ;
15         }
16         if(this.fromAccount < this.balance){
17             throw new Error("余額不足!") ;
18         }
19         this.fromAccount = this.fromAccount - this.balance ;
20         this.toAccount = this.toAccount + this.balance ;
21     } ,
22     getFromAccount : function(){
23         return this.fromAccount ;
24     } ,
25     getToAccount : function(){
26         return this.toAccount ;
27     } ,
28     getBalance : function(){
29         return this.balance ;
30     }
31 } ;
32 
33 var tt = new TransferTransaction(1000,3000,20000,"vip") ;
34 tt.transfer() ;
35 tt.getToAccount() ;
View Code

哈哈哈老師我完成了,很快就寫出來了(*^__^*) 嘻嘻……

大耳文老師看了下代碼說道,恩改的不錯,但依然還是有問題存在的,咱們回頭說面向對象,你的代碼書寫方式確實符合了面向對象的特點,但是思想還停留在過程化的設計思想上。

這時大熊君過來很謙虛的說道請老師指點 。。。 未完待續

幾分鍾過后老師總結出一些例子中的問題

1.職責多需要分解

2.抽象實體模型

3.依賴性強可擴展低

咱們來一步步分析和重構

首先你的對象完成了很多職責比如 級別過濾 金額比對 異常處理  這些都不屬於轉賬的核心功能  應該划分出來

還有就是 在轉賬操作時 Account應該是一個實體數據模型  應該獨立出來 不應該在具體業務方法中出現 這也違背了(迪米特法則)一種軟件設計法則,稍后會提供擴展閱讀鏈接(*^__^*)

重構分為幾個步驟去設計:

1,建立Account類

 1 function Account(balance,rank){
 2     this.balance = balance ;
 3     this.rank = rank ;
 4 } ;
 5 Account.prototype = {
 6     getBalance : function(){
 7         return this.balance ;
 8     } 
 9     getRank : function(){
10         return this.balance ;
11     }
12 } ;
View Code

2,重新設計TransferTransaction類

 

 1 function TransferTransaction(fromAccount,toAccoun){
 2     this.fromAccount = fromAccount ;
 3     this.toAccount = toAccount ;
 4 } ;
 5 TransferTransaction.prototype = {
 6     transfer : function(balance){
 7         var transBalance = 1000 ;
 8         if("vpi" == this.this.fromAccount.getRank()){
 9             transBalance = 5000 ;
10         }
11         if(this.fromAccount.getBalance() > transBalance){
12             throw new Error("您的轉賬金額超出了規定范圍!") ;
13         }
14         if(this.fromAccount.getBalance() < balance){
15             throw new Error("余額不足!") ;
16         }
17         this.fromAccount.setBalance(this.fromAccount.getBalance() - this.balance) ;
18         this.toAccount.setBalance(this.toAccount.getBalance() + this.balance) ;
19     } ,
20     getFromAccount : function(){
21         return this.fromAccount ;
22     } ,
23     getToAccount : function(){
24         return this.toAccount ;
25     }
26 } ;
View Code

3,增加新的轉賬管理類TransTManager類(主要負責周邊功能任務)

 1 function TransTManager(fromAccount,toAccoun){
 2     this.fromAccount = fromAccount ;
 3     this.toAccount = toAccount ;
 4     this.transferTransaction = null ;
 5 } ;
 6 TransTManager.prototype = {
 7     transfer : function(balance){
 8         var transBalance = 1000 ;
 9         if("vpi" == this.this.fromAccount.getRank()){
10             transBalance = 5000 ;
11         }
12         if(this.fromAccount.getBalance() > transBalance){
13             throw new Error("您的轉賬金額超出了規定范圍!") ;
14         }
15         if(this.fromAccount.getBalance() < balance){
16             throw new Error("余額不足!") ;
17         }
18         this.transferTransaction = new TransferTransaction(this.fromAccount,this.toAccount) ;
19         this.transferTransaction.transfer(balance) ;
20     }
21 } ;
View Code

4,接下來設計會員的級別類每個等級作為一個類出現(引用策略模式)

 1 function NormalDiscount(){
 2 
 3 } ;
 4 NormalDiscount.prototype = {
 5     getDiscount : function(){
 6         return 3000 ;
 7     }
 8 } ;
 9 function VIPDiscount(){
10 
11 } ;
12 VIPDiscount.prototype = {
13     getDiscount : function(){
14         return 5000 ;
15     }
16 } ;
View Code

 5,經過重構后思維會清晰很多而且更加面向對象了,其實思維往往比語言本身重要得多

 以下是完整的案例代碼

 

 1 function Account(balance,rank){
 2     this.balance = balance ;
 3     this.rank = rank ;
 4 } ;
 5 Account.prototype = {
 6     getBalance : function(){
 7         return this.balance ;
 8     } 
 9     getRank : function(){
10         return this.balance ;
11     }
12 } ;
13 
14 function TransferTransaction(fromAccount,toAccoun){
15     this.fromAccount = fromAccount ;
16     this.toAccount = toAccount ;
17 } ;
18 TransferTransaction.prototype = {
19     transfer : function(balance){    
20         this.fromAccount.setBalance(this.fromAccount.getBalance() - balance) ;
21         this.toAccount.setBalance(this.toAccount.getBalance() + balance) ;
22     } ,
23     getFromAccount : function(){
24         return this.fromAccount ;
25     } ,
26     getToAccount : function(){
27         return this.toAccount ;
28     }
29 } ;
30 
31 function TransTManager(fromAccount,toAccoun){
32     this.fromAccount = fromAccount ;
33     this.toAccount = toAccount ;
34     this.transferTransaction = null ;
35     this.rankVendor = {
36         "normal" : {
37             get : function(){
38                 return 1000 ;
39             }
40         } ,
41         "vip" : {
42             get : function(){
43                 return 5000 ;
44             }
45         }
46     } ;
47 } ;
48 TransTManager.prototype = {
49     transfer : function(balance){
50         var transBalance = this.rankVendor[this.fromAccount.getRank()]["get"]() ;
51         if(this.fromAccount.getBalance() > transBalance){
52             throw new Error("您的轉賬金額超出了規定范圍!") ;
53         }
54         if(this.fromAccount.getBalance() < balance){
55             throw new Error("余額不足!") ;
56         }
57         this.transferTransaction = new TransferTransaction(this.fromAccount,this.toAccount) ;
58         this.transferTransaction.transfer(balance) ;
59     }
60 } ;
61 
62 var tt = new TransTManager(new Account(5000,"vip"),new Account(3000,"Normal")) ;
63 tt.transfer(3000) ;

 

 

 

三,名詞解釋

(1)迪米特法則:迪米特法則(Law of Demeter)又叫作最少知識原則(Least Knowledge Principle 簡寫LKP),就是說一個對象應當對其他對象有盡可能少的了解,不和陌生人說話。英文簡寫為: LoD.

(2)實體模型:實體類一般對應物理意義上的實體,業務類則是對一些業務規則(算法)的封裝,比如User,是一個實體類,它有屬性,Login這里就算一個業務

 

四,總結一下

 

1.類的職責不能多,職責多需要分解

 

2.抽象實體模型

 

3.理解oo的特質封裝,繼承,多態

 

這時傳來了大耳文老師的聲音:好了中午休息結束了我們要工作了。。。。。。(此篇章已結束,未完待續(*^__^*) 嘻嘻……)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM