$obj()->var syntax for accessors?

Or maybe like this?


class Mammal {
  /* private things */

  public function render(){
    /* Mammal way of rendering itself */
  }
}

class Dog extends Mammal {
  /* private things */
  private smells_real_good;

  public function render(){
    parent::render();
    /* +Dog way of rendering itself */
  }
}

class Human extends Mammal {
  /* private things */
  private drives_cars;

  public function render(){
    parent::render();
    /* +Human way of rendering itself */
  }
}

$animals = array( 'Dog' => new Dog(), 'Human' => new Human());


Let’s not forget inheritance. It may hinder with your design a little.

Though it’s not clear from your example, but if you completely (and, most likely, redundantly) handle the render method inside each class rather then delegating it to view-centric classes, you’re kissing DRY and reusability good bye. The very reasons why this kind of encapsulation and why making a goal out of erasing getters/setters are not worth it.

Indeed. Of course in a strongly typed language you’d specify the return types in the DogRenderer interface and that wouldn’t be an issue.

I see two competing design decisions here. If we ensure that all private members are fully private, data types and all, then the Dog class must handle every task that uses Dog data, including templating. Or if we ensure good separation of concerns, then we must extract data from Dog, one way or another, which will reveal data types. I don’t see how we can have it both ways, and if I had to pick the lesser of two evils, I’d say it’s better to have good separation of concerns.

I think that’s a very eloquent description of the problem at hand. In my example, if the interface could enforce data types, the dog class would deal with exporting the data. As usual it’s a trade-off and I’d agree that good separation of concerns is far more important. Even the added complexity of the double dispatch code I posted above is a huge negative in my opinion.

I think one thing to remember here is that every class has an API. Once that’s published and in use in production it shouldn’t change without breaking. This is fundamental in OOP. If getAge() was designed to return an integer timestamp then it always should. How that time is represented within the Dog class is irrelevant as long as the Dog class returns the value in the type required of it when getAge() is called. getAge() can transparently do the conversion internally if needed.

Agree with all of that.

A key aspect of MVC (at least how I understand it) is that the view has full control over what it outputs, not the class. So by giving choice of how to render to the class, that makes the view semi-redundant.

I like a data-holding class to be in full control of its data and how other things access that data, but I don’t like it doing much more than that. Then there are classes which control flow, and then the views which throw that all together and display them as they want. Of course, that’s just the way I like it done

I can always extend classes and change all that without breaking backwards compatibility. There are some concerns in doing that, but it is a viable and it’s a used solution.

Changing the return type of a method in a child class would break polymorphism and in my opinion is worse than adding another method which gets the same data in a different format.

Actually I was thinking about another method in the child class. This because method overload is not that straightforward in PHP. I was also thinking about code I’m not authoring directly, i.e. third party frameworks. As such, I don’t want to worry about in-file code details whit every new version of the frameworks I may use, by directly changing the code in the original class.

Going back to what you said, method overloading doesn’t break polymorphism. And, in this case, getters/setters would also need overloading.

And backward compatibility and encapsulation are not the same thing.

Ah I thought you were suggesting overloading the method in the child class which, as you say, isn’t easy in PHP. Method overloading in PHP does break polymorphism because PHP doesn’t support it properly.

e.g.



class Dog {
  public function setBirthday($d, $m, $y) {
   
  }
}

class Dog2 extends Dog {
 public function setBirthday(DateTime $date) {

 }
}

In some languages this is allowed and would work as expected. Obviously in PHP it won’t.

It doesn’t mean it can’t be done. Exception is your friend.

Anyway, I’m almost done with the code. You know, a little procrastination, a little busy at work. :slight_smile: Tomorrow I’ll post it so we’d have something to talk about specifically.

As promised, a code we can discuss about.

The short story… picture-short: http://i1054.photobucket.com/albums/s490/itmitica/itmitica-dog-2.png

The short story… php-short:


// A short story
echo '<h1>World events</h1>';

// Once upon a time on Earth.
$earth = new World( 'Earth' );

// Something was happening.
echo '<hr>';
echo '<h2>Event #1</h2>';

// A dog named Spot was barking at John's house.
// John, like a good master, feeds Spot.
// Spot is eating now.
$spot = new Dog( 'Spot' );
$spot -> bark();

$john = new Man( 'John' );
$john_house = new Location( 'house', $john );

$spot_barking = new Sound( $spot, $john_house );
$earth -> event( $spot_barking );

// Another thing happen then.
echo '<hr>';
echo '<h2>Event #2</h2>';

// A dog named Rex sees Spot eating and barks at John's house too.
// John is not Rex's master. He's chasing Rex away.
$rex = new Dog( 'Rex' );
$rex -> bark();

$rex_barking = new Sound( $rex, $john_house );
$earth -> event( $rex_barking );

// Rex runs to his master's house, Bob, and barks.
// Bob is also a good master. He feeds Rex.
// Rex is eating too now.
echo '<hr>';
echo '<h2>Event #3</h2>';
$bob = new Man( 'Bob' );
$bob_house = new Location( 'house', $bob );

$rex_barking -> changeLocation( $bob_house );
$rex -> bark();
$earth -> event( $rex_barking );

// The End.
echo '<hr>';

The whole deal in one file:


<?php

trait Base {

    public function getProperty( $property ) {
        return $this -> $property;
    }
}


class World {
    
    use Base;
    
    protected $name;
    protected $event;
    
    public function __construct ( $name ) {
        $this -> name = $name;
    }
    
    public function event( $event ) {
        $this -> event = $event;
        
        echo '<h3>' .
                $this -> event -> getProperty( 'source' ) -> getProperty( 'name' ) . ' is ' .
                $this -> event -> getProperty( 'source' ) -> getProperty( 'activity' ) . ' at ' .
                $this -> event -> getProperty( 'location' ) -> getProperty( 'owner' ) -> getProperty ( 'name' )  . '\\'s ' .
                $this -> event -> getProperty( 'location' ) -> getProperty( 'type' ) . '.' .
             '</h3>';
             
        if ( $this -> event -> getProperty( 'location' ) -> getProperty( 'type' ) == 'house' ) {
            $this -> event -> getProperty( 'location' ) -> getProperty( 'owner' ) ->
                alert( $this -> event -> getProperty( 'source' ) );
        }
        
        echo '<p>' . 
                $this -> event -> getProperty( 'location' ) -> getProperty( 'owner' ) -> getProperty ( 'name' ) . ' is ' . 
                $this -> event -> getProperty( 'location' ) -> getProperty( 'owner' ) -> getProperty ( 'activity' ) . ' ' .
                $this -> event -> getProperty( 'source' ) -> getProperty( 'name' ) . '. ' .
                $this -> event -> getProperty( 'source' ) -> getProperty( 'name' ) . ' is ' .
                $this -> event -> getProperty( 'source' ) -> getProperty( 'activity' ) . '.' .
             '</p>';
    }
}


class Sound {
    
    use Base;

    protected $source;
    protected $location;
    
    public function __construct( $source, $location ) {
        $this -> source = $source;
        $this -> location = $location;
    }
    
    public function changeLocation( $newlocation ) {
        $this -> location = $newlocation;
    }
}


class Location {

    use Base;
    
    protected $type;
    protected $owner;
    
    public function __construct( $type, $owner ) {
        $this -> type = $type;
        $this -> owner = $owner;
    }
}


class Man {

    use Base;
    
    protected $name;
    protected $activity = 'idle';
    protected $alertsource;
    
    protected $dogResponse = [
      'feeding' => 'eat',
      'chasing'  => 'run'
    ];
    
    public function __construct( $name ) {
        $this -> name = $name;
    }
    
    public function alert( $source ) {
        $this -> alertsource = $source;
        
        switch( $this -> alertsource -> getProperty( 'activity' ) ) {
            case 'barking':
                $this -> handleDog();
        }
    }
    
    protected function handleDog () {
        if ( $this -> alertsource -> getProperty( 'master' ) == $this -> name ) {
            $this -> activity = 'feeding'; 
        } else {
            $this -> activity = 'chasing';
        };
        
        call_user_func( [ $this -> alertsource, $this -> dogResponse[ $this -> activity ] ] );        
    }
}


class Dog {
    
    use Base;
    
    protected $name;
    protected $master;
    protected $activity = 'idle';
    
    protected $dogMaster = [
      'Spot' => 'John',
      'Rex'  => 'Bob'
    ];
    
    public function __construct ( $name ) {
        $this -> name = $name;
        $this -> master = $this -> dogMaster[ $this -> name ];
    }
    
    public function bark() {
        $this -> activity = 'barking';
    }
    
    public function eat() {       
        $this -> activity = 'eating';
    }
    
    public function run() {
        $this -> activity = 'running';
    }
}


// A short story
echo '<h1>World events</h1>';

// Once upon a time on Earth.
$earth = new World( 'Earth' );

// Something was happening.
echo '<hr>';
echo '<h2>Event #1</h2>';

// A dog named Spot was barking at John's house.
// John, like a good master, feeds Spot.
// Spot is eating now.
$spot = new Dog( 'Spot' );
$spot -> bark();

$john = new Man( 'John' );
$john_house = new Location( 'house', $john );

$spot_barking = new Sound( $spot, $john_house );
$earth -> event( $spot_barking );

// Another thing happen then.
echo '<hr>';
echo '<h2>Event #2</h2>';

// A dog named Rex sees Spot eating and barks at John's house too.
// John is not Rex's master. He's chasing Rex away.
$rex = new Dog( 'Rex' );
$rex -> bark();

$rex_barking = new Sound( $rex, $john_house );
$earth -> event( $rex_barking );

// Rex runs to his master's house, Bob, and barks.
// Bob is also a good master. He feeds Rex.
// Rex is eating too now.
echo '<hr>';
echo '<h2>Event #3</h2>';
$bob = new Man( 'Bob' );
$bob_house = new Location( 'house', $bob );

$rex_barking -> changeLocation( $bob_house );
$rex -> bark();
$earth -> event( $rex_barking );

// The End.
echo '<hr>';

?>

You’ll need PHP 5.4 for it. I’ve added a trait, just for fun, and I’ve used shorthand for arrays. You can change the Base trait to Base class, extend the classes, remove use and replace the shorthand for arrays with full 5.3 declaration.

<hr>

The base of the story is this: all objects are brought back together in a world. A sound is happening at a location, but it can change its location. A man responds to an event by identifying the source and taking appropriate action. A source (dog) will react to the action of the man.

The parallel would be web requests. The web server is a world. A sound is a request by a user (a dog). A man is the app delegated to handle the request and to send back a response to the user.

<hr>

The result is something like this:


World events

Event #1
Spot is barking at John's house.
John is feeding Spot. Spot is eating.

Event #2
Rex is barking at John's house.
John is chasing Rex. Rex is running.

Event #3
Rex is barking at Bob's house.
Bob is feeding Rex. Rex is eating.

Feel free to ask a question or dismantle the model. I won’t mind, I promise.