Interface with two different implementations
I have an interface Person
and I have 2 classes Female
and Male
that implement the interface.
For the Female class, I have a method getPregnancyMonth
that my Male class does not have. Adding that method to my interface Person
becomes a problem as my Male
class now needs to inherit that method from the interface; but the male would never be pregnant.
What would be a solution, do I need to extend
Person instead of implement
?
EDIT: Sorry if my question wasn't clear. In this case I have added both get
/set
methods from the Male
and Female
classes to the interface.
static void Main(Form form) {
Person person = Factory.createPerson(form.getGender());
person.setName(form.getName());
if ("F".equals(gender)) {
person.setPregnancyMonth(form.getPregnancyMonth());
}
}
My question开发者_JS百科 is, since my interface has getPregnancyMonth
, my male has to add that method to the concrete class to implement the interface. Is there a way to avoid this?
getPregnancyMonth
Should not be in the interface of Person
.
Personally...no pun intended...I think you should create Person
as an abstract class as female and male will share a lot of the same attributes and functions.
Then you could create Female and Male interfaces that reflect unique functionality for each sex.
There should be no need to move that method into the Person
interface. The main trick is that when it comes time to execute the getPregnancyMonth()
you must ensure you're dealing with a Female
instance instead of just a Person
instance.
For example, if you need to process a bunch of Person
objects that do not require any particular treatment, a method can be created to do so easily:
public static void processPeople(List<Person> people) {
for (Person p : people) {
p.someMethod();
}
}
However, when you must deal with the getPregenancyMonth()
method, you must ensure that your method only accepts Female
instances:
public static void checkBirthSchedule(Female girl) {
girl.getPregnancyMonth();
...
}
In other words, as you are trying to get at, keep things as abstract as you can in higher up interfaces, but ensure you go to a more concrete type when needed. Using a technique like making a separate method with a more specific type of Person
saves you at compile time from getting ClassCastException
's, which is very nice.
In my opinion, getPregnancyMonth
is not generic enough to be in a Person
interface. It should probably be defined in the Female class only.
You may also define a second interface containing that getPregnancyMonth
. Female will implement both.
Don't add getPregnancyMonth
to Person
. As you've discovered, that does not make much sense.
In object oriented programming, a base class/interface should (ideally) only contain the details that are common to each of its subclasses - since getPregnancyMonth
is not common to both subclasses (it doesn't make any sense for a Male
), it shouldn't be in the Person
interface. Again I stress - this is talking about ideals.
As other answers have suggested, try to avoid the problem altogether, if you can. For example, you can use instanceof
to detect whether a given Person
is a Male
or Female
, and only call getPregnancyMonth
if the Person
is a Female
.
Edit, in response to comment: the use of a factory method as described is largely pointless. If we ignore those people who don't wish to be labelled with a "normal" gender, you'll only ever create a Male
or a Female
object - you're much better just having public static Female createFemale ( )
and public static Male createMale ( )
methods in your Factory
. This way, you'll avoid all this trouble you've been having. It'll also get rid of the string being used like a enum (i.e. "male", or "female" as an argument to getPerson
), but that's a whole different matter...
If you still want to use a combined factory method, you can cast the result as a Female
:
Female female = (Female) Factory.getPerson("female");
female.getPregnancyMonth ( );
Or, if you have a Person
that may or may not be a Female
, you can cast that too:
if (person instanceof Female)
{
Female female = (Female) person;
female.getPregnancyMonth( );
}
I can't say I'm overly fond of either of those approaches though - they have their place, but there's usually a better way.
Implement/Extend doesn't really matter here. You can either have isPregnant() always return false for Male, or you can just push the method directly into Female. Seeing as you think it is ridiculous that a Male would be pregnant, you would design your code such that it only calls isPregnant() on a Female, not just any person.
BTW By this I don't mean throw in an if(person instanceof Female), design your heiarchy as to avoid this.
It sounds like a broken object hierarchy, but taking it as stated, your interface needs two methods: isFemale() and getPregnancyMonth(). Then Male.getPregnancyMonth() throws an UnsupportedOperationException. A better approach would probably be to design your model so that a Male could never be put into a position where his pregnancy month would be asked.
精彩评论