邁向架構設計師之路系列—1-簡單對象訪問模式

    假設場景

  現在假如公司要你做一個公司內部的薪資管理系統,根據職位的不同,每月的工資自然不一樣,經理一月10000加上分紅1000,技術人員一月5000加上200的餐補,客服一月3000,現在要是由你來做,你會怎麼設計?代碼無錯便是優已經不適用了

  大部分人的寫法v1.0

  這樣的寫法會帶來一個問題?什麼問題呢?複用性的問題

  假如現在你接了個私活,別的公司讓你也寫個公司內部的薪資計算系統,你說那還不簡單,把代碼複製過去就行啊,有人說過初級程序員的工作就是Ctrl+C和Ctrl+V,這其實是非常不好的編碼習慣,因爲當你的代碼中重複的代碼多到一定程度的時候,維護起來就是一場災難,越大的系統這種方式帶來的問題越嚴重,編程有一個原則就是儘可能的想辦法避免重複,再說了比如客戶要求你給它們公司做的內部薪資計算系統是個Web版的,或者是個WindowsApplication版的,那麼你下面的代碼就廢掉了,想想看,薪資計算系統哪些是和控制檯程序無關的,只和計算有關的,也就是說分出一個類讓計算和顯示分開,也就是要讓業務和界面分開

  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;

  namespace _1_SimpleFactory
  {
      class Program
      {
          static void Main(string[] args)
          {
                try
                {
                    int intSalary = 0; ;//薪水
                    string strPost = Console.ReadLine();//職位
                    switch (strPost)
                    { 
                        case "經理":
                            intSalary = 10000 + 1000;
                           break;
                        case "技術":
                            intSalary = 5000 + 200;
                           break;
                        case "客服":
                            intSalary = 3000;
                           break;
                    }
                    Console.WriteLine("工資是:" + intSalary);
                    Console.ReadLine();
                 }
                  catch (Exception ex)
                 { 
                 }
          }
      }
  }

  

   一:第一次改版後的代碼v1.1

  爲什麼要改版?因爲考慮到複用性的問題

  要讓業務邏輯和界面邏輯分開,讓它們的耦合度下降,只有分離開才能達到更容易維護和擴展,也就是業務的封裝

  我們新建一個類庫,名字叫BusinessLogic,裏面有個類文件叫Calculate.cs,專門處理根據職位計算工資的,當我們需要做個Web版的話,只需要把界面邏輯文件Program.cs代碼中的“Console.WriteLine("工資是:" + intSalary);Console.ReadLine();”換成“ASP.NET的Response.Write(("工資是:" + intSalary);Response.End();”就OK了,業務邏輯文件Calculate.cs是可以複用的,這樣就達到了業務和界面分離了,如果你現在要做一個Windows版的,手機版的,PDA版的,都可以複用這個Calculate類

  這次改版後的代碼修復了原始代碼不能複用性的問題,這裏只是用到了面向對象三個特性中的封裝特性,把業務邏輯封裝起來,繼承和多態在這裏還沒用到呢

  這樣的寫法又會帶來一個新的問題?缺乏靈活性,靈活性包含兩個方面,一個方面是修改性,一個方面則是擴展性

  已經把業務和界面分離了不是很靈活了嗎?好那我們現在打個比方,比方CEO現在要在公司中安排一些親戚進來工作,職位給了個虛頭銜是助理,親戚自然不能虧待,工資是每月6000+1000,你該怎麼設計呢?只要修改Calculate.cs 業務邏輯文件,在裏面加個分支就行了,問題來了,如果你這樣做,你只是要增加一個職位卻讓“經理”和“技術”以及“客服”參加了編譯,這是糟糕的,你萬一一不小心把case "經理":intSalary = 10000 + 1000;改成了case "經理":intSalary = 1000 + 1000;你們經理該弄死你了,其次你看到“經理”補助1000,我們技術部補助才200,他奶奶的,不行我要提高我們部門的待遇,然後你把case "技術":intSalary = 5000 + 200;改寫成intSalary = 5000 + 800;這樣你們部門每個技術都要比原來多發600元人民幣了,本來是讓你增加一個功能,卻讓原來運行良好的代碼產生了變化,這是非常糟糕的且有巨大風險的,那麼我們下面該怎麼設計呢?

   1:Calculate.cs 業務邏輯文件

   using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;

  namespace BusinessLogic
  {
      public class Calculate
      {
          /// <summary>
          /// 計算工資
          /// </summary>
          /// <param name="strPost">職位描述</param>

      ///<returns>工資</returns>
          public static int CalculateSalary(string strPost)
          {
              int intSalary = 0; ;//薪水
              switch (strPost)
              {
                  case "經理":
                      intSalary = 10000 + 1000;
                     break;
                  case "技術":
                      intSalary = 5000 + 200;
                     break;
                  case "客服":
                      intSalary = 3000;
                     break;
              }
              return intSalary;
          }
      }
  }

  2:Program.cs 界面邏輯文件

  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using BusinessLogic;

  namespace _1_SimpleFactory
  {
      class Program
      {
          static void Main(string[] args)
          {
              try
              {
                  int intSalary = Calculate.CalculateSalary(Console.ReadLine());
                  Console.WriteLine("工資是:" + intSalary);
                  Console.ReadLine();
              }
              catch (Exception ex)
              { 
                
              }
          }
      }
  }

  工程架構圖

  

  二:第二次改版後的代碼v1.2

  爲什麼要再次改版呢?因爲考慮到靈活性的問題

  我們要讓“經理”和“技術”以及“客服”運算類分開,修改其中一個類不影響其他的類,增加“助理”等運算類也不影響其他的代碼

  首先還是在名字叫BusinessLogic的類庫裏面有個類文件叫Calculate.cs的計算工資類,裏面有個虛方法CalculateSalary(),用於計算工資的,然後我們把“經理”和“技術”及“客服”和“助理”寫成了計算工資類的子類,繼承後,重寫了CalculateSalary()方法,這樣要修改任何一個職位的工資,就不需要提供其他職位的代碼了,其次要增加一個職位,只需要新建一個類繼承計算工資類,重寫CalculateSalary()方法就行了,也不用提供其他職位的代碼了

  這次改版後的代碼修復了第一版代碼不具有靈活性的問題

  這樣寫又會帶來一個新的問題?什麼問題呢?就是如何讓客戶端知道我想用哪個計算工資類呢?我們是用CalculateJinLi.cs這個經理計算工資類,還是用CalculateJiShu.cs這個技術計算工資類呢?

  1:Calculate.cs

  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;

  namespace BusinessLogic
  {
      /// <summary>
      /// 計算工資抽象類,這個抽象不是指把它定義爲抽象類,而是它存在的意義是爲了抽象
      /// </summary>
      public class Calculate
      {
          /// <summary>
          /// 計算工資
          /// </summary>
          ///<returns>工資</returns>
          public virtual int CalculateSalary()
          {
              int intSalary = 0; ;//薪水
              return intSalary;
          }
      }
  }

  2:CalculateJinLi.cs

  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;

  namespace BusinessLogic
  {
      /// <summary>
      /// 計算經理工資的類,繼承計算工資類
      /// </summary>
      public class CalculateJinLi:Calculate
      {
          public override int CalculateSalary()
          {
              int intSalary = 10000 + 1000 ;//薪水
              return intSalary;
          }
      }
  }

  3:CalculateJiShu.cs

  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;

  namespace BusinessLogic
  {  
      /// <summary>
      /// 計算技術工資的類,繼承計算工資類
      /// </summary>
      public class CalculateJiShu:Calculate
      {
          public override int CalculateSalary()
          {
              int intSalary = 5000 + 200;
              return intSalary;
          }
      }
  }

  4:CalculateKeFu.cs

  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;

  namespace BusinessLogic
  {
      /// <summary>
      /// 計算客服工資的類,繼承計算工資類
      /// </summary>
      public class CalculateKeFu:Calculate
      {
          public override int CalculateSalary()
          {
              int intSalary = 3000;
              return intSalary;
          }
      }
  }

  5:CalculateZhuLi.cs

  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;

  namespace BusinessLogic
  {
      /// <summary>
      /// 計算助理工資的類,繼承計算工資類
      /// </summary>
      public class CalculateZhuLi:Calculate
      {
          public override int CalculateSalary()
          {
              int intSalary = 6000 + 1000;
              return intSalary;
          }
      }
  }

  工程架構圖

  

   三:第三次改版後的代碼v1.3

   第二次代碼改版後遇到的問題就是如何去實例化對象的問題,到底要實例化誰,將來會不會增加實例化的對象,比如增加“助理”職位的計算工資,這是很容易變化的地方,以後保不準會增加“銷售”這個職位的計算工資類等,應該考慮一個單獨的類來做這個創造實例的過程,這就是工廠

  在第二版代碼上,我們增加CalculateFactory這個類庫,裏面有個CalculateFactory.cs類文件,類文件中有個CreateCalculate()方法,根據輸入的職位,工廠實例化出合適的對象,通過多態返回父類的方式實現計算工資的結果

  1:CalculateFactory.cs 工廠類文件

  using System.Linq;
  using System.Text;
  using BusinessLogic;

  namespace CalculateFactory
  {  
      /// <summary>
      /// 計算工資工廠類
      /// </summary>
      public class CalculateFactory
      {
          /// <summary>
          /// 根據傳入的職位創造對應的實例
          /// </summary>
          /// <param name="strPost">職位</param>
          /// <returns>Calculate抽象類</returns>
          public static Calculate CreateCalculate(string strPost)
          {
              Calculate calulate = null;
              switch (strPost)
              { 
                  case "經理":
                      calulate = new CalculateJinLi();
                      break;
                  case "技術":
                      calulate = new CalculateJiShu();
                      break;
                  case "客服":
                      calulate = new CalculateKeFu();
                      break;
                  case "助理":
                      calulate = new CalculateZhuLi();
                      break;
              }
              return calulate;
          }
      }
  }

  2:Program.cs 客戶端文件

  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using BusinessLogic;
  using CalculateFactory;

  namespace _1_SimpleFactory
  {
      class Program
      {
          static void Main(string[] args)
          {
              Calculate calculate = null;
              //由工廠根據參數決定要實例化哪個對象
              calculate = CalculateFactory.CalculateFactory.CreateCalculate("經理");
              int intResult = calculate.CalculateSalary();
              Console.WriteLine("經理工資爲:" + intResult);
              calculate = CalculateFactory.CalculateFactory.CreateCalculate("技術");
              intResult = calculate.CalculateSalary();
              Console.WriteLine("技術工資爲:" + intResult);
              calculate = CalculateFactory.CalculateFactory.CreateCalculate("客服");
              intResult = calculate.CalculateSalary();
              Console.WriteLine("客服工資爲:" + intResult);
              calculate = CalculateFactory.CalculateFactory.CreateCalculate("助理");
              intResult = calculate.CalculateSalary();
              Console.WriteLine("助理工資爲:" + intResult);
              Console.ReadLine();
          }
      }
  }

  運行效果圖

  

  工程架構圖

  

   UML類圖解析

  統一建模語言UML(是Unified Modeling Language)的縮寫,是用來對軟件密集系統進行可視化建模的一種語言,UML爲面向對象開發系統的產品進行說明,可視化,和編制文檔的一種標準語言

  類圖分爲三層,第一層顯示類的名稱,如果是抽象類,則用斜體表示,第二層是類的特性,通常是字段和屬性,第三層是類的操作,通常是方法和行爲,前面的符號“+”,表示public,”-“表示private,“#”表示protected

  

  總結

  這樣我們就完成了一個具有複用性,靈活性(可修改,可擴展)的公司內部薪資管理系統,我們同時也看到這樣一個小小的公司內部薪資系統,都用到了封裝,繼承,多態

  思考

  如果我們有一天我們需要更改經理的工資怎麼辦?我們只需要改CalculateJinLi.cs裏的代碼就行了,如果我們需要增加“銷售”職位的工資算法怎麼辦?我們只需要增加相應的子類就行了,還要去計算工資工廠在switch裏增加分支就行了,那我們要去修改界面怎麼辦?比如我們換成Web版的,那就去改界面呀,跟運算毫無關係

  這個只是24種設計模式中最簡單的“簡單對象訪問模式”,其他很多設計模式有的很複雜,由於平時工作很忙,沒有及時更新請大家諒解,請關注我的博客

 

每天學習一點點,每天進步一點點 用文字記錄工作,用文字記錄人生
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章