Does each public property has to have accessor methods?
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 jus开发者_开发百科t 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?
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.
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!
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).
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;
}
}
}
}
?>
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.
精彩评论