Skip to main content
deleted 8 characters in body
Source Link
Daniel T.
  • 3.1k
  • 20
  • 24

Yes it breaks LSP. Specifically the setAge method is a problem. The solution IMO is to remove the method or change its postcondition.

By that I mean, the implied postcondition of setAge(age: number) is assert(this.age == age). If you change that, then you can adjust the precondition so that both Person and Employee can keep the method.

For Person:

    setAge (age: number) {
        assert(true); // precondition
        if (age >= 0) { 
            this.age = age;
        }
        assert(this.age >= 0); // post-condition
    }

For Employee:

    setAge (age: number) {
        assert(true); // precondition
        if (age >= 18) { 
            this.age = age;
        }
        assert(this.age >= 18); // post-condition
    }

With the above I have:

  • Not strengthened the precondition (They are both the same.)
  • Not weakend the postcondition (in fact I have strengthend it.)
  • Preserved the invariant (the implied invariant is age >= 0

You will notice that the real change in the above is that setAge is no longer a demand, rather it is a suggestion and that makes all the difference.

In general, the way to ensure that your classes conform to LSP is to give them methods where you tell them about events that happened outside the object rather than telling them to perform actions inside. This is why in many systems you see methods called something like xDidHappen instead of doX.

When you tell an object that x happened, you are leaving it up to the object to decide what to do in response to that. This is exactly what objects are supposed to do. When you tell an object to "do X" (or in this case "set X") you are removing agency from the object. It is no longer in control of its own state. Something outside of the object now is calling all the shots. That's not OO; That's imperative programming in OO clothing.

Yes it breaks LSP. Specifically the setAge method is a problem. The solution IMO is to remove the method or change its postcondition.

By that I mean, the implied postcondition of setAge(age: number) is assert(this.age == age). If you change that, then you can adjust the precondition so that both Person and Employee can keep the method.

For Person:

    setAge (age: number) {
        assert(true); // precondition
        if (age >= 0) { 
            this.age = age;
        }
        assert(this.age >= 0); // post-condition
    }

For Employee:

    setAge (age: number) {
        assert(true); // precondition
        if (age >= 18) { 
            this.age = age;
        }
        assert(this.age >= 18); // post-condition
    }

With the above I have:

  • Not strengthened the precondition (They are both the same.)
  • Not weakend the postcondition (in fact I have strengthend it.)
  • Preserved the invariant (the implied invariant is age >= 0

You will notice that the real change in the above is that setAge is no longer a demand, rather it is a suggestion and that makes all the difference.

In general, the way to ensure that your classes conform to LSP is to give them methods where you tell them about events that happened outside the object rather than telling them to perform actions inside. This is why in many systems you see methods called something like xDidHappen instead of doX.

When you tell an object that x happened, you are leaving it up to the object to decide what to do in response to that. This is exactly what objects are supposed to do. When you tell an object to "do X" (or in this case "set X") you are removing agency from the object. It is no longer in control of its own state. Something outside of the object now is calling all the shots. That's not OO; That's imperative programming in OO clothing.

Yes it breaks LSP. Specifically the setAge method is a problem. The solution IMO is to remove the method or change its postcondition.

By that I mean, the implied postcondition of setAge(age: number) is assert(this.age == age). If you change that, then you can adjust the precondition so that both Person and Employee can keep the method.

For Person:

    setAge (age: number) {
        assert(true); // precondition
        if (age >= 0) { 
            this.age = age;
        }
        assert(this.age >= 0); // post-condition
    }

For Employee:

    setAge (age: number) {
        assert(true); // precondition
        if (age >= 18) { 
            this.age = age;
        }
        assert(this.age >= 18); // post-condition
    }

With the above I have:

  • Not strengthened the precondition (They are both the same.)
  • Not weakend the postcondition (in fact I have strengthend it.)
  • Preserved the invariant (the implied invariant is age >= 0

You will notice that the real change in the above is that setAge is no longer a demand, rather it is a suggestion and that makes all the difference.

In general, the way to ensure that your classes conform to LSP is to give them methods where you tell them about events that happened outside the object rather than telling them to perform actions inside. This is why in many systems you see methods called something like xDidHappen instead of doX.

When you tell an object that x happened, you are leaving it up to the object to decide what to do in response. This is exactly what objects are supposed to do. When you tell an object to "do X" (or in this case "set X") you are removing agency from the object. It is no longer in control of its own state. Something outside of the object now is calling all the shots. That's not OO; That's imperative programming in OO clothing.

Source Link
Daniel T.
  • 3.1k
  • 20
  • 24

Yes it breaks LSP. Specifically the setAge method is a problem. The solution IMO is to remove the method or change its postcondition.

By that I mean, the implied postcondition of setAge(age: number) is assert(this.age == age). If you change that, then you can adjust the precondition so that both Person and Employee can keep the method.

For Person:

    setAge (age: number) {
        assert(true); // precondition
        if (age >= 0) { 
            this.age = age;
        }
        assert(this.age >= 0); // post-condition
    }

For Employee:

    setAge (age: number) {
        assert(true); // precondition
        if (age >= 18) { 
            this.age = age;
        }
        assert(this.age >= 18); // post-condition
    }

With the above I have:

  • Not strengthened the precondition (They are both the same.)
  • Not weakend the postcondition (in fact I have strengthend it.)
  • Preserved the invariant (the implied invariant is age >= 0

You will notice that the real change in the above is that setAge is no longer a demand, rather it is a suggestion and that makes all the difference.

In general, the way to ensure that your classes conform to LSP is to give them methods where you tell them about events that happened outside the object rather than telling them to perform actions inside. This is why in many systems you see methods called something like xDidHappen instead of doX.

When you tell an object that x happened, you are leaving it up to the object to decide what to do in response to that. This is exactly what objects are supposed to do. When you tell an object to "do X" (or in this case "set X") you are removing agency from the object. It is no longer in control of its own state. Something outside of the object now is calling all the shots. That's not OO; That's imperative programming in OO clothing.