empty interface(abstract class) for logical grouping of objects to avoid LSP violations
In the real world a square is a rectangle, but to a开发者_开发技巧 program this is not the case(LSP principle). is it considered an anti-pattern / poor programming to create a blank interface to logically group objects together? My thought is with a blank interface it would be impossible to violate the LSP
at the level directly beneath it. Contrived example below.
Abstract class Canine{ }
Dog extends Canine {…}
Wolf extends Canine {…}
Depends what it's for.
An empty interface imposes no syntactic requirements, since there are no methods to implement.
But the interface might be documented to impose semantic requirements, especially if Canine
itself inherits from Canoidea
or Carnivora
or whatever. This makes the empty interface potentially useful since it provides guarantees of its own. All Carnivora have ears[*], but Canoidea are characterized in part by some property of their ears that isn't easily expressed as simply as adding a method, so implementing the interface becomes a promise: "in addition to being a Carnivora, my ears behave like that".
That means it's no longer impossible to violate the LSP. You could accidentally write a subclass with an overridden function that returns 12, which is permitted for Carnivora, but the semantics of Canine say that it won't return more than 10.
Interfaces that add only semantics aren't necessarily great to work with, since there are certain mistakes that it's easy to make. An example is the difference between InputIterator
and ForwardIterator
in C++ (although of course those are template concepts, not inherited interfaces). If you tag your class with the wrong one, the compiler will never notice. But if you tag your class with RandomAccessIterator
when really it's only a ForwardIterator
, then the compiler will notice, when someone tries to use operator+
on it, since operator+
is part of RandomAccessIterator but not ForwardIterator.
An empty interface with no semantics is meaningless, and hence probably useless. The only use for it I can think of is as a kind of variant - some dodgy code could test whether the object is an instance of "Dog" or "Wolf" (or something else), and do different things accordingly. That's probably not a good use since it's probably not good code in the first place. You might as well use an ultimate superclass like Object
, if available, since either way the code is going to have to cope with types that it doesn't recognise and can't handle. "What on earth is a Fox, I can't use a Fox here" is no better or worse than "What on earth is a Vector, I can't use a Vector here", so if what you understand is dogs and wolves, there's no real advantage to accepting a Canine
compared with accepting an Object
. They're both variants which allow Dogs, Wolves, plus anything else someone chooses to slot into the hierarchy.
[*] Yes, even seals. They just don't have pinnae.
No it will not break LSP since there's nothing that can be done with the base, and therefore it's not possible for any of the classes to violate LSP.
I do sometime use empty interfaces to group classes together. I do so to show some intent. For instance, I got an empty interface called IMessage
which is used to show that the purpose of a class is only to be handled as a message in my pub/sub framework. (And only classes which implements the interface are allowed to be transported in it).
For me, it helps to make sure that classes follow single responsibility principle. (compared to a message class which do not implement any interfaces)
In your scenario how does this help us. We have an abstract class Rectangle? What is it that prevent us from claiming that Square extends Rectangle?
精彩评论