I have a design problem in a program, caused by the fact that an abstract
base class
has one method with one positional (and thus optional) argument.
Let's say this class is A
, and the method is void f(x, [y]);
. Now, y
is optional because I already know that some of the subclasses of A
will use it, some of them will not.
The actual problem is a violation of the Liskov substitution principle: in the subclasses that require y
I have to throw an exception if y
is not provided, whereas in A.f
(which is unimplemented) I'm not throwing any exception.
The design of A
is also bad because I'm providing a method f
which some subclasses really need it, some of them need a slightly different version. Clearly, I should design my interfaces as small as possible (interface segregation).
This is actually a general question and not only related to Dart.
So, how to handle optional parameters in order not to violate the Liskov substitution principle? And, in particular, how would you handle my situation, so that I don't also violate the interface segregation principle?
The only plausible solution (to my particular problem) I see right now is to make current subclasses that extend A
and that actually require y
in f
to actual extend (or implement, if A
is actually an interface) another base class with a method f(x, y)
, that is where both parameters are required. But the question/problem of how to handle optional parameters still remains!
I don't really see the connection between the LSP and the optional parameters. You'll violate the principle depending on your code, and what you do with the params not because of the options the language provides to you.
In your example I'd say that it's at least arguable you'd be violating the LSP since
A
is abstract (thus you cannot instantiate it) and you cannot callf
directly since it's not implemented.Let's omit that part and say you have class
A
and subclassB
(both concrete) and both implement methodf(x, [y])
.Then asume you have
a
as instance ofA
andb
as instance ofB
. Given any value for [x
,y
] if anywhere you usea.f(x,y)
you can use((A)b).f(x,y)
and you get the exact same result then you are not violating the LSP.Anyway, if you feel you might be violating LSP, then you have to ask yourself if you really need the hierarchy at all. Instead of declaring
B
as subclass ofA
, maybe you can have an interface implemented byA
andB
with the common methods and move code you have inA
used byB
to another class you can call fromA
andB
(see https://en.wikipedia.org/wiki/Composition_over_inheritance).