修改父行为的访问者的层次结构。 Liskov 还好吗?

Hierarchies of Visitors modifying the behavior of parent. Is it fine with Liskov?

有一个class IUser。它有一个接受访问者并允许更改 public 属性的函数。

public IUser
{
    public PermissionMatrix Permissions { get; set; }    

    public void Authorizations(IAuthManager manager)
    {
       manager.SetRoles(this);
    }
}

现在,它可以被 IAuthManagerclass 层级访问

public IAuthManager
{
   public void SetRoles(IUser user);
}

public InternalAuthManager : IAuthManager
{
   public virtual void SetRoles(IUser user)
   {
     // sets permissions in user for internal security
     // according to a complex logic
   }
}

public RestrictInternalAuthManager : InternalAuthManager
{
   public override void SetRoles(IUser user)
   {
     base.SetRoles(user); // need to use complex logic of parent
     // then reverts few permissions based on conditions
   }
}

我想评估 class RestrictInternalAuthManager 是否违反了里氏替换原则。 我一直在争论是非,

否:不检查 IAuthManager 的类型。

是:RestrictInternalAuthManager 正在更改 post-InternalAuthManager 的条件。

可以保持原样,还是 classes 需要重构?感谢任何帮助。

Liskov 替换原则在单元测试和 tdd 中派上用场。通过基础 class 的测试也应该通过任何派生的 classes。如果您正在使用 tdd 或以其他方式进行具有高代码覆盖率的单元测试,那么如果派生的 class 恢复了其中的某些更改,则测试是否进行了预期更改的基础 class 的测试将会失败。

所以,是的,我认为它违反了 LSP。

"reverting of inherited behaviour" 对我也有不好的品味。

我建议使用抽象基础 class 来完成常见的事情。比创建两个派生的 classes 只是 "add" 一些变化,而不是恢复一些继承的。

Let Φ(x) be a property provable about objects x of type T. Then Φ(y) should be true for objects y of type S where S is a subtype of T.

现在的问题是您还没有真正指定 setRoles 方法的行为;如果有人会推断 "it sets permissions",那么您并没有真正改变行为。如果你说 "this method does it in such and such way" 并且在 subclass 中你会改变它,那么是的,你会违反 LSP。 IE。如果你对 Queue 和 Stack 都说 "get() retrieves an item",那就没问题,但是如果你说 "Queue.get() retrieves oldest item" 和 "Stack.get() retrieves newest item",那么它们就不同了。

LSP 背后的想法是,预期的行为不会改变,因此例如,如果您要为基础 class 编写测试,那么如果您提供子class 代替。

另一个重要的问题是:我是在子类型化,还是子classing?如果我更改 superclass 的行为,我是否需要在 subclass 中进行更改?我只是为了方便而重用 superclass 中的实现吗?如果是这样,那就是违规了。

再想一想,如果您需要另一位经理来增加一件事,会发生什么情况;这可能很快就会失控。如果不确定是否继承,通常最好使用分解来代替。