0

Is it code smell to make an abstract child class implement a method, which overrides a parent method, whose only purpose is to call another abstract method? I want to make sure that anyone who implements my abstract class is aware that there needs to be special handling to override the functionality of a parent method. And making the parent method call my abstract method seems to be the best way to do that.

However it also feels redundant, someone could just override the parent method directly.

Here is some example code. It is totally contrived because I don't want to reveal what kind of code I'm actually working on. Please pay less attention to the example code because it may not be the best.

I'm wondering if this is ok? Or is it a symptom of code smell and I should re-work the code somehow.

<?php

abstract class Mammal {
    public function move() {
        // ... a bunch of complicated logic implementing common walk logic
    }
}


abstract class NonWalkingMammal extends Mammal {
    // Override the parent method to implement special handling defined by the abstract getMove() method.
    public function move() {
        return $this->getMove();
    }

    // NonWalkingMammals must implement logic for their movement
    abstract protected function getMove();
}

class Bat extends NonWalkingMammal {
    protected function getMove()
    {
         // ... flying logic
    }
}

class Whale extends NonWalkingMammal {
    protected function getMove()
    {
         // ... swimming logic
    }
}
7
  • Why is NonWalkingMammal a type? Who would ever need something to not do something (not walk). Would it not make more sense to have abstract class Mammal with an abstract move method, and abstract class WalkingMamal with a move that's implemented by walking?
    – bdsl
    Feb 7, 2023 at 22:42
  • Of course depending on the application this is still likely to be problematic. What if you need to model both the Walking and the Fur-Growing abilities of a mammal? PHP doesn't allow you to inherit from two classes at once.
    – bdsl
    Feb 7, 2023 at 22:47
  • @bdsl As I stated this example is contrived and not a very good one. The point is I have a bunch of subclasses that should just inherit a default behavior, but some special few (very few) classes which should do something very different. Feb 7, 2023 at 22:48
  • @bdsl yes that makes sense regarding your last comment. In my case that shouldn't be a problem because they are mutually exclusive. Feb 7, 2023 at 22:50
  • 1
    The basis of your abstract question seems to be missing the woods for the trees. Whether or not overriding a specific method is the correct approach should not hinge on what the body of this overridable method currently contains. That's not a comment on your specific example, but rather an observation about the generalized question you're asking because of it.
    – Flater
    May 2, 2023 at 1:09

3 Answers 3

2

I'm going to go with "yes"

It seems obvious from the need to completely replace the functionality in multiple classes that NonWalkingMammal should be the base class, then WalkingMammal can inherit and override with the walking logic. SwimmingMammal can make it swim etc etc

by switching things around you will improve your codebase.

2
  • The base class should just be Mammal. We don't mostly name things by what they don't do, and it doesn't make sense for WalkingMammal to be a special type of NonWalkingMammal.
    – bdsl
    Feb 7, 2023 at 22:43
  • 1
    sure, had to use the names from the question to make sense i thought
    – Ewan
    Feb 8, 2023 at 15:31
1

The whole point of abstract methods is that they must, in general, be defined by the implementing class. Your entire implementation is to call another method, which is...OK, though not great as a general rule - unless maybe that code is used elsewhere. There are cases where the entire implementation of a method is to just call another method, but they tend to involve different method signatures using the same underlying implementation (e.g., adding to the end a collection and inserting to a collection).

In your contrived example (unless you post your actual code, this is what we have to go on), it serves no purpose whatsoever, and I wonder why you wouldn't just implement the swimming, flying, etc logic in the child class's move() method (it is appropriately named, after all, and not named something like walk() which is specific to walking mammals). If you're going to use the base class Mammal everywhere, your calling code won't even know of the existence of the getMove method on some of the child classes. Just leave it off and simplify.

6
  • I did mention that my concern was it was redundant to use a secondary abstract method. My reasoning is that another programming who implements my NonWalkingMammal class is aware of the need to implement special logic for movement (and not just rely on the default logic for walking). Regarding your last sentence, there is no reason for anyone else to be aware of getMove() as it is protected and only seen within the class being impelmented. Feb 7, 2023 at 20:13
  • I should also mention that a vast majority of classes (more than 10) implement the parent abstract class and use the default move logic, and only 1 or 2 implement the child abstract class. Feb 7, 2023 at 20:18
  • @CaveJohnson "My reasoning is that another programming who implements my NonWalkingMammal class is aware of the need to implement special logic for movement (and not just rely on the default logic for walking)" - You're relying on people's memory. 2 weeks, or 2 years from now, they are not going to remember. They are going to inherit the class, maybe run some automated tests, and it will work, because Mammal provides a default implementation. They'll assume it's fine. They may discover the problem 5 months later. But this way, they'll fail fast and get an error. 1/2 Feb 7, 2023 at 21:45
  • That said, this may be an indication that this default implementation is too high up in the hierarchy (maybe you need a WalkingMammal subclass, or that this default behavior may need to be in a different "helper" class that you can compose in, rather than inherit). 2/2 Feb 7, 2023 at 21:45
  • 1
    @bdsl That is a good point. And after hearing from everyone here I think I should go with making the move method abstract and defining a WalkingMammal instead. Feb 7, 2023 at 23:08
0

If you need to enforce call order inheritance is not the way.

It will be super unobvious to people who fail to satisfy your invariants.

Try maybe doing a trick of returning special value from First method that's then mandatory argument on second method. In method body it would be ignored, but each callee would need to always call first method to even call second.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.