Liskov替代原则和加强验证?

时间:2018-03-21 13:14:56

标签: c# oop liskov-substitution-principle

    public abstract class Entity 
    {
      public abstract IList<string> Values { get; set; }
    }

    public class GenericEntity : Entity 
    {
      private IList<string> _Values;
      public override IList<string> Values 
      {
        get { return _Values; }
        set 
        {
          ValidateValues(value);
          _Values = value;
        }    
      }
      protected virtual void ValidateValues(IList<string> values)
      {
        // many validation conditions here, if values validation fails, throws...
      }
    }

public class AEntity : GenericEntity 
{
  protected override void ValidateValues(IList<string> values)
  {
    base.ValidateValues(values);
    // there is an extra validation condition
    if(values.Count < 1) throw InvalidOperationException("values count!");
  }
}

public class BEntity : GenericEntity 
{
  protected override void ValidateValues(IList<string> values)
  {
    base.ValidateValues(values);
    if(values.Count < 2) throw InvalidOperationException("values count!");
  }
}

GenericEntity用户的继承者有额外的验证条件(检查Values项的数量)时,这是否违反了LSP?我认为是因为先决条件已经得到加强。

这是否意味着我应该删除GenericEntity类并让AEntityBEntity成为Entity的直接继承者?但它会导致两个类中重复的代码。

另外我考虑另一种选择:当Entity获得对某个抽象类Validation实例的引用时。这是问题最优雅的解决方案吗?

2 个答案:

答案 0 :(得分:1)

目前完全没有“违反LSP”,因为您没有更改类别基础验证。 不幸的是,其他开发人员可以覆盖ValidateValues而无需调用基本方法......

避免这种情况的解决方案是创建两种方法:

    public void ValidateValues(IList<string> values) {
        // many validation conditions here, if values validation fails, throws...
        ValidateValuesEx();    
    }

    protected virtual void ValidateValuesEx() {
        // for extension
    }

答案 1 :(得分:0)

通过代码设计,子类可以通过不调用base.ValidateValues完全绕过验证。我不知道这是否违反了LSP,但你应该考虑这个问题。如果您做了一些小改动,您可以确保每次调用GenericEntity.ValidateValues中发生的验证。

public class GenericEntity : Entity 
{
    private IList<string> _Values;
    public override IList<string> Values 
    {
        get { return _Values; }
        set 
            {
                ValidateValues(value);
                _Values = value;
            }    
        }
        private void ValidateValues(IList<string> values)
        {
            // many validation conditions here, if values validation fails, throws...
            this.OnValidateValues(values);
        }
        protected virtual void OnValidateValues(IList<string> values)
        {
            //do nothing.  Let sub-classes override.
        }
    }
}

public class AEntity : GenericEntity 
{
    protected override void OnValidateValues(IList<string> values)
    {
        if(values.Count < 1) throw InvalidOperationException("values count!");
    }
}

在此代码中,GenericEntity控制对子类的调用&#39;验证码。不是相反。父类验证保证始终运行,并且它总是在任何子类验证之前发生。

请注意,无论子类是否调用base.OnValidateValues

,代码的工作方式都完全相同