0

Recently I read about ISP and wanted to implement it into my project. While implementing my design I think I found a flaw which violates LSP but I'm not sure.

Given I have a game project, in which players can walk around and use items. I currently use the interface Item as a base, which has the subclass Weapon, which has the subclass Gun. Before, all the implementation of these interfaces used to have a dependency to the Player interface. Like so: enter image description here When implementing the ISP I had the idea to include smaller interfaces, tailored for each abstraction. This would include an ItemUser, WeaponUser and GunUser like so: enter image description here

However doesn't this design violate the LSP since I override the getUser method with a different abstraction in each subclass? If yes, how could I prevent this while still using the smaller interfaces of ISP?

3
  • (1) The LSP is the only aspect of "SOLID" that's a mathematical fact and not just an opinion. (2) Your example doesn't break LSP because all getUser() methods satisfy the Item:getUser() interface. (3) However, the more granular User interfaces are likely useless because they'd have to be accessed by downcasting. Downcasting is often an indication that you've modeled the system badly, or that the problem is not suitable for OOP. (4) Game programming is different, often featuring complex dynamic non-hierarchical relationships. It is often inappropriate to encode game logic in the type system.
    – amon
    Jan 25 at 22:23
  • your diagram seems to indicate that the method signature for getUser changes. ie GunUser Gun.GetUser() rather than ItemUser Gun.GetUser()? if so i think that does violate the LSP, but not sure what language lets you do it
    – Ewan
    Jan 26 at 9:10
  • 3
    The problem you're running into is that you are trying to use the rules of a type system to capture the rules of a game, but there is no requirement that those two sets of rules be anything like each other. I wrote a series of articles on your problem many years ago; it starts here: ericlippert.com/2015/04/27/wizards-and-warriors-part-one Jan 30 at 19:45

2 Answers 2

7

This is a covariance of a method's return type. This is compliant with LSP:

  • The preconditions to getUser() are not changed and in particular, they are not strengthened.
  • The postconditions of getUser() are not weakened. In fact they are strengthened, since the return type is more specific.
  • The invariants appear unchanged.
  • The history constraint seems unaffected in your example.

Unrelated: the considered design will lead to deep hierarchies of interrelated objects. While SOLID will help you making it as good as possible it'll still be a complex solution with some rigidity. This is why games usually prefer flatter designs such as the entity component system architecture.

0

That's linear hierarchy, it is safe. The answer is no.

If you wonder how implementing ISP could go around LSP imagine following interface hierarchy:

Animal <- WalkingAnimal
Animal <- JumpingAnimal

If interfaces aren't properly defined replacing Animal with one of its sub-types might go around LSP since what WalkingAnimal does could be unsupported by JumpingAnimal and the other way around.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.