0

I have a parent class that has a public function that is final. Is it possible to disallow access to this method in a derived class? Other classes that derive from the parent class need to still be able to call it.

class ParentClass {
    final public function test() {

    }
}
class ChildClass extends ParentClass {

}
class ChildClass2 extends ParentClass {

}

$child = new ChildClass();
$child2 = new ChildClass2();
// make this illegal:
$child->test();
// but not this:
$child2->test();
Leri
  • 12,367
  • 7
  • 43
  • 60
DudeOnRock
  • 3,685
  • 3
  • 27
  • 58
  • 1
    @Ibu: I don't want to disallow calling of that method in all derived classes. I do want to disallow it in one very specific instance, where calling that method doesn't make any sense and would cause unwanted behavior. Other derived classes need to be able to call the function. – DudeOnRock Feb 08 '13 at 06:36
  • Just use private keyword – Php Geek Feb 08 '13 at 06:36
  • why did you make the method final? remove the final and override it in the unwanted class. Or you can add another layer of abstraction. – Ibu Feb 08 '13 at 06:40
  • @Ibu: The function has special knowledge of the inner workings of the parent class. It needs to be callable from child class instances. I don't want authors of derived classes to be able to change the function's behavior. – DudeOnRock Feb 08 '13 at 06:46
  • disallowing this would violate LSP. If you need that, it's a sure sign of your inheritance hierarchy being broken. – Gordon Mar 06 '13 at 12:06

2 Answers2

4

When you inherit from class it means that derived class is a class that inherits. It means that it has exact the same base functionality that can be expanded, overriden and so on. If it makes no sense that derived class had method that is defined in parent class means that derived class is not a class that it derives.

For real world example let's say you have class FlyingMachine that has method Fly and Move, it would not be logical if you derived class Car from FlyingMachine because cars don't fly, while deriving class AirPlane from mentioned base class is perfectly fine. But it would be logical if you had base class Machine that had method Move and car was derived from this class Car : Machine (car is a machine, right?), FlyingMachine is a machine too so FlyingMachine : Machine is perfectly fine and since air-plane is a flying machine AirPlane : FlyingMachine makes sense.

For your given example this should be something like:

abstract class VeryBaseClass {
    public function MethodThatIsLogicalForEveryDerivedClass() {}
}

abstract class BaseClass extends VeryBaseClass {
    public function SomeOtherFunctionThatMakesSenseOnlyForSomeClasses() {}
}

class ClassThatShouldNotHaveSpecificFunctionDefinedInBaseClass extends VeryBaseClass{}

class ClassThatShouldHaveSomeOtherMethod extends BaseClass {}

Edit:

If there's a functionality that several derived classes need but for some of them it should be public and for some of them not. You should define in base class this method as protected (derived class can access one but from outside impossible). And in derived class that require that this method was public make another method and call parent's method. E.g.:

abstract class Car { protected function Shift() {} }

class ManualCar extends Car {
    public function Shift() { parent::Shift(); }
}

class AutomaticCar extends Car {
      //Some implementation of automatic car that uses protected Shift method
}

Little demo

Leri
  • 12,367
  • 7
  • 43
  • 60
  • 1
    Lets say the Car class has a shift() method. Now I derive an AutomaticCar class and a ManualCar class. I know I could have made the shift() method only be part of the ManualCar class, but automatic cars have to shift too. It is just not the driver who has to deal with it. The method I have is called by other functions inside the class (lets say the board computer can shift), but it would be bad if the driver of the car tries to shift. Is there no language construct that would let me "hide" the shifting option from the driver of the automatic car? – DudeOnRock Feb 08 '13 at 07:13
  • @DudeOnRock You can achieve it with access modifiers. I'll edit my answer. – Leri Feb 08 '13 at 07:16
  • With your edit the function is not final any more, which I was hoping to retain. – DudeOnRock Feb 08 '13 at 07:38
  • @DudeOnRock You can make it final without any problem: [Demo](http://codepad.viper-7.com/sdLmtr) – Leri Feb 08 '13 at 07:42
  • oh, I see, I think you got me going on the right track. I think my specific problem could live with the shift()-like method being protected final. – DudeOnRock Feb 08 '13 at 07:55
  • @DudeOnRock Glad that I could help you. If you think that this is right answer, don't forget to accept one. ;) – Leri Feb 08 '13 at 07:58
1

How about checking for that class in the method, and throwing an exception if it's the disallowed class:

class ParentClass {
    final public function test() {
        if (get_class($this) == "ChildClass")
            throw new Exception("ChildClass cannot call test()")
    }
}

NB: this is fairly evil

Carson Myers
  • 37,678
  • 39
  • 126
  • 176
  • 1
    If I had 100 class that should not have method `test`? I'd better kill myself than using this. :D This is really evil and dirty. ;) – Leri Feb 08 '13 at 07:23
  • Evil, but I guess it would solve my problem. Not in a very encapsulated way, but what I have right now is not ideal either. – DudeOnRock Feb 08 '13 at 07:57
  • If you have 100 classes that shouldn't have it then you'd better be refactoring everything. But... if everything works, and it's just this one class, and test() causes some hard-to-find intermittent bug when called by ChildClass, and you just want to make sure that no future programmer does that... then maybe a little evil is okay. – Carson Myers Feb 08 '13 at 08:08