設計模式之 面向對象的養豬廠的故事,C#演示(一)

来源:http://www.cnblogs.com/hackpig/archive/2016/08/11/5757516.html
-Advertisement-
Play Games

對於設計模式, 從本質上說, 其最大的用途就是適應需求的變化. 因為有了設計模式,我們可以在設計階段就為未來可能發生的變化留下足夠的空間. 我們通過一個建造現代化養豬場的故事, 來討論一下設計模式與需要變化之間的關係. (一)設計模式最根本的意圖是適應需求的變化 一個機器人研發小組研製了一種能自動喂 ...


 

對於設計模式, 從本質上說, 其最大的用途就是適應需求的變化. 因為有了設計模式,我們可以在設計階段就為未來可能發生的變化留下足夠的空間.

我們通過一個建造現代化養豬場的故事, 來討論一下設計模式與需要變化之間的關係.

(一)設計模式最根本的意圖是適應需求的變化

一個機器人研發小組研製了一種能自動喂豬的機器人, 於是有人投資興建了一座由機器人管理的養豬場. 這個養豬場要飼養的豬的品種包括:

  • 大白豬: 又叫"大約克郡豬", 原產於英國. 全身白色,耳向前挺立,體長大,成年公豬體重三百到五百公斤,繁殖力強, 是全世界分佈最廣的豬型.
  • 長白豬: 是"蘭德瑞斯豬" 在中國的統稱. 著名的腌肉型豬, 原產於丹麥. 全身白色, 體軀 特長, 耳大前垂, 背腰平直, 大腿豐滿. 皮薄瘦肉多.成年公豬體重四百到五百公斤, 要求較好的飼養管理條件.
  • 波中豬: 是豬的著名品種, 原產於美國, 屬脂肪型, 已培育成肉用型. 全身黑色.成年公豬重三百九十到四百五十公斤, 早熟易肥, 但繁殖力較弱.

下麵我們來討論機器人小組的設計方案: 

 

 

                  圖v1-1

 

用UML繪製的養豬場v1.0軟體動作機制如圖V1-1所示。最初的設計因為豬場只引進了大白豬品種,所以沒有考慮飼養其它種類豬的情況。

一個喂豬機器人管理了若幹頭大白豬。由於豬天性懶惰,機器人必須把飼料放到豬嘴邊,然後吆喝一句,“大白豬,吃!”,豬才會進食。

在v1.0版的程式動作下,養豬場運行狀況相當不錯,每頭豬都養得膘肥休壯,如果不是企業要不斷的追求剩餘價值,我們的故事也許就到此結束了。

隨著養豬場的蓬勃發展,養豬場老闆決定進軍國際市場。可是,不同地方的人喜歡吃不同品種的豬。為了適應這一需求的變化,養豬場新引進了一批長白豬。

問題出現了:喂豬機器人照例把飼料準備好,拿到每一頭豬面前,大叫一聲:”大白豬,吃!“,大白豬能像往常一樣愉快地進食。但輪到長白豬時,長白豬無動於衷。這下急壞了養豬場的老闆。為瞭解決問題,喂豬機器人的研發團隊緊急出動,當晚便改好了程式。
V2.0 程式如圖v2-1所示:

 

 

                      圖v2-1

 

經過此次修改,喂豬機器人在對待每一頭豬時,會先辯認出這頭豬的類型,如果這是一頭大白豬,它就會大叫一聲”大白豬,吃!“,如果是一頭長白豬,就叫”長白豬,吃“,經過這一個性,養豬場又恢復了平靜。

可剛過了幾天,類似的問題又再次出現,養豬場引進了幾頭波中豬!這下,機器人又不知道怎麼喂了。研發團隊又準備大動干戈、修改代碼了。老闆卻對研發團隊的做法表示不理解:“你們太不像話了,我是付了錢的,可每次我要擴大再生產的時候,你們都要耽誤我好幾天時間,重新修改什麼代碼,如果下次我要養雞、養青蛙了呢?”

這個現代化養豬場出現的問題其實就是需求變化的問題。設計模式可以使系統很容易地在某一特定的層面上適應需求的變化。使用設計模式後,系統就可以很好的滿足開閉原則:我們不用修改原來的代碼,而只需要添加新的代碼就可以滿足新的需求了。

從這個角度來看,使用設計模式的關鍵是預測未來可能發生的需求變化,併在設計過程中選用合適的設計模式。同時,我們也應該將設計模式應用於系統中那些明顯不穩定、極可能發生變化的部分,而不應該為了體驗創造的樂趣,把設計模式應用於那些永遠不會發生變化的組件中去。

例如,在養豬場系統中,當養豬場頭一回引進新品種長白豬時,我們就敏銳立即認識到豬的種類是一種易變的因素,這時就必須引入設計模式以適應這種需求的變化。

從這裡我們可總結出有關設計模式的第一個核心設計原則:

設計模式最根本的意圖是適應需求的變化,我們應只對變化或者可能變化的部分使用設計模式,對於不變的部分濫用設計模式就會造成“過渡設計”。

(二) 針對介面編程,而不要針對實現編程

 

現在,我們來看一下如何改進這個現代化養豬場的設計,使其能最大限度地適應需求變化。
首先,我們應該際加一個豬的抽象介面,該介面中定義了每一類豬共有的行為,而大白豬和長白豬則具體實現這些行為。系統中的大白豬和長白豬滿足完全替換原則,使用時客戶不用考慮豬的具體類型,就可以直接通過抽象的豬的介面來操作具體的大白豬和長白豬對象。


然後,我們修改喂豬機器人的代碼,使其不考慮豬的類型,只應用抽象的豬的介面來操作所有豬的對象實例。例如,喂豬時喂豬機器人需要吼叫的不再是"大白豬,吃!"或"長白豬,吃!"而是"豬,吃!"這種通過抽象類或抽象介面來操作對象的方式就是"針用介面編程"的方法,而此前那種通過具體的類來操作對象的方法可以被稱為"針對實現編程"的方法。
改進後的養豬場如圖v3-1所示。

                  圖v3-1


這個改進的養豬場會為我們帶來什麼好處呢?假設現在養豬場的老闆需要引進波中豬,他只要買來幾頭波中豬的仔豬,扔進養豬場就可以了。喂豬機器人的代碼不需要發生任何變化,它面對每一頭豬只要說"豬,吃!"所有類型的豬都可以愉快地進食。不管養豬場飼養的豬有多少種,喂豬機器人都會把豬喂得腰肥體壯。
添加了波中豬後的系統結構如圖v3-2所示。

                            圖v3-2


可以看到,喂豬機器人完全是針對介面進行編程的,當系統添加一個新的類型時,只需要添加新類型的代碼,而系統中原有的代碼不需要做任何的改變就可以適應新的需求一一這完全符合開閉原則。

V3版面向對象養豬廠的實現代碼如下:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Windows.Forms;
  9 
 10 namespace PigFactoryV3
 11 {
 12     public partial class Form1 : Form
 13     {
 14         /*
 15          * 豬悟能的博客
 16          * http://www.cnblogs.com/hackpig/
 17          * 
 18         建成後的養豬場生意興隆, 老闆決定進軍國際市場, 但是為了迎合外國人口味, 引入了幾條長白豬.
 19         因此,這裡豬的類型成了變化點.
 20         我們把大白豬類, 改成豬介面, 這樣喂豬機器人的工作變成了, "豬,吃", 而不是原來的 "大白豬,吃"
 21         即使老闆再增加幾頭波中豬, 我們也不再需要修改喂豬機器人的工作()了. 只需要增加一個波中豬的類實現豬介面
 22          
 23          
 24         從這裡我們可以總結出有關設計模式的第一個核心設計原則:
 25         
 26         設計模式最根本的意圖是適應需求變化, 我們應只對變化或者可能變化的部分使用設計模式,對於不變的部分濫用設計
 27         模式就會造成"過度設計"
 28          
 29          */
 30 
 31 
 32         public Form1()
 33         {
 34             InitializeComponent();
 35 
 36             feedPigRobot robot = new feedPigRobot("大潤發養豬場");
 37             robot.Attack(new dbPig(1));
 38             robot.Attack(new dbPig(2));
 39             robot.Attack(new dbPig(3));
 40             robot.Attack(new dbPig(4));
 41             robot.Attack(new dbPig(5));
 42 
 43             robot.Attack(new cbPig(6));
 44             robot.Attack(new cbPig(7));
 45             robot.Attack(new cbPig(8));
 46             robot.Attack(new cbPig(9));
 47             robot.Attack(new cbPig(10));
 48 
 49             rtbMsginfo.Text = robot.work();
 50 
 51         }
 52     }
 53 
 54 
 55     public abstract class feedPigFactory
 56     {
 57         private string _factoryName;
 58 
 59         public string FactoryName
 60         {
 61             get { return _factoryName; }
 62             set { _factoryName = value; }
 63         }
 64 
 65     }
 66 
 67     public class feedPigRobot:feedPigFactory
 68     {
 69         IList<Ipig> pigList = new List<Ipig>();
 70 
 71         public feedPigRobot(string factoryName)
 72         {
 73             base.FactoryName = factoryName;
 74         }
 75 
 76         public void Attack(Ipig pig)
 77         {
 78             pigList.Add(pig);
 79         }
 80 
 81         public string work()
 82         {
 83             string msgstr = string.Empty;
 84             foreach (Ipig m in pigList)
 85             {
 86                 msgstr += m.eat()+Environment.NewLine;
 87             }
 88 
 89             return string.Format("{0}{1}{2}",
 90                 base.FactoryName + Environment.NewLine,
 91                 "喂豬機器人開始工作...." + Environment.NewLine + Environment.NewLine,
 92                 msgstr);
 93         }
 94     }
 95 
 96 
 97 
 98     public interface Ipig
 99     {
100 
101         int PigIndex
102         {
103             get;
104             set;
105         }
106 
107         string eat();
108         
109     }
110 
111 
112     public class cbPig : Ipig
113     {
114         private int _pigIndex;
115 
116         public int PigIndex
117         {
118             get { return _pigIndex; }
119             set { _pigIndex = value; }
120         }
121         public cbPig(int pignum)
122         {
123             this._pigIndex = pignum;
124         }
125 
126         public string eat()
127         {
128             return string.Format("{0}[{1}]開始吃.", "長白豬", _pigIndex);
129         }
130     }
131 
132 
133 
134     public class dbPig :Ipig
135     {
136         private int _pigIndex;
137 
138         public int PigIndex
139         {
140             get { return _pigIndex; }
141             set { _pigIndex = value; }
142         }
143         public dbPig(int pignum)
144         {
145             this._pigIndex = pignum;
146         }
147 
148         public string eat()
149         {
150             return string.Format("{0}[{1}]開始吃.", "大白豬", _pigIndex);
151         }
152     }
153 
154 }

 運行結果如下:

代碼分析:

(1) feedPigRobot是喂豬機器人類

  其成員函數 Attack 的參數是Ipig, 它是豬的介面
  public void Attack(Ipig pig)
  這個參數可以接納 dbPig , 大白豬類的實例, 或者是 cbPig,  長白豬類的實例.

   work() 成員函數 遍歷所有IPig的"豬"對象, 調用它的eat()方法. (完成對所有豬喂食的操作)

  foreach (Ipig m in pigList)
  {
        msgstr += m.eat()+Environment.NewLine;
  }

(2) 有了豬的抽象介面(Ipig), 它抽象出了豬共有了行為"吃", 即方法eat(). 喂豬機器人只對這個豬抽象介面喂食, 就不需要知道喂的究竟是長白豬,還是大白豬了.

 


養豬場的老闆還曾經提到過養雞和養青蛙。對此,我們必須明確該需求是否是合理的需求,系統是否需要適應這一需求變化。一般說來,養豬場是不會養雞、養青蛙的,我們沒必要為此多費心思。但如果老闆故意刁難的話,我們也不是沒有解決方案:適應這一需求變化的方法是提取出一個更一般的"動物介面"機器人完全使用"動物介面"類來操作所有的對象。

現在,面向對象的現代化養豬場又欣欣向榮地發展起來了。但我們不能放鬆警惕,變化的需求隨時都會出現。例如這一次,養豬場的老闆突然覺得,老從外面引進仔豬太虧,他希望能在養豬場內部建造一個繁殖基地,自產自銷。於是,我們建造了二個豬工廠,最初的豬工廠結構如圖V5-1所示。

 

 

                                                                                        圖v5-1

 

不難發現,在實現豬工廠時,我們又陷入了針對實現編程的陷阱。管理員使用豬工廠來選擇繁殖哪種類型的仔豬,豬工廠根據管理員的要求執行不同的繁殖過程,繁殖不同類型的仔豬。對於系統中己有的大白豬和長白豬,這沒有問題,但是當我們想繁殖波中豬時,問題又產生了,豬工廠的代碼必須修改。顯然,我們必須想辦法來隔離有關對象創建的代碼,以適應需求變化。設計模式中的創建型模式恰恰可以滿足我們的需要。


為了隔離具體的繁殖過程,我們可以定義一個豬工廠的抽象接U類,其派生類大自豬工廠和長白豬工廠具體地實現介面中的繁殖行為。這樣,和具體實現相關的代碼被推遲到了具體的派生類工廠中去實現,我們在系統外只要用不同的派生類工廠調用繁殖方法,就可以繁殖出不同的仔豬了。修改後的結構如圖V6-1所示。
這一改進為我們帶來的好處是,當我們要添加一種豬的類型時,也相應地添加繁殖這種豬的工廠,系統內原布的代碼不需要改變。這時,豬工廠負責繁殖仔豬,然後把仔豬交給喂豬機器人,這些仔豬的生、老、病、死就完全由喂豬機器人來負責了。為了在這一結構中添加波中豬,我們需要做的事情有添加波中豬類、添加波中豬工廠類、修改管理員繁殖仔豬的代碼等,這些工作都是在系統外完成的,與系統內原有的代碼無關(如圖V6-1所示)。

                                                                                     圖v6-1

 

面向對象養豬廠V6版實現代碼如下:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Windows.Forms;
  9 
 10 namespace PigFactoryV6
 11 {
 12     public partial class Form1 : Form
 13     {
 14         /*
 15          * 豬悟能的博客
 16          * http://www.cnblogs.com/hackpig/
 17          * 
 18         突然有一天,老闆覺得從外面進豬仔太虧, 他希望在養豬場內部建立一個繁殖基地, 自產自銷.
 19         於是, 軟體團隊建立了一個豬工廠.
 20         
 21         現在, 養豬場完全可以由自產的仔豬開始喂養了.
 22         無論是增加要喂食豬的品種, 還是繁殖新的豬的品種, 我們現在都不用改動原有的代碼.
 23         只需要增加新的豬品種的類, 實現豬工廠介面, 和豬介面就可以了.
 24          
 25         由些我們總結出設計模式第二個核心設計原則:
 26         
 27         儘量針對介面編程, 而不要針對實現編程. 針對介面編程的組件不需要知道對象的具體
 28         類型和實現, 只需要知道抽象類定義了哪些介面, 這減少了實現上的依賴關係.
 29          
 30          */
 31 
 32         public Form1()
 33         {
 34             InitializeComponent();
 35 
 36             feedPigRobot robot1 = new feedPigRobot("大潤發養豬場");
 37             adminMan man1=new adminMan(2);
 38             foreach (Ipig m in man1.Piglist)
 39                 robot1.Attack(m);
 40             this.rtbMsgInfo.Text = robot1.work();
 41             
 42         }
 43     }
 44 
 45 
 46     public class adminMan
 47     {
 48         private IList<Ipig> _piglist;
 49 
 50         public IList<Ipig> Piglist
 51         {
 52             get { return _piglist; }
 53             set { _piglist = value; }
 54         }
 55 
 56         public adminMan(int breedType)
 57         {
 58             IList<Ipig> piglist1 = new List<Ipig>();
 59             switch (breedType)
 60             {
 61                 case 1:  //大白豬
 62                     foreach (Ipig m in new breedDBpig().breedPig())
 63                         piglist1.Add(m);
 64                     break;
 65                 case 2:  //長白豬
 66                     foreach (Ipig m in new breedCBpig().breedPig())
 67                         piglist1.Add(m);
 68                     break;
 69                 default:
 70                     break;
 71             }
 72             if (piglist1.Count > 0)
 73                 _piglist = piglist1;
 74            
 75         }
 76     }
 77 
 78 
 79 
 80     public interface IbreedPig
 81     {
 82         IList<Ipig> breedPig();
 83     }
 84 
 85     public class breedDBpig : IbreedPig
 86     {
 87         private IList<Ipig> pigList = new List<Ipig>();
 88         public IList<Ipig> breedPig()
 89         {
 90             Random ran = new Random(DateTime.Now.Millisecond);
 91             for (int i = 0; i < ran.Next(2, 20); i++)
 92                 pigList.Add(new dbPig(i));
 93             return pigList;
 94         }
 95     }
 96 
 97     public class breedCBpig : IbreedPig
 98     {
 99         private IList<Ipig> pigList = new List<Ipig>();
100         public IList<Ipig> breedPig()
101         {
102             Random ran = new Random(DateTime.Now.Millisecond);
103             for (int i = 0; i < ran.Next(2, 20); i++)
104                 pigList.Add(new cbPig(i));
105             return pigList;
106         }
107     }
108 
109 
110     public abstract class feedPigFactory
111     {
112         private string _factoryName;
113 
114         public string FactoryName
115         {
116             get { return _factoryName; }
117             set { _factoryName = value; }
118         }
119 
120     }
121 
122     public class feedPigRobot : feedPigFactory
123     {
124         IList<Ipig> pigList = new List<Ipig>();
125 
126         public feedPigRobot(string factoryName)
127         {
128             base.FactoryName = factoryName;
129         }
130 
131         public void Attack(Ipig pig)
132         {
133             pigList.Add(pig);
134         }
135 
136         public string work()
137         {
138             string msgstr = string.Empty;
139             foreach (Ipig m in pigList)
140             {
141                 msgstr += m.eat() + Environment.NewLine;
142             }
143 
144             return string.Format("{0}{1}{2}",
145                 base.FactoryName + Environment.NewLine,
146                 "喂豬機器人開始工作...." + Environment.NewLine + Environment.NewLine,
147                 msgstr);
148         }
149     }
150 
151 
152 
153     public interface Ipig
154     {
155 
156         int PigIndex
157         {
158             get;
159             set;
160         }
161 
162         string eat();
163 
164     }
165 
166 
167     public class cbPig : Ipig
168     {
169         private int _pigIndex;
170 
171         public int PigIndex
172         {
173             get { return _pigIndex; }
174             set { _pigIndex = value; }
175         }
176         public cbPig(int pignum)
177         {
178             this._pigIndex = pignum;
179         }
180 
181         public string eat()
182         {
183             return string.Format("{0}[{1}]開始吃.", "長白豬", _pigIndex);
184         }
185     }
186 
187 
188 
189     public class dbPig : Ipig
190     {
191         private int _pigIndex;
192 
193         public int PigIndex
194         {
195             get { return _pigIndex; }
196             set { _pigIndex = value; }
197         }
198         public dbPig(int pignum)
199         {
200             this._pigIndex = pignum;
201         }
202 
203         public string eat()
204         {
205             return string.Format("{0}[{1}]開始吃.", "大白豬", _pigIndex);
206         }
207     }
208 
209 
210 }

 

 運行效果:

 

代碼說明:

(1)  下麵代碼可以看到, 

feedPigRobot是喂豬機器人類, adminMan是繁殖管理類, 它使用工廠方式創建對應品種的豬.


 feedPigRobot robot1 = new feedPigRobot("大潤發養豬場");
            adminMan man1=new adminMan(2);
            foreach (Ipig m in man1.Piglist)
                robot1.Attack(m);
            this.rtbMsgInfo.Text = robot1.work();
adminMan()有個構造函數參數, 傳入參數1,或者2, 表示使用工廠方式創建隨機數量的大白豬或者是長白豬.
 IList<Ipig> piglist1 = new List<Ipig>();
            switch (breedType)
            {
                case 1:  //大白豬
                    foreach (Ipig m in new breedDBpig().breedPig())
                        piglist1.Add(m);
                    break;
                case 2:  //長白豬
                    foreach (Ipig m in new breedCBpig().breedPig())
                        piglist1.Add(m);
                    break;
                default:
                    break;
            }

(2)  本常式傳入adminMan()參數為2, 因此只是隨機創建了一批長白豬

 


由此,我們可以總結出設計模式的第二個核心設計原則:
儘量針對介面編程,而不要針對實現編程。針對介面編程的組件不需要知道對象的具體類型和實現,只需要知道抽象類定義了哪些介面,這減少了實現上的依賴關係。

 

 

未完待續......

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • --> Poker類用於存入54張撲克牌 --> Player類對玩家進行發牌和顯示處理(寫完我就後悔了,J,Q,K,A,2的排序太low了..package com.dragon.java.hwddz; --> 寫完發現邏輯是不是有問題啊... ...
  • 定義: 單來源調用指一個類的生成工作只能由特定類來執行。 eg李寧牌鞋子只能由李寧專賣店生產 這個問題歸結起來,也就是說在工廠模式中,指定的產品類只能通過具體的特定工廠類來生成,而不能自己new出來或者通過其他類生成。 具體的,我們就在代碼實現中進行說明瞭。 這裡我們來一步一步分析。 首先,一個類實 ...
  • 昨天跟大家分享了Hibernate中單向的一對多、單向多對一、雙向一對多的映射關係,今天跟大家分享下在Hibernate中雙向的多對多的映射關係 這次我們以項目和員工舉個慄子,因為大家可以想象得到,在真實的環境下,一個項目肯定是對應著多個員工的,這毫無疑問, 那麼同時,一個比較牛員工也能同時參與多個 ...
  • 接觸Hibernate也有一小段的時間了,愈發的覺得Hibernate是個神奇的東西,為什麼這麼說呢?因為你可以不懂一行sql,直接面向對象,就可以將數據直接保存到資料庫去!! 你還可以保存一個對象,然後一次性的將與它相關的所有數據保存到資料庫,比如說,你只需要保存班級對象,就可以將該班級信息和該班 ...
  • "封裝、"多態"、"繼承"。 "單一職責原則"、"開放封閉原則"、"里氏替換原則"、"依賴倒置原則"、"介面分離原則"。 低耦合與高內聚 ...
  • 首先,我看的是Nop 3.80,最新版 百度資料很多,Nop用到的主要的技術有: 1、Mvc,最新版用的是 5.2.3.0 2、entity framework 3、autofac 4、插件化 5、(因為我是邊看代碼邊寫這個博客,怕忘記了一些東西,所以邊看便記錄,後面我看到感覺有好處的會添加到這裡來 ...
  • (三) 優先使用聚合,而不是繼承 有一段時間,養豬場的老闆雇用了清潔工人來打掃豬舍。但有一天,老闆忽然對自己說"不對啊,既然我有機器人,為什麼還要雇人來做這件事情?應該讓機器人來打掃宿舍!" 於是,這個需求被提交到了機器人的研發小組。看到這個需求,我們敏感地意識到,這是一個潛藏了更多變化的需求,未來 ...
  • Atitit.excel導出 功能解決方案 php java C#.net版總集合.docx 1.1. Excel的保存格式office2003 office2007/2010格式1 1.2. 類庫選型java .net均有apache.poi 可用,php使用phpexcel1.8.01 1.3. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...