日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

你在用什么思想编码:事务脚本 OR 面向对象?

發布時間:2025/4/16 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 你在用什么思想编码:事务脚本 OR 面向对象? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近在公司內部做技術交流的時候,說起技能提升的問題,調研大家想要培訓什么,結果大出我意料,很多人想要培訓:面向對象編碼。于是我拋出一個問題:你覺得我們現在的代碼是面向對象的嗎?有人回答:是,有人回答否。我對這個問題的回答是:語法上,是了,但是架構上或者思想上,不是。我們現在的大部分代碼,如果要死扣一個名詞的話,那就是:事務腳本。

?

1:最開始的事務腳本

在 Martin Fowler 的書中,存在一個典型的 應用場景,即“收入確認”(Revenue Recognition)。該“收入確認”的描述:

一家軟件公司有3種產品,其售價策略分別為,第一種:交全款才能賣給你;第二種,付三分之一,就給你,60天后,再給1/3,90天后給完全部;第三種,付1/3,就給你,30天后給1/3,60天后給完。

但是,關于這個描述,我打算多啰嗦幾句,而且個人覺的這個啰嗦非常之緊要,因為它影響到了我們的設計。以下是啰嗦的部分:

“收入確認”,在概念上,確實是產品的入賬策略,實際上,Martin 的代碼,也是這么去實現的,不同的產品有不同的入賬策略。不過,數據庫實現,RevenueRecognition 這個表記錄的是“產品的某個合同根據產品類型所計算出來的:應該執行的入賬日及金額”,即策略是跟著合同走的,而不是跟著產品走的。這很有意思,如果你精讀此部分,這種矛盾就會一直糾結在你心頭。同時,我們又不得不時刻提醒自己存在的這個需求。

現在,關于這個場景,如果我們理解了 產品 合同 RevenueRecognition 之間的關系,我們就很能理解了數據庫是被設計成這樣的:

其概念模型為如下:

好了,現在我們來看看什么是事務腳本,對的,就用代碼來說話。在原文中, Martin 舉了兩個例子,但是精讀之后,我打算將其顛個倒,把原文中的示例2講在前頭。因為示例2,很好的表達了什么才是作者或者譯者眼中的“收入確認”,以及我眼中的“收入策略”。

第一個要實現的功能,即第一個事務腳本描述如下:

根據合同 ID,找到該合同,并根據合同類型得到應該在哪天收入多少錢,并插入數據庫。

從該描述中,我們知道,這個腳本最應該發生在簽訂合同時。因為合同一旦簽訂,就應該記錄什么時候應該收到客戶端多少錢。代碼如下:

class RecognitionService
{
??? dynamic dal = null;
???
??? // 計算哪天該入賬多少并插入
??? public void CalculateRevenueRecognitions(long contactNumber)
??? {
??????? DataSet contractDs = dal.FindContract(contactNumber);
??????? double totalRevenue = (double)contractDs.Tables[0].Rows[0]["ID"];
??????? DateTime dateSigned = (DateTime)contractDs.Tables[0].Rows[0]["DateSigned"];
??????? string type = (string)contractDs.Tables[0].Rows[0]["Type"];
??????? if(type == "S")??? // 電子表格類
??????? {
??????????? // the sql "INSERT INTO REVENUECONGNITIONS (CONTRACT,AMOUNT,RECOGNIZEDON) VALUES (?,?,?)"
??????????? dal.InsertRecognition(contactNumber, totalRevenue / 3, dateSigned);
??????????? dal.InsertRecognition(contactNumber, totalRevenue / 3, dateSigned.AddDays(60));
??????????? dal.InsertRecognition(contactNumber, totalRevenue / 3, dateSigned.AddDays(90));
??????? }else if(type == "W")??? // 文字處理
??????? {???
??????????? dal.InsertRecognition(contactNumber, totalRevenue, dateSigned);
??????? }else if(type == "D")??? // 數據庫
??????? {???
??????????? dal.InsertRecognition(contactNumber, totalRevenue / 3, dateSigned);
??????????? dal.InsertRecognition(contactNumber, totalRevenue / 3, dateSigned.AddDays(30));
??????????? dal.InsertRecognition(contactNumber, totalRevenue / 3, dateSigned.AddDays(60));
??????? }
??? }???
}

?

第二個需求是:計算某合同在某個日期前的應該有的入賬。

class RecognitionService
{
??? dynamic dal = null;
????????
??? // 得到哪天前入賬了多少
??? public double RecognizedRevenue(long contractNumber, DateTime asOf)
??? {
??????? // the sql "SELECT AMOUNT FROM REVENUECONGNITIONS WHERE CONTRACT=? AND RECOGNIZEDON <=?";
??????? DataSet ds = dal.FindRecognitionsFor(contractNumber, asOf);
??????? double r = 0.0;
??????? foreach(DataRow dr in ds.Tables[0].Rows)
??????? {
??????????? r += (double)dr["AMOUNT"];
??????? }
???????
??????? return r;
??? }
}

從上面的代碼,我們可以看出什么才是 事務腳本:

1:采用面向過程的方式組織業務邏輯;
2:沒有或盡量少的實體類;
3:一個方法一件事情,故有大量業務類或方法;
4:能與行數據入口表數據入口很好協作;

?

2:事務腳本之變體

也許上面的代碼多多少少讓大家嗤之以鼻,認為現在很少會這樣來寫代碼了。那么,我們來看看下面這段代碼:

class RecognitionBll
{
??? dynamic dal = null;
???
??? // 計算哪天該入賬多少并插入
??? public void CalculateRevenueRecognitions(long contactNumber)
??? {
??????? List<Contact> contracts = dal.FindContract(contactNumber);
??????? double totalRevenue = (double)contracts[0].Id;
??????? DateTime dateSigned = (DateTime)contracts[0].DateSigned;
??????? string type = (string)dal.FindContractType(contactNumber);
??????? // 上面這行代碼你還可能會寫成
??????? // string type = (string)dal.contracts[0].ProductType;
??????? // 或者
??????? // string type = (string)dal.contracts[0].Product.Type;
??????? if(type == "S")??? // 電子表格類
??????? {
??????????? // the sql "INSERT INTO REVENUECONGNITIONS (CONTRACT,AMOUNT,RECOGNIZEDON) VALUES (?,?,?)"
??????????? dal.InsertRecognition(contactNumber, totalRevenue / 3, dateSigned);
??????????? dal.InsertRecognition(contactNumber, totalRevenue / 3, dateSigned.AddDays(60));
??????????? dal.InsertRecognition(contactNumber, totalRevenue / 3, dateSigned.AddDays(90));
??????? }else if(type == "W")??? // 文字處理
??????? {???
??????????? dal.InsertRecognition(contactNumber, totalRevenue, dateSigned);
??????? }else if(type == "D")??? // 數據庫
??????? {???
??????????? dal.InsertRecognition(contactNumber, totalRevenue / 3, dateSigned);
??????????? dal.InsertRecognition(contactNumber, totalRevenue / 3, dateSigned.AddDays(30));
??????????? dal.InsertRecognition(contactNumber, totalRevenue / 3, dateSigned.AddDays(60));
??????? }
??? }
???
??? // 得到哪天前入賬了多少
??? public double RecognizedRevenue(long contractNumber, DateTime asOf)
??? {
??????? // the sql "SELECT AMOUNT FROM REVENUECONGNITIONS WHERE CONTRACT=? AND RECOGNIZEDON <=?";
??????? List<RevenueRecognition> revenueRecognitions = dal.FindRecognitionsFor(contractNumber, asOf);
??????? double r = 0.0;
??????? foreach(RevenueRecognition rr in revenueRecognitions)
??????? {
??????????? r += rr.Amount;
??????? }
???????
??????? return r;
??? }
}

public class Product
{
??? public long Id;
??? public string Name;
??? public string Type;
}

public class Contact
{
??? public long Id;
??? public long ProductId;
??? public string ProductType;
??? public Product Product;
??? public double Revenue;
??? public DateTime DateSigned;
}

public class RevenueRecognition
{
??? public long ContactId;
??? public double Amount;
??? public double RevenuedOn;
}

在這個事務腳本的變種中,我們看到了所有人寫過代碼的影子:

1:有了實體類了,所以看上去貌似是面向對象編碼了;

2:看到了 “三層架構” 了,即:實體層、DAL層、業務邏輯層等;

但是,它仍舊是 事務腳本 的!唯一不同的是,它光鮮的把 DataSet 變成了 List<Model> 了!

?

3:什么是面向對象的?

那么,什么是面向對象的編碼,面向對象的一個很重要的點就是:“把事情交給最適合的類去做”,并且“你得在一個個業務類之間跳轉,才能找出他們如何交互”。這確實是個不那么簡單的話題,而本文的主旨也僅在于指出,如果我們的代碼中還沒有 工作單元 映射 緩存 延遲加載 等等概念,即便我們編碼再熟練,也僅僅是在熟練的 面向過程編碼。

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的你在用什么思想编码:事务脚本 OR 面向对象?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。