81

I usually try to make sure my object instances comply with the Liskov Substitution Principle, but I've always wondered is do people think LSP should apply to constructors too?

I've tried googling for this but I haven't been able to find any strong opinions either way.

I should note that most of my coding is in Ruby, but I sometimes find that my subclass constructors are slightly different from the parent class. They take the same base set of arguments, and often extra args. Sometimes this also happens with other class methods.

In the back of my head this has always felt like an LSP violation, but I wanted to see if anyone else feels this way too.

dkubb
  • 2,126
  • 18
  • 16
  • If you are wondering whether instances comply with the LSP, then constructors are irrelevant (assuming no metaprogramming), they are not methods. But if you are passing around the classes/constructors themselves around, like in factory methods, then the LSP may easily be applicable to those. – Bergi Nov 06 '22 at 20:02

3 Answers3

90

No, when you use a constructor you know you are dealing with the subtype. This allows you to have preconditions not required for the parent constructor such as other parameters. This is why in most languages the constructor name is that of the class being created.

A good example of how this is that a ColoredSquare could be a proper subtype of Square, but requires an extra parameter: color. If you couldn't do things like this subtypes would be much less useful.

In some sense, the constructor isn't really part of the type: it is a function that returns an element of that type. Thus, defining a new constructor for a subtype, doesn't break LSP.

Ella Sharakanski
  • 2,683
  • 3
  • 27
  • 47
Philip JF
  • 28,199
  • 5
  • 70
  • 77
  • 11
    "when you use a constructor you know you are dealing with the subtype." Thanks. This is a great point. – Chance May 25 '11 at 15:28
  • It's probably important to make a distinction between constructors, static factory methods, factory methods that are instances of the type being produced (most notably `Clone`), and other factories. Static factories may produce different types of objects which derived from the return type, but the LSP should apply to all objects produced; there is no requirement, however, that derived classes support the same static methods as the base class. If a class supports a factory method like `Clone`, one should honor the LSP and support similar semantics in subtypes. – supercat Dec 23 '11 at 15:21
  • For factories which return things of other types, the item returned from a derived factory should be usable as an instance of an item that would be returned from the base factory. So if `CarFactory` has a `MakeCar()` method which returns `Car`, which in turn supports a `Drive()` method, one should be able to call `SomeCarFactory.MakeCar().Drive()` even if `SomeCarFactory` is really a `FordFactory` (which inherits `CarFactory`) which returns a `Ford` (which inherits `Car`). – supercat Dec 23 '11 at 15:28
  • https://medium.com/javascript-scene/how-to-fix-the-es6-class-keyword-2d42bb3f4caf – backdesk Apr 13 '15 at 12:59
18

Definitely No.

Constructors are normally specialized for subtypes. Trying to apply LSP to constructors would be like saying subtypes can't have added specific methods or members. But the restriction is only the other way around.

And I also agree with Philip, constructors are not really part of a type (and in some languages you can easily use other factories instead of constructors). Using smalltalk terminology you would say constructors are methods of meta-classes.

No violation of LSP here, it only applies to instance methods, not to class methods (constructors or any other class methods).

kriss
  • 23,497
  • 17
  • 97
  • 116
2

This is sort of an opinionated question but the way I tend to write mine is such that the extra parameters have no real bearing on changing functionality. By that I mean that when my constructor requires an extra parameter in a subclass it is to maintain the standard functionality (but doing different underlying things) this way lets say I create ClassA = new ClassB(with some args); then functionality is the same whether I do this or ClassA = new ClassA(); and I usually use some sort of Factory method to create them so it's seamless in how they work. Again this is just how I do things and is in no way the absolute correct way to do things.

Jesus Ramos
  • 22,940
  • 10
  • 58
  • 88