SitePoint Sponsor

User Tag List

Page 1 of 2 12 LastLast
Results 1 to 25 of 26
  1. #1
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,011
    Mentioned
    56 Post(s)
    Tagged
    0 Thread(s)

    Properly implementing Polymorphism.

    One of the most powerful parts of the object oriented programming paradigm is polymorphism. Child classes may inherit then modify the behaviors of their parents. Through the inheritance tree of the classes we can move from the abstract to the specific. But it is also something I frequently see done... poorly... even in high level and popular applications (not going to name drop). You know it's being done poorly when you see this

    Code php:
    class foo {
      public function save() {
      /*
       * A lot of steps
       */
        $this->presave();
        $this->dosave();
        $this->postsave();
       /*
        * A lot more steps
        */
        return;
      }
     
      protected function presave(){}
      protected function postsave(){}
     
    }

    The idea is that the empty functions get defined later. This is the WRONG way to do this (I should know because I did it the wrong way for a long time).

    For polymorphism to be effective, flexible and powerful, you must follow a doctrine of "for each method, one and only one task". In other words, the above should look like this.

    Code php:
    class foo {
      public function save() {
        $this->step1();
        $this->step2();
        $this->step3();
        $this->step4();
        $this->doSave();
        $this->step5();
        $this->step6();
        $this->step7();
      }
    }

    In live code each of the step functions will have a descriptive name of course - I'm just rattling off an example here. Now that each step of the save process is it's own method we can modify the process at any step we choose by extending the function at the step we want to interrupt, do our interrupt action, then call the parent action.

    So if you find yourself wanting to write an empty method in a concrete class that gets called as a 'hook' - its almost certain that the function calling that hook is doing too much and needs to be broken down into other functions. Note that this doesn't apply to empty methods in abstract classes - they serve a different purpose altogether by requiring uncertain behavior to be defined before completion.

  2. #2
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    2 Thread(s)
    Hi Michael,

    Great that you are bringing forth proper OOP ideas (as you have always done). It occurs to me that many people may not understand why using polymorphism can be a good idea and examples of the power of it. I won't have time to knock together a simple example today. But I think a simple example might help others.

    In any regards, thanks for doing this and keep it going

    Regards,
    Steve
    ictus==""

  3. #3
    From Italy with love silver trophybronze trophy
    guido2004's Avatar
    Join Date
    Sep 2004
    Posts
    9,401
    Mentioned
    147 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by ServerStorm View Post
    It occurs to me that many people may not understand why using polymorphism can be a good idea and examples of the power of it. [...] I think a simple example might help others.
    An example would definitely help. I'm sure if one knows what you are talking about, it's all clear. But me, I don't see much difference in the two pieces of code you wrote. The second has some more 'calls', and no protected functions (are they supposed not to be there, or did you just not write them to keep the example short?), but it's not clear to me why the first is the wrong way, and the second the correct way.

    If you could elaborate some more, I would be very grateful. I have a basic knowledge of OOP, and am trying to get deeper into it, so I'm very interested in posts like these.

  4. #4
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    2 Thread(s)
    Quote Originally Posted by guido2004 View Post
    [...]

    If you could elaborate some more, I would be very grateful. I have a basic knowledge of OOP, and am trying to get deeper into it, so I'm very interested in posts like these.
    Hi guido2004,
    I am trying to think of a good subject to use to simply illustrate the power behind this pattern. To often the example on the web use Automobiles or Animals to demonstrate, but I am going to find something that actually affects PHP authors so they can see the difference when they think of how the do this when writing procedural code. The subject need not to get people mired in details so that it remains clear. I will have it by morning (9:56 PM Eastern Time now), so around 7:00 AM tomorrow I will lay it out and hopefully will do it Justice

    Glad you are looking to grow in this area! I am sure there are a good number of people that could benefit from moving more of their code to OOP, but just need a non-intimidating approach and good reasons to see why they should switch; too often OOP developers loose sight that procedural programming is quite different and at the same time often very effective. The better approach would to see how OOP can better structure and reduce code and promote reuse; while this is possible also in procedural programming there are some things that can be done with OOP that are not possible via procedural methods.

    Regards,
    Steve
    ictus==""

  5. #5
    From Italy with love silver trophybronze trophy
    guido2004's Avatar
    Join Date
    Sep 2004
    Posts
    9,401
    Mentioned
    147 Post(s)
    Tagged
    4 Thread(s)
    Hi Steve, I already use OOP, and I even know what polymorphism is (in theory). What isn't clear to me is the point @Michael Morris ; is trying to make with the two pieces of code he posted. An example to explain why the first way is wrong, and the second is good, would be great.
    And since Michael says he has done it the wrong way for a long time, I was hoping he could take a case where he did it wrong, explain how he did it (wrong), why it is wrong, how he would do it today (right) and why it is right.

  6. #6
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    2 Thread(s)
    Quote Originally Posted by guido2004 View Post
    Hi Steve, I already use OOP, and I even know what polymorphism is (in theory). What isn't clear to me is the point @Michael Morris ; is trying to make with the two pieces of code he posted. An example to explain why the first way is wrong, and the second is good, would be great.
    And since Michael says he has done it the wrong way for a long time, I was hoping he could take a case where he did it wrong, explain how he did it (wrong), why it is wrong, how he would do it today (right) and why it is right.
    Ok saves me doing an example
    ictus==""

  7. #7
    From Italy with love silver trophybronze trophy
    guido2004's Avatar
    Join Date
    Sep 2004
    Posts
    9,401
    Mentioned
    147 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by ServerStorm View Post
    Ok saves me doing an example
    Hey, if you can give me an example to clarify Michael's post, don't hesitate!

  8. #8
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,011
    Mentioned
    56 Post(s)
    Tagged
    0 Thread(s)
    I'll construct something more elaborate this evening.

  9. #9
    Avid Logophile silver trophy
    ParkinT's Avatar
    Join Date
    May 2006
    Location
    Central Florida
    Posts
    2,285
    Mentioned
    181 Post(s)
    Tagged
    4 Thread(s)
    I think the illustration/example does not properly present @Michael Morris ; actual intent.
    That example, although you are demonstrating Polymorphism, demonstrates a very 'procedural' process. It is very linear and does not [necessarily] benefit from OOP.

    I stand with @guido2004 ; in that, because it is procedural, there is no difference between the two code snippets.

    This is a very interesting discussion - please don't misunderstand my statement as a criticism.
    Don't be yourself. Be someone a little nicer. -Mignon McLaughlin, journalist and author (1913-1983)


    Literally, the best app for readers.
    Make Your P@ssw0rd Secure
    Leveraging SubDomains

  10. #10
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    2 Thread(s)
    This was the polymorphic example I was going to post this morning. Notice the displayHTMLElement function is completely agnostic to what type of HTML element is being processed.

    Copy all this code into a php file and run it:
    PHP Code:
    <?php 
    Interface IHtml{  
      public function 
    showHTML(); 
      public function 
    setText($text);
    }
    class 
    HtmlElement implements IHtml{
       public function 
    showHTML(){}  
       public function 
    setText($text){}
    }
    class 
    H1 extends HtmlElement
      protected 
    $h1;  
      public function 
    setText($text){    
        
    $this->h1 "<h1>$text</h1>";  
     }  
     public function 
    showHTML(){    
       echo 
    $this->h1;  }
     }

     class 
    Paragraph extends HtmlElement
       protected 
    $p;  
       public function 
    setText($text){    
         
    $this->"<p>$text</p>";  
       }  
       public function 
    showHTML(){    
         echo 
    $this->p;  
      }
    }

    function 
    displayHTMLElement(HtmlElement $obj){  
      
    $obj->showHTML();
    }

    $pageH1 = new H1();
    $pageH1->setText('This is an example of polymorphism in PHP');

    $aParagraph = new Paragraph();
    $aParagraph->setText('This is a paragraph of text. I should have use lores Impsum? Oh Well?');

    $bParagraph = new Paragraph();
    $bParagraph->setText('This is another paragraph of text. You get the point!');

    $page = array(  
      
    $pageH1  
      
    $aParagraph  
      
    $bParagraph
    );

    foreach(
    $page as $htmlElementObj){  
      
    displayHTMLElement($htmlElementObj);
    }
    ?>
    Obviously this is simplified for the purposes of example.

    Polymorphism in PHP basically means that a single class, method or variable name may present itself in many different forms.

    We know that every class that inherits from HtmlElement baseclass, automatically inherits the showHTML(), and setText() methods. We override the showHTML() and setText() methods in every subclass of HtmlElement. Our subclass objects know what to do when their showHTML() method is being called, without us having to worry of what type our subclass object is. This pattern is often used to replace bad or difficult switch code.

    Feel free to tear it apart if need be

    Regards,
    Steve
    ictus==""

  11. #11
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,011
    Mentioned
    56 Post(s)
    Tagged
    0 Thread(s)
    Math is the parent of computing, so let us turn to it to provide the abstraction we need.

    Code php:
     
    class A {
      protected $a;
      protected $b;
     
      public function __construct( $a, $b ) {
        $this->a = $a;
        $this->b = $b;
      }
     
      public function sum() {
        return $this->a + $this->b;
      }
    }

    This demonstrates two principles. First, constructors should only be used to create a ready state. They should never perform other operations. Now, let's extend the class to handle division

    Code php:
    class B extends A {
      public function divide() {
        return $a / $b;
      }
    }

    See a problem? If $b == 0, we'll get E_USER_ERROR. So we could correct our function for this.

    Code php:
    class B extends A {
      public function divide() {
        if ($this->b == 0 ) {
          echo 'undefined';
          return null;
        } else {
          return $this->a / $this->b;
        }
      }
    }

    But suppose we have a formula where, when $b = 0, we should return zero. With the example above we can to overwrite the entire function like so

    Code php:
    class C extends B {
      public function divide() {
        return ($this->b == 0) ? 0 : $this->a / $this->b;
      }
    }

    Got the basics, good. Let's make it complex

    Code php:
    class A extends ArrayObject {
      public function sum() {
        $r = 0;
     
        foreach ($this as $v) {
          $r += $v;
        }
     
        return $r;
      }
     
      public function product() {
        $r = 1;
     
        foreach ($this as $v) {
          $r *= $v;
        }
     
        return $r;
      }
    }

    We will start with array object because we want array-like behaviors. If we make an 'A' we can address it like an array like so.

    Code php:
    $a = new A(array( 1, 2, 3, 4, 5, 6 ));
    echo $a->sum(); // 21
    $a[] = 9;
    echo $a->sum(); // 30

    Now we will make another child that needs a product of all non-zero.

    Code php:
    class B extends A {
      public function productOfAllNonZero() {
        $r = 1;
        foreach ( $this as $v ) {
          if ($v != 0 ) {
           $r *= $v;
          }
        }
     
        return $r;
      }
    }

    Note that this repeats the product function almost verbatim, but adds another step. This is inefficient. We can refactor though.


    Code php:
    class A extends ArrayObject {
      public function sum() {
        return $this->doSum($this);
      }
     
      protected function doSum( $a ) {
        $r = 0;
     
        foreach ($a as $v) {
          $r += $v;
        }
     
        return $r;
      }
     
      public function product() {
        return $this->doProduct($this);
      }
     
      public function doProduct( $a ) {
        $r = 1;
     
        foreach ($a as $v) {
          $r *= $v;
        }
     
        return $r;
      }
    }

    And now the child class is made simpler.

    Code php:
    class B extends A {
      public function productOfAllNonZero() {
        return $this->doProduct($this->filterZeros( $this ));
      }
     
      protected function filterZeros( $a ) {
        $r = array();
     
        /*
         * Yes, array_filter() is quicker, but less illustrative of process.
         * That, and I'm not sure array_filter works with array objects and
         * not inclined to look that up right now.
         */
        foreach ( $a as $v ) {
          if ($v != 0) {
            $r[] = $v;
          }
        }
     
        return $r;
      }
    }

    Now, let's create a class that has a method with a lot going on.

    Code php:
    class C extends B {
      protected $answer = 0;
     
      const THE_MEANING_OF_LIFE = 42;
      // That's what Douglas Adam's says.
     
      public function foo() {
        $this->productOfAllNonZero();
        $this->subtractEverything();
        $this->addTheMeaningOfLife();
        $this->square();
        $this->addEverything();
        return $this->answer;
      }
     
      protected function productOfAllNonZero() {
        $this->answer = parent::productOfAllNonZero();
      }
     
      protected function subtractEverything() {
        $this->answer -= $this->doSum($this);
      }
     
      protected function addTheMeaningOfLife() {
        $this->answer += self::THE_MEANING_OF_LIFE;
      }
     
      protected function addEverything() {
        $this->answer += $this->doSum($this);
      }
    }
    Now suppose we need to a square operation after adding the meaning of life.

    Code php:
    class D extends C {
      protected function addTheMeaningOfLife() {
        parent::addTheMeaningOfLife();
     
        $this->answer = pow($this->answer, 2);
      }
    }

    And I can go on, but this sufficiently demonstrates how to make granular functions. One word of warning which should go without saying - one letter variables and class names are ok when dealing with the utterly abstract of math. Do not use them in live code.

  12. #12
    SitePoint Member
    Join Date
    Dec 2010
    Location
    Silicon Valley
    Posts
    24
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    @Michael Morris

    To say that preX(), postX(), methods in your first example is "wrong" would be over the top. There is always a trade off to everything.

    You can still make great use of pre, post and save (or commit), like so:

    PHP Code:
    <?php

    class Example {
      
    // a collection of pre step objects to do before calling save
      
    private $pre = array();

      
    // a collection of post step objects to do after save is called
      
    private $post = array();

      public function 
    all() {
        
    $this->pre();
        
    $this->save();
        
    $this->post();
      }

      public function 
    pre() {
        foreach (
    $this->pre as $object) {
          
    //do something
        
    }
      }

      public function 
    save() {
        
    //commit
      
    }

      public function 
    post() {
        foreach (
    $this->post as $object) {
          
    //do something
        
    }
      }
    }
    The Pre/Post objects encapsulate the behavior. It doesn't affect the class and each method never does more than one thing.

  13. #13
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,011
    Mentioned
    56 Post(s)
    Tagged
    0 Thread(s)
    PHP does allow you to have a member and method with the same name. In my opinion, that's a major Pain In The Ass and extremely bad practice because very few languages will let you do that. The language used in concert with PHP -> Javascript, doesn't permit such behavior. It leads to unmaintainable byzantine crap code - don't do it.

    The possibility of using traits, closures and object composition to further refine object behavior is out of scope, but my argument remains the same. This is correct.

    Code php:
    class A {
      public function save() {
        // save action only
      }
    }
    class B extends A {
      public function save() {
        // more actions
        parent::save();
      }
    }

    This is not correct.

    Code php:
    class A {
      public function save() {
        $this->preSave();
        // save method
      }
    }
     
    class B extends A {
      protected function presave() {
         // more actions
      }
    }

    YMMV, but I see the use of hooks amateurism. Yes it works, but it isn't elegant and it is FAR more limited than breaking the functions down into one action each. In that event you can effectively put your hook anywhere you want it to be and have it do whatever you want.

  14. #14
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    2 Thread(s)
    @Michael Morris

    Nice example, several concepts well explained, thanks.

    Though In your first example that started this thread you mentioned about overriding empty methods in the 'right way' but in your most recent example you aptly demonstrate granularity (and also why some people favour composition over inheritance ) but for some of us it is still hard to see the correlation to your first example.

    In your first example 'The proper way', if we related to your second example, would it be that Class C extends B in your most recent example and the methods were either inherited or overridden in Class B extend A with the methods in Class A? The point being keeping each class very focused and inheriting rather than trying to jam all tasks into a singular class?


    Regards,
    Steve
    ictus==""

  15. #15
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,011
    Mentioned
    56 Post(s)
    Tagged
    0 Thread(s)
    Object composition solves different problems than inheritance does. Both have advantages and disadvantages. The goal is to recognize which of the two best addresses the problem at hand and deploy the correct one.

    As to class length, a class should be as long as it needs to be, as short as it can possibly be, and no longer. If in doubt err in favor of brevity.

  16. #16
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    2 Thread(s)
    Quote Originally Posted by Michael Morris View Post
    Object composition solves different problems than inheritance does. Both have advantages and disadvantages. The goal is to recognize which of the two best addresses the problem at hand and deploy the correct one.

    As to class length, a class should be as long as it needs to be, as short as it can possibly be, and no longer. If in doubt err in favor of brevity.
    Agreed, but I am still unclear what your first post was illustrating about overriding empty methods. Maybe I am the slow one in the crowd? I like the fact that you were recommending the right versus wrong way but don't understand why it is the right way.

    Regards,
    Steve
    ictus==""

  17. #17
    Theoretical Physics Student bronze trophy Jake Arkinstall's Avatar
    Join Date
    May 2006
    Location
    Lancaster University, UK
    Posts
    7,062
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Or if you want to run the parent's thread by only inserting a single method to be run after/before one of the defaults....

    (Yes, it's messy. Yes, I was bored):
    PHP Code:
    <?phpclass ProcessManager extends Process{    protected $Processes = array();    public function __Construct(){            }    public function hasFinished(){        return == count($this->getPossibleTasks());    }    public function getPossibleTasks(){        $Tasks = array();        foreach($this->getRemainingTasks() as $Name => $Process){            if($Process->isPossible()){                $Tasks[$Name] = $Process;            }        }        return $Tasks;    }    public function getRemainingTasks(){        $Processes = array();        foreach($this->Processes as $Name => $Process){            if(!$Process->hasFinished()){                $Processes[$Name] = $Process;            }        }        return $Processes;    }    public function countPossibleTasks(){        return count($this->getPossibleTasks());    }    public function countRemainingTasks(){        return count($this->getRemainingTasks());    }    public function getProcess($Name){        if($Name instanceof Process){            return $Name;        }        return array_key_exists($Name$this->Processes) ? $this->Processes[$Name] : false;    }    public function addProcess($Name$Callback$Arguments = array()){        if(!is_array($Arguments)){            $Arguments = array($Arguments);        }        $this->Processes[$Name] = $Process = new Process($this$Callback$Arguments);        return $Process;    }    public function step(){        foreach($this->getPossibleTasks() as $Task){            echo "<p>" $Task->run() . "</p>";        }    }    public function run(){        while($this->countPossibleTasks() > 0){            $this->step();        }        if($this->countRemainingTasks() > 0){            echo "Not all tasks could be run due to a structural error";        }    }}class Process{    protected $Before = array();    protected $Dependencies = array();    protected $Callback;    protected $Arguments = array();    protected $Status 0;    protected $Manager;    function __Construct(ProcessManager $Manager$Callback, array $Arguments = array()){        $this->Manager $Manager;        $this->Callback $Callback;        $this->Arguments $Arguments;    }    protected function hasFinished(){        return $this->Status == 1;    }    public function equals(Process $Process){        return $Process->Callback == $Callback;    }    public function isPossible(){        foreach($this->Dependencies as $Task){            if(!$Task->hasFinished()){                return false;            }        }        return true;    }    public function ensureAfter(){        $Arguments func_get_args();        if(count($Arguments) != 1){            foreach($Arguments as $Argument){                ensureAfter($Argument);            }            return $this;        }        $Name $Arguments[0];        $Dependency $this->Manager->getProcess($Name);        if($Dependency !== false){            $this->Dependencies[] = $Dependency;        }        return $this;    }    public function ensureBefore(){        $Arguments func_get_args();        if(count($Arguments) != 1){            foreach($Arguments as $Argument){                ensureAfter($Argument);            }            return $this;        }        $Name $Arguments[0];        $Child $this->Manager->getProcess($Name);        if($Child !== false){            $Child->ensureAfter($this);        }        return $this;    }    public function run(){        $this->Status 1;        return call_user_func_array($this->Callback$this->Arguments);    }}
    class 
    SomeData{    protected $Something '';    public function setSomething($Value){        $this->Something $Value;    }    public function getSomething(){        return $this->Something;    }}
    function 
    setSomeData(SomeData $Data){    $Data->setSomething("This is my data");    return 'I set the data to "This is my data"';}function outputSomeData(SomeData $Data){    $Text $Data->getSomething('This is my data');    return 'I retrieved the data, it is: "' $Text '"';}function somethingEntirelyDifferent(){    return 'I dont need the data';}$Data = new SomeData;$Manager = new ProcessManager;$Manager->addProcess('f2''outputSomeData'$Data);$Manager->addProcess('f1''setSomeData'$Data)->ensureBefore('f2');$Manager->addProcess('f3''somethingEntirelyDifferent');$Manager->run();
    Jake Arkinstall
    "Sometimes you don't need to reinvent the wheel;
    Sometimes its enough to make that wheel more rounded"-Molona

  18. #18
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    2 Thread(s)
    @Jake Arkinstall

    Code cleaned up for all the enjoy:
    Code PHP:
    class ProcessManager extends Process{    
      protected $Processes = array();    
      public function __Construct(){ }    
      public function hasFinished(){
        return 0 == count($this->getPossibleTasks());
      }    
      public function getPossibleTasks(){
        $Tasks = array();        
        foreach($this->getRemainingTasks() as $Name => $Process){
          if($Process->isPossible()){
            $Tasks[$Name] = $Process;
          }        
        }
        return $Tasks;    
      }
      public function getRemainingTasks(){
        $Processes = array();        
        foreach($this->Processes as $Name => $Process){
          if(!$Process->hasFinished()){ 
            $Processes[$Name] = $Process;
          }
        }
        return $Processes;
      }    
      public function countPossibleTasks(){
        return count($this->getPossibleTasks());
      }
      public function countRemainingTasks(){
        return count($this->getRemainingTasks());
      }    
      public function getProcess($Name){
        if($Name instanceof Process){
          return $Name;
        }
        return array_key_exists($Name, $this->Processes) ? $this->Processes[$Name] : false;    
      }
      public function addProcess($Name, $Callback, $Arguments = array()){
        if(!is_array($Arguments)){
          $Arguments = array($Arguments);
        }
        $this->Processes[$Name] = $Process = new Process($this, $Callback, $Arguments);
        return $Process;    
      }
      public function step(){
        foreach($this->getPossibleTasks() as $Task){
          echo "<p>" . $Task->run() . "</p>";
        }
      }
      public function run(){
        while($this->countPossibleTasks() > 0){
          $this->step();
        }
        if($this->countRemainingTasks() > 0){
          echo "Not all tasks could be run due to a structural error";
        }    
      }
    }
    class Process{    
      protected $Before = array();    
      protected $Dependencies = array();    
      protected $Callback;    
      protected $Arguments = array();    
      protected $Status = 0;    
      protected $Manager;    
      function __Construct(ProcessManager $Manager, $Callback, array $Arguments = array()){
        $this->Manager = $Manager;
        $this->Callback = $Callback;
        $this->Arguments = $Arguments;
      }    
      protected function hasFinished(){
        return $this->Status == 1;
      }
      public function equals(Process $Process){
        return $Process->Callback == $Callback;    
      }    public function isPossible(){
        foreach($this->Dependencies as $Task){
          if(!$Task->hasFinished()){
            return false;
          }        
        }        
        return true;    
      }    
      public function ensureAfter(){        
        $Arguments = func_get_args();        
        if(count($Arguments) != 1){            
          foreach($Arguments as $Argument){                
            ensureAfter($Argument);            
          }            
          return $this;        
        }        
        $Name = $Arguments[0];        
        $Dependency = $this->Manager->getProcess($Name);        
        if($Dependency !== false){
          $this->Dependencies[] = $Dependency;        
        }        
        return $this;    
      }    
      public function ensureBefore(){        
        $Arguments = func_get_args();        
        if(count($Arguments) != 1){            
          foreach($Arguments as $Argument){                
            ensureAfter($Argument);
          }            
          return $this;        
        }        
        $Name = $Arguments[0];        
        $Child = $this->Manager->getProcess($Name);        
        if($Child !== false){            
          $Child->ensureAfter($this);        
        }        
        return $this;    
      }    
      public function run(){        
        $this->Status = 1;        
        return call_user_func_array($this->Callback, $this->Arguments);  
      }
    }
    class SomeData{
      protected $Something = '';    
      public function setSomething($Value){        
        $this->Something = $Value;    
      }    
      public function getSomething(){        
        return $this->Something;    
      }
    }
    function setSomeData(SomeData $Data){    
      $Data->setSomething("This is my data");    
      return 'I set the data to "This is my data"';
    }
     
    function outputSomeData(SomeData $Data){    
      $Text = $Data->getSomething('This is my data');    
      return 'I retrieved the data, it is: "' . $Text . '"';
    }
    function somethingEntirelyDifferent(){
      return 'I dont need the data';}
      $Data = new SomeData;$Manager = new ProcessManager;
      $Manager->addProcess('f2', 'outputSomeData', $Data);
      $Manager->addProcess('f1', 'setSomeData', $Data)->ensureBefore('f2');
      $Manager->addProcess('f3', 'somethingEntirelyDifferent');$Manager->run
    }
    ictus==""

  19. #19
    From Italy with love silver trophybronze trophy
    guido2004's Avatar
    Join Date
    Sep 2004
    Posts
    9,401
    Mentioned
    147 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by ServerStorm View Post
    Agreed, but I am still unclear what your first post was illustrating about overriding empty methods. Maybe I am the slow one in the crowd? I like the fact that you were recommending the right versus wrong way but don't understand why it is the right way.
    That makes two of us
    Quote Originally Posted by Michael Morris View Post
    Object composition solves different problems than inheritance does. Both have advantages and disadvantages. The goal is to recognize which of the two best addresses the problem at hand and deploy the correct one.
    It would be very interesting to see some examples of problems that are best solved by object composition, and some that are best solved by inheritance. I know I'm asking much, but hey, I can try

    And yes, I'm trying to keep things on my level. For example, I understand Jake's code example (from a purely technical point of view), but I have no idea what he was trying to show us from a theoratical point of view. Without some explanation, I'm lost.

  20. #20
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,011
    Mentioned
    56 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by ServerStorm View Post
    Agreed, but I am still unclear what your first post was illustrating about overriding empty methods. Maybe I am the slow one in the crowd? I like the fact that you were recommending the right versus wrong way but don't understand why it is the right way.

    Regards,
    Steve
    You shouldn't need to produce empty placeholder methods which may never need to be populated. It just doesn't look right. Also, there is a break between the external facing API and the internal API. For example, going back to the original example..

    Code php:
    class A {
     
      public function save() {
       /*
        * code before save
        */
       $this->presave();
       /*
        * code doing save
        */
       $this->postsave();
       /*
        * code after post save
        */
      }
    }

    This is less efficient, in my mind, than

    Code php:
    class A {
     
      public function save() {
        $this->precommit();
        $this->commit();
        $this->postcommit();
      }
     
      protected function precommit() {
       /*
        * code before save
        */
      }
     
      protected function commit() {
       /*
        * code doing save
        */
      }
     
      protected function postcommit() {
       /*
        * code after post save
        */
      }
    }

    The former method, the one I label as incorrect, allows for 2 possible hook points and 2 functions which may remain unused. The later method allows for 6 hook points overall, though effectively 4 hooks since there is no operative difference between hooking in at the end of precommit and hooking to the start of commit.

    More important than the choice to do hooks or not is this - the more your function does, the more likely you will need to alter the course of its operation in the future. That's the primary reason to keep the scope of the function small. The same applies to classes - the larger they are the more likely they will need modification.

    Avoiding hooks encourages code granularity. Polymorphism, whether achieved through inheritance or by composition, needs the elements of the code to be as granular as possible.

  21. #21
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    2 Thread(s)
    Quote Originally Posted by Michael Morris View Post
    You shouldn't need to produce empty placeholder methods which may never need to be populated. It just doesn't look right. Also, there is a break between the external facing API and the internal API. For example, going back to the original example..
    [...]
    The former method, the one I label as incorrect, allows for 2 possible hook points and 2 functions which may remain unused. The later method allows for 6 hook points overall, though effectively 4 hooks since there is no operative difference between hooking in at the end of precommit and hooking to the start of commit.

    More important than the choice to do hooks or not is this - the more your function does, the more likely you will need to alter the course of its operation in the future. That's the primary reason to keep the scope of the function small. The same applies to classes - the larger they are the more likely they will need modification.

    Avoiding hooks encourages code granularity. Polymorphism, whether achieved through inheritance or by composition, needs the elements of the code to be as granular as possible.
    Excellent, thanks for your efforts as your thread intention is now very clear to me and hopeful for others too!
    ictus==""

  22. #22
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    2 Thread(s)
    Quote Originally Posted by guido2004 View Post
    [...]
    It would be very interesting to see some examples of problems that are best solved by object composition, and some that are best solved by inheritance. I know I'm asking much, but hey, I can try
    [...]
    @guido2004
    Maybe we should start a new thread with an example/description of object composition with comments of when to choose over inheritance and then link the inheritance to this thread. Thoughts?
    ictus==""

  23. #23
    From Italy with love silver trophybronze trophy
    guido2004's Avatar
    Join Date
    Sep 2004
    Posts
    9,401
    Mentioned
    147 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by ServerStorm View Post
    Excellent, thanks for your efforts as your thread intention is now very clear to me and hopeful for others too!
    Yes, much clearer. Thanks Michael.

  24. #24
    From Italy with love silver trophybronze trophy
    guido2004's Avatar
    Join Date
    Sep 2004
    Posts
    9,401
    Mentioned
    147 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by ServerStorm View Post
    @guido2004
    Maybe we should start a new thread with an example/description of object composition with comments of when to choose over inheritance and then link the inheritance to this thread. Thoughts?
    Yes, I guess that's a good idea. The scope of threads is best kept small as well

  25. #25
    Theoretical Physics Student bronze trophy Jake Arkinstall's Avatar
    Join Date
    May 2006
    Location
    Lancaster University, UK
    Posts
    7,062
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Thanks ServerStorm, it seems the forum is less paste-friendly than I remember it! Should have looked at the post before closing the window

    The code I wrote above is essentially a way of inserting added functionality into a method. For example, a method whch has ten steps may very often have those steps in all child classes but maybe adding a method call in somewhere inbetween other calls - it allows you to jump in the middle of functionality without repeating the same call steps in every child class methods.

    It was purely theoretical and anyone who would actually use it would be downright insane. Just seeing what I could come up with (Probably habit from the old Application Design forum where each reply to an idea generally expanded it, made it more complicate and, as a payoff, ever so slightly more flexible to the point of pointlessness. Im going to miss that forum.)

    It is.essentially, to break it down, an object which holds an array of methods to use. Thy are run in any order so long as the methods can be run, I.e. the appropriate preceding methods have been run. If you'd like to, for exmple, extend a mapper such tht an email is sent after data is processed but before it is saved, you could modify the "save" object's tasks to make your email method run after the processing method, and modify the db-calling method to run after the emailing method. Its only real-life use would be in a system where
    the parent classes are subject to massive frequent changes.

    Nicely rounded off Michael, I believe youve explained your point very well.

    Apologies for typography errors, this was written via a mobile phone, and the browser doesnt seem to handle this textarea nicely at all.
    Jake Arkinstall
    "Sometimes you don't need to reinvent the wheel;
    Sometimes its enough to make that wheel more rounded"-Molona


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •