Based on what I have been reading about the Liskov Substitution Principle, I understand that a square and rectangle class cannot be a part of the same inheritance tree.

I would like to apply these ideas to a Folder and a File, as they commonly exist on disk. Is there a property of one or the other or both which would force a conclusion they too should not be part of the same inheritance tree according to Liskov?

What are some properties we could consider?

  1. The data. Files consist of bytes. However, Folders can be considered to consist of the bytes of the files they contain.

  2. Access to both is defined by permissions

I suppose the property where inheritance breaks down would be one concerning containment. A folder contains files. A file does not. However, if one considers that a file is just a collection of bytes, then a folder could be considered to be just the collection of bytes consisting of the files in the folder.

Is there a property of a file or a folder that requires us to maintain two inheritance trees?

  • 2
    Can you clarify a bit more please? A Square is a Rectangle. Thus I hardly understand why it shouldn't be implemented as inherited class. Mar 2 '20 at 18:37
  • 1
    A square is a rectangle with constrained dimensions. Usually descendant classes add new behavior, not subtract it from the base class. The rationale for an LSV violation is described here. Mar 2 '20 at 18:49
  • 2
    "a square and rectangle class cannot be a part of the same inheritance tree" - this is only true if the base type needs to be defined in a way that conflicts with the subtype (e.g., if it's necessary that the rectangle abstraction be defined in such a way that Width and Height change independently). If that's the case, than a Square is not a Rectangle - by this behavioral classification criterion (which is what matters in OOP). If there is no some such constraint, a Square can be considered and substituted for a Rectangle, and there's no violation of Liskov. So it depends on the model. Mar 2 '20 at 19:46
  • 1
    For example, if you only have read-only properties (and maybe some other methods that don't result in conflicting semantics between the two, e.g. draw(location), and if that's all you need in your abstract interface, then it's fine. In other words, it's not some universal property of rectangles and squares that results in a violation of Liskov SP, but what the base type is defined to be, and what its methods mean and how they are expected to be used. Mar 2 '20 at 19:55
  • 1
    Teaching and learning OOP by means of real-life analogies is just a very bad idea, as the square vs. rectangle discussions has been showing for decades now. @FilipMilovanović has put it very nicely. Mar 3 '20 at 6:56

The "substitution" from LSP does not mean that the subclass ultimately behaves same as way the super class. It cannot do it always anyway, simply because it is another class.

What it does mean is that the contract which is important to the code it is being injected to does not change. So, for the Folder and File there are a lot of common:

  • Both have parent directory
  • Both have name
  • Both may exist of not
  • Both have permissions and ownership
  • Both can be moved, copied to another directory
  • Both can be renamed
  • Both can be deleted

So for a code which only uses the properties and methods, you could substitute one with the other without violating the LSP.

The data. Files consist of bytes. However, Folders can be considered to consist of the bytes of the files they contain.

This is rather unorthodox, I believe it would not be reasonable to do so. Though I remember in some Unix-like OS I could read a directory as a text file which were listing files.

  • Is it unorthodox enough to conclude that folders and files should not be considered part of the same inheritance tree? I cannot imagine having a folder and not have an interface that would try to access files contained within it.
    – ericg
    Mar 3 '20 at 2:29
  • @ericg more realistically there would be some FilesystemEntry which both File and Folder inherit.
    – max630
    Mar 3 '20 at 6:02
  • A lot of those common properties aren't really common for certain corner cases. For example, a file must have a parent directory, a directory may have one; a deletion operation can be performed on both, but different errors can occur, which makes the contract slightly different. Mar 3 '20 at 6:59
  • @ericg Just as in the composite pattern: the component interface exposes child access functions, but leaf nodes just behave as if they had no children. No LSP issue if you formulate preconditions, postconditions and invariants accordingly
    – Christophe
    Mar 3 '20 at 7:44

If you are creative enough you can make one descend from the other and satisfy Liskov. But you would have to bend semantics, like with square and rectangle. And that would not benefit your model or make it easy to work with by folks who have a good understanding of what a square, a rectangle, a file or a folder is.

You could argue that a square is a simple rectangle that has 4 sides with just one side length specification x. Then rectangle could descend from it and have an additional side length specification y. Then if you treated a rectangle like a square you would be OK. Whenever you draw it you would just use x for all four sides and that would make a square.

But that would be a technical trick to make ends meet without making much sense and it is the same with files and folders. If you take meaning of properties into account (rather than just treating them as variables that may have different meaning depending on the context), this would fall apart. A good model is serious about semantics, if it is not it will be a pain to work with. So whatever the answer to your question is, you should not be smart just to fix something that does not fit naturally.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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