tags:

views:

62

answers:

5

I am getting into OOP and I run into the following dilemma. I have the a class:

class Recipe {

    var $title;
    var $time;
    var $ingredients = array();
    var $instructions;
    var $category;

    function __construct($title, $time, $ingredients, $instructions, $category) {

        $this->title = $title;
        ...
    }

    function getTitle() {
        return $this->title;
    }

}

All the properties are public (by default). Do I have to define accessor methods for all these properties (e.g. getTitle) or can I just refer to the properties directly, like this:

...
$recipe = new Recipe($title, $time, $ingredients, $instructions, $category);
echo $recipe->title; // versus $recipe->getTitle();

It looks like I will save lots of time not having to define accessor methods. However I wonder what are the pros and cons of this kind of approach?

+2  A: 

Public properties don't need getter/setter methods, but it makes you slightly more prone to errors. Using accessors also let you enforce validation on data, whereas setting public properties directly can allow any data to be set.

If you take advantage of PHP's magic functions, you can write a dynamic getter/setter method for access to private/protected properties.

<?php


/**
 * Implements auto get/set
 * class Entity extends GetterSetter {}
 * $name = $entity->getName(); // return $this->name;
 * $name = $entity->getFullName(); // return $this->full_name;
 * $entity->setFullName("Ian"); // $this->full_name = "Ian";
 */

class GetterSetter {
    public function __call($name, $args) {
        if (substr($name, 0, 3) == "get") {
            $key = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', (substr($name, 3, strlen($name)-3))));
            if (property_exists($this, $key)) {
                //print "/* GET " . $this . "->" . $key . " = " . $this->$key . "\n */";
                return $this->$key;
            }
        } elseif (substr($name, 0, 3) == "set") {
            $key = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', (substr($name, 3, strlen($name)-3))));
            if (property_exists($this, $key)) {
                //print "/* SET " . $this . "->" . $key . " = " . $args[0] . "\n */";
                return ($this->$key = $args[0]);
            } else {
                print "Key not found: " . $this . "->" . $key;
            }
        }
    }

}
?>
Ian Wetherbee
What kind of errors? Messing up values?
Michael
It helps to enforce validation and reduce errors by preventing arbitrary data to be set.
Ian Wetherbee
Can't I just use **__get** and **__set** instead?
Michael
+4  A: 

The golden rule of OOP is always make your properties private! There are very few occasions where public properties would be allowed, but even then there's probably an alternative solution.

The reason is: if you make your properties public, anybody can change them to whatever they want. Most properties can't be just any value. Should your $title property be an integer? I highly doubt it. So what if you or somebody else accidentally sets it to an integer? You won't detect it. It'll set the value and your program will continue until it fails because a string was expected somewhere. Also, chances are your properties should be verified in some way before they are set. You would include all this verification within the setter of the property.

Even if you don't need to verify a property, you're still best to put them behind getters and setters in case eventually you do need to verify it.

Making your properties private ensures nothing messes around with your object when it shouldn't, avoiding any errors that result from it. :)

Sometimes you think "well, only I'll be editing my code, so nothing will happen". You should however practise doing it now. Get in the habit of doing it. You'll avoid any troubles later on.

sixfoottallrabbit
This makes a lot of sense! Why is then in PHP by default all the properties are set to public and not private?
Michael
I guess firstly because it means you don't have to stick "public" in front of every method. Also, there are probably back compatibility issues that require it to remain public. Although I can't seem to find any documentation on why exactly they are public by default. Note, in C++ the properties are private by default. Just make sure you know which way it is when you start using a particular language. :)
sixfoottallrabbit
[Please read up on PHP5-era OO](http://us3.php.net/manual/en/oop5.intro.php), which includes accessibility keywords. Your current style is PHP4-era, which nobody should be using for new code.
Charles
@Charles, **accessibility keywords** what are those? I cannot fing the reference in php doc. Do you mean magic methods like **__set** or **__get**?
Michael
-1 Golden rule is stOOPid. The fictionality of a $title attribute mischievously being set to an integer isn't a very rational reasoning for implementing setters. Hollow setters/getters don't contribute to code reliability, only to its appearance of. It's advisable to rather use __get() in scripting languages **when** an actual use case arises.
mario
@Michael: [private, public, protected](http://us3.php.net/manual/en/language.oop5.visibility.php). I used the wrong word. "Visibility" is how the docs refer to them.
Charles
@mario, I think that best practices should be stressed since it's obvious the OP is a beginner. Teach them the value of encapsulation and how to implement it, then illustrate when it's okay to bend/break the rule rather than the other way around. Knowing when not to stress about encapsulation is better than not being familiar with the concept at all.
kevinmajor1
+1  A: 

With getters/setters:

  • You can change the implementation. For example, later you might change getTime to count the number of $instructions instead of having a member $time.
  • Setters can throw exceptions, verify the input or new state, change other data, log, ...

Think of your object as how other objects would want to use it or what it should do. An object is more than a list of data types.

Ishtar
+3  A: 

I'd say one shouldn't use unnecessary loads of setters/getters especially in PHP, or your application could get remarkably slower. Here is a simple example :

<?php
class Recipe {

    public $title;

    function __construct($title){
        $this->title = $title;
    }

    function getTitle(){
        return $this->title;
    }

}

$a = new Recipe('potatoes');
$t1 = microtime(true);
for($i=0;$i<1000000;$i++){
    $x = $a->title;
}
$t2 = microtime(true) - $t1;
echo $t2.'s<br/>';

$a = new Recipe('potatoes');
$t1 = microtime(true);
for($i=0;$i<1000000;$i++){
    $x = $a->getTitle();
}
$t2 = microtime(true) - $t1;
echo $t2.'s<br/>';

?> 

Echoes :

0.25662112236023s

1.0309250354767s

4 times slower with getter!

darma
If I understand your code correctly, it is the other way around... it is 4 times faster with getter.
Michael
sorry i copied the results in the wrong order, let me edit my post - and feel free to try the test yourself :)
darma
+2  A: 

Getters/Setters are frowned upon in most scripting languages. They were originally introduced with Java and Java Beans specifically. Strong encapsulation makes sense in statically compiled code. In scripting langauges all access goes through an interpreter anyway, not directly to memory addresses; hence magic methods are there. In PHP you can route everything through __get, making wrappers redundant.

The use case for getters/setters are elaborate filtering and validation schemes. Unless you come up with a concrete attribute, it doesn't make sense to prepare hollow setters (getters seldomly transform the values anyway).

mario
+1, well put...
stereofrog