日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
面向?qū)ο笤O(shè)計討論:有狀態(tài)類還是無狀態(tài)類?這是個難題

相信大家都清楚何謂面向?qū)ο缶幊?。不過有時候我們還需要花點時間決定為特定類賦予怎樣的屬性。很明顯,如果類屬性分配有誤,那么我們很可能遇到嚴重的后續(xù)問題。在這里我們將共同探討哪些類應(yīng)該具備狀態(tài),而哪些類應(yīng)為無狀態(tài)。

創(chuàng)新互聯(lián)主要從事網(wǎng)站設(shè)計制作、成都做網(wǎng)站、網(wǎng)頁設(shè)計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)射洪,10年網(wǎng)站建設(shè)經(jīng)驗,價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):13518219792

對象的狀態(tài)意味著什么

在我們討論有狀態(tài)類與無狀態(tài)類之前,首先應(yīng)該對對象的狀態(tài)擁有深入理解。正如字典中所言,狀態(tài)是指“某人或某物在特定時間點下所處之特定狀況?!?/p>

當我們著眼于編程并考量對象在特定時間點下的狀態(tài)時,相關(guān)范疇就縮小到了給定時間中對象的屬性或者成員變量值。那么對象的屬性由誰決定?答案是類。誰又來決定類中的屬性與成員?答案是編寫該類的程序員。誰又是程序員?就是各位正在閱讀本篇文章的朋友們。那么我們是否真的精于決斷每個類各自需要怎樣的屬性?

答案恐怕是否定的。至少我見過的不少印度程序員就僅僅為了薪酬而加入編程行業(yè),他們明顯缺少做出正確屬性選擇的能力。首先,這類知識沒辦法從學校里直接學到。具體來講,我們需要投入大量時間來積累經(jīng)驗,并借此摸索出正確選擇——這更像是一種藝術(shù)而非技術(shù)。工程技術(shù)往往擁有嚴格的規(guī)則,但藝術(shù)卻沒有。即使是經(jīng)歷了十五年的編程從業(yè)時光,我在考慮某個類需要怎樣的屬性甚至如何為該類選擇名稱時,仍然需要費一番心思。

那么我們能否通過規(guī)則限定屬性的具體需求?換言之,對象狀態(tài)當中應(yīng)當包含哪些屬性?或者說,對象是否應(yīng)當永遠優(yōu)先選擇無狀態(tài)?下面一起來看。

實體類/業(yè)務(wù)對象

編程領(lǐng)域充斥著大量諸如實體類乃至業(yè)務(wù)對象等的名稱,旨在體現(xiàn)類的某種明確狀態(tài)。如果我們選擇Employee類作為示例,那么其作用就是包含某位員工的狀態(tài)。那么具體狀態(tài)內(nèi)容是什么?EmpID、Company、Designation、JoinedDate等等……正如教材上所言,這種類應(yīng)當為有狀態(tài),毫無疑問。

但我們應(yīng)該如何進行薪酬計算?

我們是否該在Employee類中添加CalculateSalary() 方法?

是否應(yīng)該使用SalaryCalculator 類,該類又是否應(yīng)當包含Calculate()方法?

如果存在SalaryCalculator類:

  • 其是否應(yīng)該包含諸如BasicPay、DA HRA等屬性?
  • 或者Employee對象是否應(yīng)當作為私有成員變量通過構(gòu)造方法注入至SalaryCalculator?
  • 或者SalaryCalculator是否應(yīng)當顯示Employee公共屬性(Java中的 Get&Set Employee 方法)?

輔助/操作/修改類

這些類負責執(zhí)行特定任務(wù)。SalaryCalculator就屬于其中之一。這些類擁有多種命名方式,用于體現(xiàn)其行為并通過前綴或者后綴進行表達,例如:

  • SomethingCalculator 類,例如: SalaryCalculator
  • SomethingHelper 類,例如: DBHelper
  • SomethingController類,例如: DBController
  • SomethingManager類
  • SomethingExecutor類
  • SomethingProvider類
  • SomethingWorker類
  • SomethingBuilder類
  • SomethingAdapter類
  • SomethingGenerator類

人們可以通過不同前綴或后續(xù)表達類狀態(tài),在這里我們就不過多討論了。

我們能否向這些類中添加一項狀態(tài)? 我建議大家以無狀態(tài)方式處理這些類。下面來看具體理由。

混合類

根據(jù)維基百科給出的面向?qū)ο缶幊虄?nèi)的封裝定義,其概念為“……將數(shù)據(jù)與函數(shù)打包成單一組件。”這是否意味著全部用于操作該對象的方法都應(yīng)該被打包進實體類當中?我認為不是。實體類應(yīng)當使用有狀態(tài)訪問方法,例如GetName()、SetName()、GetJoiningDate以及GetSalary() 等等。不過 CalculateSalary()應(yīng)被排除在外。為什么?

根據(jù)單一責任原則:“一個類應(yīng)當只出于單一理由進行變更?!比绻覀儗?CalculateSalary()方法添加到Employee類當中,那么該類則由于以下兩種理由而發(fā)生變更:

Employee類狀態(tài)變更:當新屬性被添加到Employee當中時。

計算邏輯中出現(xiàn)變更。

下面讓我們再明確地整理一遍。假設(shè)我們擁有2個類。Employee類與SalaryCalculator類。那么二者該如何彼此對接?實現(xiàn)方式多種多樣。其一為在GetSalary方法中創(chuàng)建一個SalaryCalculator類對象,并調(diào)用Calculate()以設(shè)置Employee類的薪酬變量。在這種情況下,該類將同樣表現(xiàn)為實體類與輔助類的特性,我們將其稱為混合類。我個人不建議大家使用這種混合類。

基本原則:“一旦大家發(fā)現(xiàn)自己的類可能已經(jīng)轉(zhuǎn)化為混合類,請考慮對其進行重構(gòu)。如果大家發(fā)現(xiàn)自己的類不屬于以上任何一種類別,請馬上停止后續(xù)編程工作。”

輔助/操作類中的狀態(tài)

有狀態(tài)的輔助類會帶來哪些問題?在給出答案之前,讓我們首先通過以下示例了解SalaryCalculator類能夠包含的不同狀態(tài)值組合:

場景一——基本值

 
 
  1. class SalaryCalculator 
  2.  
  3.  { 
  4.  
  5.      public double Basic { get; set; } 
  6.  
  7.      public double DA { get; set; } 
  8.  
  9.      public string Designation { get; set; } 
  10.  
  11.      public double Calculate() 
  12.  
  13.      { 
  14.  
  15.          //Calculate and return 
  16.  
  17.      } 
  18.  
  19.  } 

缺點

這時Basic薪酬有可能為“Accountant”則Designation可能為“Director”,二者完全不能匹配。在這種情況下,我們無法通過任何強制性方式確保SalaryCalculator獨立運作。

同樣的,如果其執(zhí)行于線程環(huán)境下,亦會導致運行失敗。

場景二——對象即狀態(tài)

 
 
  1. class SalaryCalculator 
  2.  
  3.  
  4.     public Employee Employee { get; set; } 
  5.  
  6.     public double Calculate() 
  7.  
  8.     { 
  9.  
  10.         //Calculate and return 
  11.  
  12.     } 
  13.  

缺點

如果兩個線程共享SalaryCalculator對象,而每個線程對應(yīng)不同的員工,那么整個執(zhí)行順序有可能導致以下邏輯錯誤:

  • 線程1設(shè)置employee1對象
  • 線程2設(shè)置employee2對象
  • 線程1調(diào)用Calculate 方法并為employee2獲取Salary

可以看到其中Employee關(guān)聯(lián)性可通過構(gòu)造方法進行注入,并使得該屬性為只讀。接下來我們需要為每個Employee對象創(chuàng)建SalaryCalculator 對象。因此,***不要通過這種方式設(shè)計輔助類。

場景三——無狀態(tài)

 
 
  1. class SalaryCalculator 
  2.  
  3.  
  4.     public double Calculate(Employee input) 
  5.  
  6.     { 
  7.  
  8.         //Calculate and return 
  9.  
  10.     } 
  11.  

這是一種近乎***的情況。不過需要考慮的是,如何全部方法都不使用任何成員變量,那么我們該如何保證其屬于無狀態(tài)類。

正如SOLID第二原則所言:“開放擴展,封閉修改?!笔裁匆馑??具體來講,當我們編寫一個類時,必須保證其徹底完成,即不要再對其進行后續(xù)修改。但與此同時,其也應(yīng)具備通過子類與覆蓋實現(xiàn)擴展的能力。那么,我們的類最終應(yīng)該如下所示:

 
 
  1. interface ISalaryCalculator 
  2.  
  3.  
  4.     double Calculate(Employee input); 
  5.  
  6.  
  7. class SimpleSalaryCalculator:ISalaryCalculator 
  8.  
  9.  
  10.     public virtual double Calculate(Employee input) 
  11.  
  12.     { 
  13.  
  14.         return input.Basic + input.HRA; 
  15.  
  16.     } 
  17.  
  18.  
  19. class TaxAwareSalaryCalculator : SimpleSalaryCalculator 
  20.  
  21.  
  22.     public override double Calculate(Employee input) 
  23.  
  24.     { 
  25.  
  26.         return base.Calculate(input)-GetTax(input); 
  27.  
  28.     } 
  29.  
  30.     private double GetTax(Employee input) 
  31.  
  32.     { 
  33.  
  34.         //Return tax 
  35.  
  36.         throw new NotImplementedException(); 
  37.  
  38.     } 
  39.  

正如我之前所反復強調(diào),編程應(yīng)該面向接口進行。在以上代碼片段當中,我出于篇幅的考慮而略去了接口實現(xiàn)方法。另外,計算邏輯應(yīng)當始終處于受保護函數(shù)之內(nèi),從而保證繼承類能夠在必要時對其進行調(diào)用。

以下為Calculator類的正確消費方式:

 
 
  1. class SalaryCalculatorFactory 
  2.  
  3.  
  4.     internal static ISalaryCalculator GetCalculator() 
  5.  
  6.     { 
  7.  
  8.         // Dynamic logic to create the ISalaryCalculator object 
  9.  
  10.         return new SimpleSalaryCalculator(); 
  11.  
  12.     } 
  13.  
  14.  
  15. class PaySlipGenerator 
  16.  
  17.  
  18.     void Generate() 
  19.  
  20.     { 
  21.  
  22.         Employee emp = new Employee() { }; 
  23.  
  24.         double salary =SalaryCalculatorFactory.GetCalculator().Calculate(emp); 
  25.  
  26.     } 
  27.  

其中Factory類負責封裝決定使用哪個子類的邏輯。其既可如上所述選擇有狀態(tài),亦可選擇動態(tài)反映機制。對該類進行變更的惟一理由就是創(chuàng)建對象,因此我們并沒有違背“單一責任原則”。

在使用混合類的情況下,大家可能從Employee.Salary 屬性或者Employee.GetSalary() 處調(diào)用計算邏輯,如下所示:

 
 
  1. class Employee 
  2.  
  3.  
  4.     public string Name { get; set; } 
  5.  
  6.     public int EmpId { get; set; } 
  7.  
  8.     public double Basic { get; set; } 
  9.  
  10.     public double HRA { get; set; } 
  11.  
  12.     public double Salary 
  13.  
  14.     { 
  15.  
  16.         //NOT RECOMMENDED  
  17.  
  18.         get{return SalaryCalculatorFactory.GetCalculator().Calculate(this);} 
  19.  
  20.     } 
  21.  

總結(jié)

“思考時不編程,編程時不思考?!边@項原則讓為我們帶來充足的考量空間,從而正確把握類的有狀態(tài)與無狀態(tài)決定——以及在有狀態(tài)時讓其顯示哪種狀態(tài)。

實體類應(yīng)該有狀態(tài)。

輔助/操作類應(yīng)當無狀態(tài)。

確保輔助類無狀態(tài)。

如果存在混合類,確保其不會違背單一責任原則。

在編程之前花點時間進行類設(shè)計。把類設(shè)計成果交給其他同事審查,并考量其反饋意見。

認真選擇類名稱。這些名稱將幫助我們決定其狀態(tài)。命名工作并沒有固定限制,以下是我個人的一些建議:

  • 實體類應(yīng)當在名稱中體現(xiàn)對象類型,例如: Employee
  • 輔助/工作類名稱應(yīng)當反映出其作用。例如: SalaryCalculator、PaySlipGenerator等
  • 永遠不要在類名稱中使用動詞,例如: CalculateSalary{}類

分享文章:面向?qū)ο笤O(shè)計討論:有狀態(tài)類還是無狀態(tài)類?這是個難題
文章來源:http://www.5511xx.com/article/dhiccgp.html