Jump to content

Extending classes and methods question


NotionCommotion

Recommended Posts

I have a method in several child classes which are basically the same, but have some small differences, and I would like to have most of the script in the parent class.  The following would basically work, but I would rather execute $this->bla_a() before echoing anything.  How would you recommend doing so?  Thanks

$controller=new ChildController1();
$controller->doSomething();


class ParentController {


 protected function doSomething($extra)
 {
  header('Content-Type: application/json;');
  $id=(isset($_POST['id']))?$_POST['id']:0;
  $other=new otherclass();
  $x=$other->bla($id,$extra);
  $flag=$x['flag'];
  unset($x['flag']);
  echo(json_encode($x));
  return $flag;
 }
}


class ChildController1 extends ParentController {


 public function doSomething($extra='dummy')
 {
  if(parent::doSomething('usethis1')){
   $this->bla_a();
  }
 }
}


class ChildController2 extends ParentController {


 public function doSomething($extra='dummy')
 {
  if(parent::doSomething('usethis2')){
   $this->bla_b();
  }
 }
}
Link to comment
Share on other sites

1. Making $extra optional with a default value needs to be handled below the parent class. If you use "dummy" a lot, that hints at those child classes having something more in common than simply that one argument, which then suggests you create another class between the parent and child which redefines those methods.

 

2. If the "if something then call method" pattern is very common, and kinda even if it isn't, then move that logic into the parent class. Parent has "something; $this->bla();", defines an empty bla(), and each child overrides it with their own code (ie, renaming bla_a and bla_b to bla).

Link to comment
Share on other sites

1. Making $extra optional with a default value needs to be handled below the parent class. If you use "dummy" a lot, that hints at those child classes having something more in common than simply that one argument, which then suggests you create another class between the parent and child which redefines those methods.

 

2. If the "if something then call method" pattern is very common, and kinda even if it isn't, then move that logic into the parent class. Parent has "something; $this->bla();", defines an empty bla(), and each child overrides it with their own code (ie, renaming bla_a and bla_b to bla).

 

Thanks Requinx,

 

  1. The child class will never be passed an argument, and the parent will always be passed one..  The only reason I included the "dummy" argument is so I could use the same method name in each without generating a warning/error.  Does this change your recommendations?
  2. Will do.  I was thinking of passing a very simple anonymous function, but was chastised a while back...
Link to comment
Share on other sites

The child class will never be passed an argument, and the parent will always be passed one..  The only reason I included the "dummy" argument is so I could use the same method name in each without generating a warning/error.  Does this change your recommendations?

A bit.

 

So you have a parent method which does a thing, but it doesn't have the full picture (you made it protected so somebody else in the class hierarchy has to call it) while the child classes have the rest (the usethis1 and usethis2 values, which comes from within the class and doesn't need to be passed in)?

 

Two approaches:

a) The parent's method becomes a helper method with a different name, and the child classes call it with the right value.

b) The parent's method is the real method, defines an abstract method (which the children implement) to get whatever value, and calls it within the method to get the value. Requires your parent be abstract.

And you can use traits for a variation on either of those.

 

That adds onto the "if something then call method" option, which is a lot like the (b) approach in that the parent's method calls other methods to get data and to perform additional actions.

 

Will do.  I was thinking of passing a very simple anonymous function, but was chastised a while back...

Anonymous functions are appealing, it's true, but there's an object-oriented design solution here that would be more appropriate.
Link to comment
Share on other sites

So you have a parent method which does a thing, but it doesn't have the full picture (you made it protected so somebody else in the class hierarchy has to call it) while the child classes have the rest (the usethis1 and usethis2 values, which comes from within the class and doesn't need to be passed in)?

Exactly!

 

Two approaches:

a) The parent's method becomes a helper method with a different name, and the child classes call it with the right value.

Basically what I am doing, but using the same name.  Why not use the same name and do my default argument value hack?  If not, is there an accepted naming standard?  For instance, if child method is "doThis()", parent helper method should be "_doThis()"..

 

b) The parent's method is the real method, defines an abstract method (which the children implement) to get whatever value, and calls it within the method to get the value. Requires your parent be abstract.

And you can use traits for a variation on either of those.

 

That adds onto the "if something then call method" option, which is a lot like the (b) approach in that the parent's method calls other methods to get data and to perform additional actions.

 

I really need to get better at this!  Are you willing to give me some pseudo-real examples on how this works?  Or point me to some literature which I could read through, and hopefully answer questions regarding it?

 

Thanks you!

Link to comment
Share on other sites

It's generally considered bad to override a method and change it's parameter signature. This is why PHP throws a strict standards warning if you do so. The reason behind this is that it makes the child classes incompatible with the parent, and thus they couldn't reliable be used with something that might written to specifications of the parent class.

 

For example, what if you had some service that expects a ParentController object and tried called the doSomething() method. That code would assume that the function accepts an argument and does something with it. If you passed one of your child controller instances, the argument would be ignored. In your particular case this is not as much of an issue, but it's still a poor habit to get into. Think about if instead of reducing the parameter list you tried to extend it with non-optional parameters. Then the service would generate errors when calling the function because it didn't pass enough arguments.

 

The better solution is to use an abstract (or empty implementation) method as the entry point which the child classes can then override. Any code that should be shared would then be moved out to another separate method which the children can then call when appropriate.

 

<?php
$controller=new ChildController1();
$controller->doSomething();


abstract class ParentController {
    abstract public function doSomething();

    protected function doSomethingInternal($extra){
        header('Content-Type: application/json;');
        $id=(isset($_POST['id']))?$_POST['id']:0;
        $other=new otherclass();
        $x=$other->bla($id,$extra);
        $flag=$x['flag'];
        unset($x['flag']);
        echo(json_encode($x));
        return $flag;
    }
}


class ChildController1 extends ParentController {
    public function doSomething(){
        if($this->doSomethingInternal('usethis1')){
            $this->bla_a();
        }
    }
}


class ChildController2 extends ParentController {
    public function doSomething(){
        if($this->doSomethingInternal('usethis2')){
            $this->bla_b();
        }
    }
}
As also mentioned, if the pattern of 'if doSomething then bla();' is common across several children, you can move that up to the parent and just have the child classes override the bla method. Something like this:

abstract class ParentController {
    abstract protected function getExtra();
    public function success(){}
    public function failure(){}

    protected function doSomething(){
        header('Content-Type: application/json;');
        $id=(isset($_POST['id']))?$_POST['id']:0;
        $other=new otherclass();
        $x=$other->bla($id,$this->getExtra());
        $flag=$x['flag'];
        unset($x['flag']);
        echo(json_encode($x));
        return $flag;
    }
}


class ChildController1 extends ParentController {
    protected function getExtra(){ return 'usethis1'; }
    public function success(){
        //Rather than call a method, you'd just put your logic here directly
        $this->bla_a(); 
    }
}


class ChildController2 extends ParentController {
    protected function getExtra(){ return 'usethis2'; }
    public function failure(){
        //Rather than call a method, you'd just put your logic here directly
        $this->bla_b();
    }
}
  • Like 1
Link to comment
Share on other sites

Thanks Kicken,  Yea, I see your point and don't disagree with your reasons why it is bad.  I've totally ignored (or at least 96% ignored as I have in fact created some abstract classes without really knowing why) the concepts of abstract, traits, and the like, but expect I need to do differently.  If you know of any good resources to learn up, please advise.  Regardless, still appreciate your help.

Link to comment
Share on other sites

a) The parent's method becomes a helper method with a different name, and the child classes call it with the right value.

 

@Kicken.  I just went over your actual script examples.  For your first example, seems like this is what you are doing.  No?  Instead of calling the method "doSomething()", you call it "doSomethingInternal()".  From a naming prospective, anything wrong with calling the internal method "_doSomething()"?  You did create an abstract method "doSomething()", but it is empty.  What is the point?  To just let people know it is being defined in the child, or is there something more?  For your second example, I see how you are using getExtra().  Where does the success() and failure() methods come in?  Thanks

 

 

b) The parent's method is the real method, defines an abstract method (which the children implement) to get whatever value, and calls it within the method to get the value. Requires your parent be abstract.

And you can use traits for a variation on either of those.

 

@Requinx.  Do you mind giving me a brief script example how this would be implemented?  Something similar to my original and the one that Kicken provided so I could compare?  Thanks

Link to comment
Share on other sites

Instead of calling the method "doSomething()", you call it "doSomethingInternal()".  From a naming prospective, anything wrong with calling the internal method "_doSomething()"?

_doSomething would be fine. I'm not a fan of underscore prefixing methods personally, but that's just my own personal style. If possible I would give it a more meaningful name name than doSomethingInternal() even, but that would require knowing more about what the method does.

 

You did create an abstract method "doSomething()", but it is empty.  What is the point?

The doSomething is defined as being abstract which means a few things.

  • It is unimplemented. The method is defined, but has no known implementation yet so it cannot have a body.
  • If a class has an abstract method, it itself must be defined as abstract. This prevents anyone from doing new ParentController() in the code.
  • Any child classes must either implement that method, or declare themselves to be abstract. So in order to do new ChildController1, ChildController1 must implement the doSomething method.

For your second example, I see how you are using getExtra().  Where does the success() and failure() methods come in?  Thanks

The success() and failure() methods are defined as simple empty methods. This means the child classes can override them and implement a specific behaviour, but they are not required too (unlike if they were abstract). getExtra() is defined as abstract so each child class must define that method.

 

What I forgot to do in the example was change doSomething() to call success or failure rather than return $flag. The do something method would look more like:

    protected function doSomething(){
        header('Content-Type: application/json;');
        $id=(isset($_POST['id']))?$_POST['id']:0;
        $other=new otherclass();
        $x=$other->bla($id,$this->getExtra());
        $flag=$x['flag'];
        unset($x['flag']);
        echo(json_encode($x));

        if ($flag){ $this->success(); }
        else { $this->failure(); }
    }
Link to comment
Share on other sites

b) The parent's method is the real method, defines an abstract method (which the children implement) to get whatever value, and calls it within the method to get the value. Requires your parent be abstract.

And you can use traits for a variation on either of those.

 

I am still trying to understand how this would work.  Are you saying that the child implements the parent abstract method, and that parent method calls a method defined in the child to get the necessary value?

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.