I have some troubles with understanding these two principles. This is a bit long-read-question, so be patient.
Lets assume that we have a class
abstract class Shape {
abstract void onDraw();
}
and interface
interface SideCountable {
int getSidesCount();
}
And then we create two children classes
class Quad extends Shape {
@Override
public void onDraw() {
//some draw logic here
}
}
class Triangle extends Shape {
@Override
public void onDraw() {
//some draw logic here
}
}
And now we will make Shape
implements SideCountable
abstract class Shape implements SideCountable{
abstract void onDraw();
}
and make changes in children classes
class Quad extends Shape {
@Override
public int getSidesCount() {
return 4;
}
@Override
public void onDraw() {
//some draw logic here
}
}
class Triangle extends Shape {
public int getSidesCount() {
return 3;
}
public void onDraw() {
//some draw logic here
}
}
And create test function for this structure (following LSP) :
public void printSidesCount(List<Shape> shapes) {
for(int i=0; i < shapes.size(); i++) {
System.out.println(shapes.get(i).getSidesCount());
}
}
And here I want to stop because actually in the next step I was stucked.
What if we'll create a third class Circle
?
class Circle extends Shape {
public int getSidesCount() {
return ???;
}
@Override
public void onDraw() {
//some draw logic here
}
}
Circle has no sides, so implementation of SideCountable
for this children sounds ridiculous. Ok, we can move implementation to Quad and Triangle only, but in this case LSP will not working anymore. Can someone describe what the best way I should to do?
SideCountable
in Shape
class and return 0 for Circle
and break interface segregation principle?SideCountable
to Quad
and Triangle
and break LSP principle?First of all, your method printSidesCount
only needs to know that the list contains SideCountable
objects. So giving its parameter the type List<Shape>
is more specific than necessary. Give it a List<SideCountable>
instead:
public void printSidesCount(List<SideCountable> sideCountables) {
for(int i=0; i < (); i++) {
System.out.println(sideCountables.get(i).getSidesCount());
}
}
Or even List<? extends SideCountable>
which means "a list of some arbitrary unknown type that implements SideCountable
":
public void printSidesCount(List<? extends SideCountable> sideCountables) {
for(int i=0; i < sideCountables.size(); i++) {
System.out.println(sideCountables.get(i).getSidesCount());
}
}
If not all shapes have a countable number of sides, then class Shape
should not implement interface SideCountable
. Instead, make classes Quad
and Triangle
implement interface SideCountable
besides extending class Shape
:
class Quad extends Shape implements SideCountable {
// ...
}
class Triangle extends Shape implements SideCountable {
// ...
}
And make class Circle
extend Shape
but not implement SideCountable
:
class Circle extends Shape { // Not SideCountable
// ...
}
When you do it like this, the type system will help you:
List<Quad>
or List<Triangle>
to printSidesCount
since those types implement SideCountable
List<Circle>
to printSidesCount
, which is good because trying to do this would not make sense for a list of circles