打造可維護軟體C#版 第五章 讓程式碼單元的介面保持簡單

        第五章談到程式碼函式參數數量的化簡,主要有兩種重構方法「參數物件」和「使用方法物件替代方法」。


指導方針:


限制每個程式碼單元的參數不能超過4個。


透過將多個參數提取到物件來達成這件事。


較少的參數讓程式碼單元更容易理解和重新利用。


 


範例示範引進參數物件(Introduce Parameter Object)的重構方法


惡例Render方法有6個參數。


   private void Render(Square square, Graphics g, int x, int y, int w, int h)


   {


       square.Sprite.Draw(g, x, y, w, h);


       foreach (Unit unit in square.Occupants)


       {


           unit.Sprite.Draw(g, x, y, w, h);


       }


    }


可建立Rectangle類別,其中有Point 、Width 和 Height。


   public class Rectangle


   {


       public Point Position { get; set; }


       public int Width { get; set; }


       public int Height { get; set; }


 


       public Rectangle(Point position, int width, int height)


       {


           this.Position = position;


           this.Width = width;


           this.Height = height;


       }


   }


 


使用參數物件之後,原本Render方法縮減到3個參數。


 


   private void Render(Square square, Graphics g, Rectangle r)


   {


       Point position = r.Position;


       square.Sprite.Draw(g, position.X, position.Y, r.Width, r.Height);


       foreach (Unit unit in square.Occupants)


       {


           unit.Sprite.Draw(g, position.X, position.Y, r.Width, r.Height);


       }


   }


 


可再繼續化簡


 


   private void Render(Square square, Graphics g, Rectangle r)


   {


       Point position = r.Position;


       square.Sprite.Draw(g, r);


       foreach (Unit unit in square.Occupants)


       {


           unit.Sprite.Draw(g, r);


       }


   }


 


5.1  動機


 


簡短介面更容易理解及重利用


 


具有簡短介面的方法比較容易修改


 


5.2  如何使用本指導方針?


 


冗長介面通常不是主要的問題,而是一種程式碼壞味道,意味著更深層的可維護性問題。


 


惡例顯示有冗長參數介面的方法


 


       public void DoBuildAndSendMail(MailMan m, string firstName, string lastName,


           string division, string subject, MailFont font, string message1,


           string message2, string message3)


       {


           // Format the email address


           string mId = $"{firstName[0]}.{lastName.Substring(0, 7)}" +


               $"@{division.Substring(0, 5)}.compa.ny";


           // Format the message given the content type and raw message


           MailMessage mMessage = FormatMessage(font,


               message1 + message2 + message3);


           // Send message


           m.Send(mId, subject, mMessage);


       }


 


將參數分類之後改寫成物件參數,成功地減少參數數量


 


       public void DoBuildAndSendMail(MailMan m, MailAddress mAddress,


           MailBody mBody)


       {


           // Build the mail


           Mail mail = new Mail(mAddress, mBody);


           // Send the mail


           m.SendMail(mail);


       }


       public class Mail


       {


           public MailAddress Address { get; set; }


           public MailBody Body { get; set; }


 


           public Mail(MailAddress mAddress, MailBody mBody)


           {


               this.Address = mAddress;


               this.Body = mBody;


           }


       }


 


       public class MailBody


       {


           public string Subject { get; set; }


           public MailMessage Message { get; set; }


 


           public MailBody(string subject, MailMessage message)


           {


               this.Subject = subject;


               this.Message = message;


           }


       }


 


       public class MailAddress


       {


           public string MsgId { get; private set; }


 


           public MailAddress(string firstName, string lastName,


               string division)


           {


               this.MsgId = $"{firstName[0]}.{lastName.Substring(0, 7)}" +


                   $"@{division.Substring(0, 5)}.compa.ny";


           }


       }


 


如果方法的各個參數無法妥適地組織在一起,該怎麼辦?


 


       public static void DrawBarChart(Graphics g,


           CategoryItemRendererState state,


           Rectangle graphArea,


           CategoryPlot plot,


           CategoryAxis domainAxis,


           ValueAxis rangeAxis,


           CategoryDataset dataset)


       {


           // ..


       }


 


第一種方法「多載」


 


       public static void DrawBarChart(Graphics g, CategoryDataset dataset)


       {


           Charts.DrawBarChart(g,


           CategoryItemRendererState.DEFAULT,


           new Rectangle(new Point(0, 0), 100, 100),


           CategoryPlot.DEFAULT,


           CategoryAxis.DEFAULT,


           ValueAxis.DEFAULT,


           dataset);


       }


 


第二種方法「使用方法物件替代方法」


 


   public class BarChart


   {


       private CategoryItemRendererState state = CategoryItemRendererState.DEFAULT;


       private Rectangle graphArea = new Rectangle(new Point(0, 0), 100, 100);


       private CategoryPlot plot = CategoryPlot.DEFAULT;


       private CategoryAxis domainAxis = CategoryAxis.DEFAULT;


       private ValueAxis rangeAxis = ValueAxis.DEFAULT;


       private CategoryDataset dataset = CategoryDataset.DEFAULT;


 


       public BarChart Draw(Graphics g)


       {


           // ..


           return this;


       }


 


       public ValueAxis GetRangeAxis()


       {


           return rangeAxis;


       }


 


       public BarChart SetRangeAxis(ValueAxis rangeAxis)


       {


           this.rangeAxis = rangeAxis;


           return this;


       }


 


       // More getters and setters.


   }


 


       private void ShowMyBarChart()


       {


           Graphics g = this.CreateGraphics();


           BarChart b = new BarChart()


           .SetRangeAxis(myValueAxis)


           .SetDataset(myDataset)


           .Draw(g);


       }


 


《Clean Code》有提到火車寫法不好,用這種寫法要考慮。


 


5.3  常見的反對意見


 


反對意見:參數物件具有龐大的介面


 


應該思考引進物件之結構,以及與其他程式碼之間的關係。


 


反對意見:重構冗長介面並未改善我的情況


 


避免冗長的介面並不是只依靠重構,可持續將不同職責拆分到各個方法。


 


反對意見:框架或程式庫已經定義了具有冗長參數列的介面


 


部分框架或程式庫有冗長參數介面,可用包裹或適配器的方式隔離框架或程式庫。


 


5.4  參考


 


 


留言

這個網誌中的熱門文章

異世界NTR web版第三章 觀後感 喧賓奪主 ,反派實力過強

泛而不精的我被逐出了勇者隊伍 web第三章 觀後感 菲莉真能打; 露娜超爽der

持有縮小技能的D級冒險者,與聖女結婚並加入勇者團隊 漫畫 01-04 觀後感 大我與小我