重构指南 - 封装集合(Encapsulate Collection)

封装就是将相关的方法或者属性抽象成为一个对象。

封装的意义:

  1. 对外隐藏内部实现,接口不变,内部实现自由修改。
  2. 只返回需要的数据和方法。
  3. 提供一种方式防止数据被修改。
  4. 更好的代码复用。
当一个类的属性类型为集合,或者方法返回类型为集合时,如果符合以下条件,我们就可以考虑将集合进行封装:
  1. 返回的数据仅用于展示
  2. 当集合的Add,Remove方法包含其它业务逻辑
向类的调用者隐藏类中的完整集合有如下几个好处:
  1. 保证返回的集合数据不会被修改。
  2. 在Add, Remove方法中可以添加验证,日志或其他业务逻辑。
代码示例:
using System.Collections.Generic;

namespace LosTechies.DaysOfRefactoring.EncapsulateCollection.Before
{
    public class Order
    {
        private List<OrderLine> _orderLines;
        private double _orderTotal;

        public IList<OrderLine> OrderLines
        {
            get { return _orderLines; }
        }

        public void AddOrderLine(OrderLine orderLine)
        {
            _orderTotal += orderLine.Total;
            _orderLines.Add(orderLine);
        }

        public void RemoveOrderLine(OrderLine orderLine)
        {
            orderLine = _orderLines.Find(o => o == orderLine);

            if (orderLine == null)
                return;

            _orderTotal -= orderLine.Total;
            _orderLines.Remove(orderLine);
        }
    }

    public class OrderLine
    {
        public double Total { get; private set; }
    }
} 
 
上面的代码在Add或者Remove orderLine时存在业务逻辑,如果调用时直接修改OrderLines的元素,就会产生bug,所以需要重构如下:
 
using System.Collections.Generic;

namespace LosTechies.DaysOfRefactoring.EncapsulateCollection.After
{
    public class Order
    {
        private List<OrderLine> _orderLines;
        private double _orderTotal;

//方法一:返回IEnumerable类型
        public IEnumerable<OrderLine> OrderLines
        {
            get { return _orderLines.Skip(0); }
        }

//方法二:返回只读类型
         public ReadOnlyCollection<OrderLine> OrderLines
        {
            get { return _orderLines.AsReadOnly(); }
        }
        public void AddOrderLine(OrderLine orderLine)
        {
            _orderTotal += orderLine.Total;
            _orderLines.Add(orderLine);
        }

        public void RemoveOrderLine(OrderLine orderLine)
        {
            orderLine = _orderLines.Find(o => o == orderLine);

            if (orderLine == null)
                return;

            _orderTotal -= orderLine.Total;
            _orderLines.Remove(orderLine);
        }
    }

    public class OrderLine
    {
        public double Total { get; private set; }
    }
}
 
注意:虽然直接返回IEnumerable,这样只能遍历取出它的值,但是还是可以通过转换为List后操作集合中的元素,所以我们采用_orderLines.Skip(0)迭代返回,这样就能阻止调用者转换为list。
posted @ 2017-01-04 11:16  <HOU>  阅读(485)  评论(0编辑  收藏  举报