It is possible, in a language like php, to live without having to implement or copypaste any getter and setter and without a bunch of other rather standard functions. Although I believe this could be done more efficiently by making the language more powerful, I found a solution using the magic method __call.
Why it's not only about setters and getters
Here I describe how I came to a more sophisticated set of methods than just a setter and a getter, to implement along with each variable. At the end of this section there's a class describing it all. If you think it's obvious just skip this part.
Let's start with a simple class with a variable, a setter and a getter:
- class EXAMPLE {
- protected $name;
- function set_name($name) {
- $this->name = $name;
- }
- function get_name() {
- return $this->name;
- }
- }
Now, what I do normally, is to take away the get_, because it is very unlikely that I'll ever create a method named name with other semantics than return the variable $name.
- ...
- function name() { // instead of function get_name()
- return $this->name;
- }
- ...
A normal use of class EXAMPLE would be:
- $example = new EXAMPLE();
- $example->set_name('Nick');
- print $example->name(); // 'Nick'
But there's more to it. I want the variable name to be set or either initialized (only the first time) to a default value:
- ...
- function make_name() {
- return 'Peter';
- }
- function name() {
- return $this->name;
- }
- ...
The advantage of using a separate function make_name is that it can be overridden and reused (with parent::make_name()) in extending classes. The make_ functions can become very complex, but I know that they will be called only once.
Unfortunately this is not enough: I don't want to implement the function make_name in class EXAMPLE, but I want it to be available for subclasses. On the other hand, I want to be able use EXAMPLE (therefore it's not abstract), counting on the fact that name will always be set from the outside. This means that one must be able to check whether the variable can be initialized:
- ...
- function can_make_name() {
- return FALSE;
- }
- function make_name() {
- return NULL;
- }
- function name() {
- if (!$this->can_make_name()) throw(new Exception('Cannot make name and name is not set.'));
- $this->set_name($this->make_name());
- }
- return $this->name;
- }
- ...
I'll be able to use class EXAMPLE like this:
- $example = new EXAMPLE();
- $example->set_name('Nick');
- print $example->name(); // 'Nick'
But also to create a subclass:
- class EXAMPLE_EXTENDED extends EXAMPLE {
- function can_make_name() {
- return TRUE;
- }
- function make_name() {
- return 'Paul';
- }
- }
And use it like this:
- $example = new EXAMPLE_EXTENDED();
- print $example->name(); // 'Paul'
Note that the possibility to set_name in EXAMPLE_EXTENDED is still there, before and after default initialization.
Naturally the method can_make_name can be more complex than returning just TRUE or FALSE: depending on the implementation of make_name it will check, for example, if an other variable has been set (e.g. full_name, or id), if a connection to a database can be made, if the data exists for a certain id etc.
Now, I want to be able to check if $example->name is available, that is if it has been set or else if it can be created. I cannot do this by calling name directly, because it could have not been created yet (in addition to being protected), and I cannot do this by calling name(), because this will cause an exception. Which means I need an other method:
- class EXAMPLE {
- ...
- function has_name() {
- if ($this->can_make_name()) return TRUE;
- return FALSE;
- }
- ...
- }
Now I'll be able to write code like:
- // $example might be an EXAMPLE with or with no name set, might be an EXAMPLE_EXTENDED
- if ($example->has_name()) {
- print $example->name();
- } else {
- print 'Our example has no name';
- }
Let's go on: not all names are valid to be set, and I want to be able to check the validity of a value and throw an exception if someone attempts to write an invalid name[1]:
- class EXAMPLE {
- ...
- function can_set_name($name) {
- return TRUE;
- }
- function set_name($name) {
- if (!$this->can_set_name($name)) throw new Exception('Cannot set name.');
- $this->name = $name;
- }
- ...
Again, to have a separate function can_set_ is good because of the same reasons: it is overridable by other classes, while set_name very likely won't need to be.
As a next function, I want some effects to take place when the name is set, and again I want this things to be overridable by subclasses:
- class EXAMPLE {
- ...
- function prepare_name($name) {
- // add some other actions here, for example opening a file or other things.
- return $name;
- }
- function set_name($name) {
- if (!$this->can_set_name($name)) throw new Exception('Cannot set name.');
- $name = $this->prepare_name($name);
- $this->name = $name;
- }
- ...
Again if we want to "defer" implementation to subclasses, and to stay as general as possible EXAMPLE we'll write:
- class EXAMPLE {
- ...
- function prepare_name($name) {
- return $name;
- }
- ...
What we've got now is a set of useful methods which permit polymorphism in many important places. Here's the whole class:[2]
- class EXAMPLE {
- protected $name;
- function name() {
- if (!$this->can_make_name()) throw(new Exception('Cannot make name and name is not set'));
- $this->set_name($this->make_name());
- }
- return $this->name;
- }
- function has_name() {
- if ($this->can_make_name()) return TRUE;
- return FALSE;
- }
- function can_make_name() {
- return FALSE;
- }
- function make_name() {
- return NULL;
- }
- function can_set_name($name) {
- return TRUE;
- }
- function set_name($name) {
- if (!$this->can_set_name($name)) throw new Exception('Cannot set name.');
- $name = $this->prepare_name($name);
- $this->name = $name;
- }
- function prepare_name($name) {
- return $name;
- }
- }
As you can see this is a big set of methods which provide good extensibility. Too many compared to what the class EXAMPLE actually does.
Implicit methods
You might have implemented parts of this set of methods for any variable you added to a class. For some of them you might do it in the future. Maybe you didn't do it and now you cannot change the interface of a certain class and this means trouble. Getters and setters are the most common - but not all of them. People around the world are copypasting (or using templates for) setters and getters and losing time for which in my opinion is a missing language abstraction - let's call it implicit methods. Methods that are "implemented" implicitly (with default, commonly used results and effects) as soon as a variable is declared, as they would be in a class inheriting from a super class that implements them.
In a language where implicit methods are implemented, as one can write a class or an interface in a modern object oriented language, one should be able to implement sets of implicit methods ("meta interfaces"). private, protected and public would be just three very basic ones, followed by getandset, getsetmake or user defined ones.{{update||Actually someone came up with a solution for java, called Project Lombok. It uses eclipse annotations to insert getters and setters at compile time - and you can use the classes as if those methods were implemented.}}
Assuming I call strictgetsetmake the metainterface described above, the implementation of EXAMPLE would be:
- class EXAMPLE {
- strictgetsetmake $name;
- function can_set_name($name) {
- return TRUE;
- }
- function prepare_name($name) {
- return $name;
- }
- }
And assuming that can_make_ by default returns FALSE if make_ is not explicitly implemented and TRUE if it is, the EXAMPLE_EXTENDED class would look like:
- class EXEMPLE_EXTENDED {
- function make_name() {
- return 'Paul';
- }
- }
Thanks to implicit methods the interface of these two classes would be exactly the same - and no getters and setters should be written anymore.
Note that the classes' interface would still be defined staticly. No dynamic method attributions would happen.
Thinking further: one could just "implement" classes by writing:
- class ELEMENT {
- strictgsm $name;
- strictgsm $id;
- strictgsm $db;
- }
- class ELEMENT_DEFINED extends ELEMENT {
- function make_name() {
- return 'Paul';
- }
- }
- class ELEMENT_DB extends ELEMENT {
- function can_make_name() {
- if (!$this->has_db()) return FALSE;
- if (!$this->has_id()) return FALSE;
- return TRUE;
- }
- function make_name() {
- return $this->db()->get_element('elements', $this->id());
- }
- }
And use them like in:
- // $e is an instanceof ELEMENT
- $e->set_db($db);
- $e->set_id($id);
- if ($e->has_name()) {
- print $e->name();
- } else {
- print 'This element is not defined enough.';
- }
Magic functions
Although I think the described layer of abstraction (or something similar) would be the way to go, I decided to try to implement something in php, and I ended up using the magic method __call (i.e. the dynamic way). I wrote a class IMPLICIT_METHODS which takes care of method calls of nonexisting methods, and returning default values or doing default actions. This slowed my program down some, but made my programming much faster.[3].
What I'd write now is:
- class EXAMPLE extends IMPLICIT_METHODS {
- // name
- protected $has_name = TRUE;
- // any REALLY needed method
- }
The variable has_name (or any variable with name has_x) is a flag that tells the class IMPLICIT_METHODS[4] that I want to use all the methods described above. No more default getter, setter, checker, initializer etc. methods to write unless I want to.