多种单一用途方法与少数多用途方法

Multiple single purpose methods vs few multiple purpose methods

我一直认为带有显式签名和3-5行代码的方法和函数会使代码更清晰,但在很多情况下我被告知我写得太多functions/methods。显然,在方法很少的 class 中导航更容易。这些人说一个方法应该只为了可重用性而拆分。我个人认为,当一个方法更长时,它往往会随着修改而变得更长,并且会增加复杂性。即使是疯子,它也不再是可测试的了。我读过这个主题,我没有改变主意,但似乎只有我一个人这么想。我错了吗?

我从 msdn 中获取了这段代码:

   private void CreatePO(string filename)
   {
      // Create an instance of the XmlSerializer class;
      // specify the type of object to serialize.
      XmlSerializer serializer = 
      new XmlSerializer(typeof(PurchaseOrder));
      TextWriter writer = new StreamWriter(filename);
      PurchaseOrder po=new PurchaseOrder();

      // Create an address to ship and bill to.
      Address billAddress = new Address();
      billAddress.Name = "Teresa Atkinson";
      billAddress.Line1 = "1 Main St.";
      billAddress.City = "AnyTown";
      billAddress.State = "WA";
      billAddress.Zip = "00000";
      // Set ShipTo and BillTo to the same addressee.
      po.ShipTo = billAddress;
      po.OrderDate = System.DateTime.Now.ToLongDateString();

      // Create an OrderedItem object.
      OrderedItem i1 = new OrderedItem();
      i1.ItemName = "Widget S";
      i1.Description = "Small widget";
      i1.UnitPrice = (decimal) 5.23;
      i1.Quantity = 3;
      i1.Calculate();

      // Insert the item into the array.
      OrderedItem [] items = {i1};
      po.OrderedItems = items;
      // Calculate the total cost.
      decimal subTotal = new decimal();
      foreach(OrderedItem oi in items)
      {
         subTotal += oi.LineTotal;
      }
      po.SubTotal = subTotal;
      po.ShipCost = (decimal) 12.51; 
      po.TotalCost = po.SubTotal + po.ShipCost; 
      // Serialize the purchase order, and close the TextWriter.
      serializer.Serialize(writer, po);
      writer.Close();
   }

我操纵将其转换为以下代码:

    private void CreatePO(string filename)
    {
        Serialize(GetPurchaseOrder(), filename);
    }

    private PurchaseOrder GetPurchaseOrder()
    {
        return new PurchaseOrder()
            {

                ShipTo = GetBillAdress(),
                OrderDate = System.DateTime.Now.ToLongDateString(),
                OrderedItems = GetOrderedItems(),
                ShipCost = (decimal)12.51
            };
    }

    //Will be inside of PurchaseOrder class
    private decimal GetSubTotal(OrderedItem[] items)
    {
        return items.Sum(x => x.LineTotal);
    }

    private OrderedItem[] GetOrderedItems()
    {
        OrderedItem i1 = new OrderedItem()
            {
                ItemName = "Widget S",
                Description = "Small widget",
                UnitPrice = (decimal)5.23,
                Quantity = 3,
                Calculate()
            };

        // Insert the item into the array.
        return new OrderedItem[]{ i1 };
    }

    private void Serialize<T>(T toSerialize, string filename)
    {
        using (var w = new StreamWriter(filename))
        {
            var s = new XmlSerializer(typeof(T));

            s.Serialize(w, toSerialize);
        }            
    }

    private Adress GetBillAdress()
    {
        return new Address()
            {
                Name = "Teresa Atkinson",
                Line1 = "1 Main St.",
                City = "AnyTown",
                State = "WA",
                Zip = "00000"
            };
    }

有人可能会说这里的大部分功能只会用一次。但最佳做法是什么?这种拆分代码的方式会减慢执行速度吗?并使编译复杂化?除了可读性之外,两者的真正优势是什么?

嗯,我认为你是对的。但这是一个意见。

有关方法数量和大小的最佳实践,请参阅this question

但也有可能以完全错误的方式做到这一点!

在较小的方法中拆分工作时,您必须注意不要在这些方法之间引入不需要的依赖关系。

例如,当一个方法开始依赖另一个方法设置的状态而不是将该信息作为参数传递时,我们就进入了浑水。

所以这当然取决于它是如何实现的。

对于可测试性来说,这实际上并不重要,因为大多数这些小方法都是私有的,只能通过 class 的 public 接口进行测试。

当然不是要单独测试每种方法。这会使测试非常脆弱。当您从一个经过测试的大方法开始并重构为一堆较小的方法时,不应更新测试,您应该保持 100% 的覆盖率。