自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面向?qū)ο笤O(shè)計(jì)討論:有狀態(tài)類還是無(wú)狀態(tài)類?這是個(gè)難題

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

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

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

在我們討論有狀態(tài)類與無(wú)狀態(tài)類之前,首先應(yīng)該對(duì)對(duì)象的狀態(tài)擁有深入理解。正如字典中所言,狀態(tài)是指“某人或某物在特定時(shí)間點(diǎn)下所處之特定狀況。”

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

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

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

實(shí)體類/業(yè)務(wù)對(duì)象

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

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

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

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

如果存在SalaryCalculator類:

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

輔助/操作/修改類

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

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

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

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

混合類

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

根據(jù)單一責(zé)任原則:“一個(gè)類應(yīng)當(dāng)只出于單一理由進(jìn)行變更。”如果我們將 CalculateSalary()方法添加到Employee類當(dāng)中,那么該類則由于以下兩種理由而發(fā)生變更:

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

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

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

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

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

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

場(chǎng)景一——基本值

  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.  } 

 

缺點(diǎn)

這時(shí)Basic薪酬有可能為“Accountant”則Designation可能為“Director”,二者完全不能匹配。在這種情況下,我們無(wú)法通過(guò)任何強(qiáng)制性方式確保SalaryCalculator獨(dú)立運(yùn)作。

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

場(chǎng)景二——對(duì)象即狀態(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.  

 

缺點(diǎn)

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

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

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

場(chǎng)景三——無(wú)狀態(tài)

 

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

 

這是一種近乎***的情況。不過(guò)需要考慮的是,如何全部方法都不使用任何成員變量,那么我們?cè)撊绾伪WC其屬于無(wú)狀態(tài)類。

正如SOLID第二原則所言:“開(kāi)放擴(kuò)展,封閉修改。”什么意思?具體來(lái)講,當(dāng)我們編寫(xiě)一個(gè)類時(shí),必須保證其徹底完成,即不要再對(duì)其進(jìn)行后續(xù)修改。但與此同時(shí),其也應(yīng)具備通過(guò)子類與覆蓋實(shí)現(xiàn)擴(kuò)展的能力。那么,我們的類最終應(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.  

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

以下為Calculator類的正確消費(fèi)方式:

  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類負(fù)責(zé)封裝決定使用哪個(gè)子類的邏輯。其既可如上所述選擇有狀態(tài),亦可選擇動(dòng)態(tài)反映機(jī)制。對(duì)該類進(jìn)行變更的惟一理由就是創(chuàng)建對(duì)象,因此我們并沒(méi)有違背“單一責(zé)任原則”。

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

 

  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é)

“思考時(shí)不編程,編程時(shí)不思考。”這項(xiàng)原則讓為我們帶來(lái)充足的考量空間,從而正確把握類的有狀態(tài)與無(wú)狀態(tài)決定——以及在有狀態(tài)時(shí)讓其顯示哪種狀態(tài)。

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

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

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

如果存在混合類,確保其不會(huì)違背單一責(zé)任原則。

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

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

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

原文標(biāo)題:Object-Oriented Design Decisions: Stateful or Stateless Classes

【51CTO譯稿,合作站點(diǎn)轉(zhuǎn)載請(qǐng)注明原文譯者和出處為51CTO.com】

責(zé)任編輯:王雪燕 來(lái)源: 51CTO
相關(guān)推薦

2024-11-18 16:28:20

2024-04-30 11:14:19

KubernetesReplicaSet數(shù)量

2013-12-09 09:56:30

NAT64IPv6stateful

2024-05-30 11:53:51

2021-07-02 14:14:14

Python對(duì)象設(shè)計(jì)

2021-07-16 10:23:47

Python設(shè)計(jì)對(duì)象

2021-03-09 20:52:01

架構(gòu)無(wú)狀態(tài)服務(wù)

2022-07-20 07:23:40

Kubernetes容器

2009-01-16 08:52:26

面向?qū)ο?/a>OOP編程

2018-03-30 16:03:04

軟件無(wú)狀態(tài)”

2020-06-30 08:41:38

HTTP無(wú)狀態(tài)協(xié)議

2020-03-27 10:50:29

DSL 狀態(tài)機(jī)工具

2012-03-14 10:48:05

C#

2009-11-06 14:42:24

Visual Stud

2010-02-02 13:15:26

Python類

2023-09-27 23:28:28

Python編程

2020-08-11 10:20:26

http數(shù)據(jù)庫(kù)狀態(tài)

2023-12-01 07:03:16

2019-10-09 08:29:30

IPv6IP地址狀態(tài)

2016-10-11 15:42:08

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)