Getters and Setters .. Why?

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
trukfixer
Forum Contributor
Posts: 174
Joined: Fri May 21, 2004 3:14 pm
Location: Miami, Florida, USA

Getters and Setters .. Why?

Post by trukfixer »

I came across some example code like this:

Code: Select all

function property($value=null) {
  if ($value!==null)
    $this->field = $value;
  return $this->field;
}
Now, as I understand it this is to ensure a variable is properly set, but what's the difference in using getters and setters versus doing

Code: Select all

$this->field = "some value";
I mean, if it isnt set, it is null , obviously

I can see why , if you dont set

Code: Select all

var $field = NULL;
//OR
var $field = "";
you can get a warning or error from php for undeclared variables, etc..

but still, what is the hoopla over using getters and setters versus just setting the damn thing either way ?

Code: Select all

($value !== null)?$this->field=$some_value:$this->field=null;
I really dont get it- why are there so many that say you should always use getters and setters, and why are they used *THIS WAY* as opposed to simply setting it?

what's the purpose? what's the theory behind it? explain to me why this *cough*Code Bloat *cough* is necessary ?

it makes absolutely no sense to me, since if a field is null, it would have to be declared to avoid notices/warnings, and if it isnt null, just set it, why use some silly contrived 6-lines-of-code-to-do-what-you-can-do-in-one getter or setter function ??

Give me some good, logical set in programming principles arguments, not "because its the way to do stuff in OOP" , which I wont listen to. I want to know WHY it is "the way do do it" , not "just because"
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

Basically, under object oriented rules all internal variables to an object are under control of that object. Allowing someone to set the variables willy-nilly without validation, "fixing" or various other interfaces is just plain silly. This is more a problem in PHP 4 than it is in 5. Lets say that a particular variable must be a single letter. If an object allows someone to set that variable directly, they could change it to anything they want. Because there's protection over that variable in order to properly handle things this object now has to have code or a method to verify the validity of that variable in each method. That adds even more bloat and time wasting than what you claim getters and setters add as bloat.

Under PHP 5, we have access controls. You can set a variable to be publicly available to set if you wish, however it is never recommended. At worst, every variable should be "protected," but I almost 100% of the time use private. Reason why? I don't trust child classes to set the variables correctly. Whether it's my own creation of children or someone else using them, it doesn't matter.

The end goal is consistent data the class can rely on and doesn't have to question. A getter and setter forces the data to always be valid (to the class) if written properly. So how do you like your data?
User avatar
hawleyjr
BeerMod
Posts: 2170
Joined: Tue Jan 13, 2004 4:58 pm
Location: Jax FL & Spokane WA USA

Post by hawleyjr »

I religiously use setters and getters I find it makes the code easier read and like feyd said it allows you to do E&O at the class level.
User avatar
neophyte
DevNet Resident
Posts: 1537
Joined: Tue Jan 20, 2004 4:58 pm
Location: Minnesota

Post by neophyte »

Okay you guys lost me a little. This is the first time I've heard of setters or getters and I can understand where trukfixer is coming from. Why would you do something like that? Am I right that his first snipet of code (the funtion) is a "setter"? If so what is a "getter"? Feyd I think I'm following you but can I trouble you to ask for a code example?
User avatar
hawleyjr
BeerMod
Posts: 2170
Joined: Tue Jan 13, 2004 4:58 pm
Location: Jax FL & Spokane WA USA

Post by hawleyjr »

Here is a stripped down class with 4 vars that have setters and getters.

Code: Select all

class MyClassGetAll extends QueryFunctions{
   
   
	var $a_var1=array();
	var $a_var2=array();
	var $a_var3=array();
	var $a_var4=array();
	var $query_str;
   
	function MyClassGetAll(){
   
		$qry = "SELECT var1, var2, var3, var4 
			FROM ".DB_NAME.".".SOMETABLE;
   
		$this->query_str = $qry;
		//echo '<HR><b>QUERY:</B><BR>'.$qry.'<HR>';
		$QF = new QueryFunctions( &$qry, __CLASS__ );
   
		if($QF->result){
   
			while($row = mysql_fetch_array($QF->result))
			{
				$this->a_var1[] = $row['var1'];
				$this->a_var2[] = $row['var2'];
				$this->a_var3[] = $row['var3'];
				$this->a_var4[] = $row['var4'];
   
				//USE ID AS ARRAY KEY
				//$ak = $row['var1'];
				//$this->a_var1[$ak] = $row['var1'];
				//$this->a_var2[$ak] = $row['var2'];
				//$this->a_var3[$ak] = $row['var3'];
				//$this->a_var4[$ak] = $row['var4'];
			}//END WHILE
		}//END IF NO ERROR

		}
   
		function getA_Var1(){ return $this->a_var1; }
		function getA_Var2(){ return $this->a_var2; }
		function getA_Var3(){ return $this->a_var3; }
		function getA_Var4(){ return $this->a_var4; }

   
		function setA_Var1($x){ $this->a_var1=$x; }
		function setA_Var2($x){ $this->a_var2=$x; }
		function setA_Var3($x){ $this->a_var3=$x; }
		function setA_Var4($x){ $this->a_var4=$x; }
   
   
		//ASSIGN VAR TO CLASS
		/*
			$MCGA= new MyClassGetAll();
			$a_var1=&$MCGA->getA_Var1();
			$a_var2=&$MCGA->getA_Var2();
			$a_var3=&$MCGA->getA_Var3();
			$a_var4=&$MCGA->getA_Var4();
			$query_str=&$MCGA->getQueryStr();
		*/
   
}
User avatar
neophyte
DevNet Resident
Posts: 1537
Joined: Tue Jan 20, 2004 4:58 pm
Location: Minnesota

Post by neophyte »

Okay that makes 'm0 betta sense.' Basicaly it's an interface to control access to the object properties. If you used it in a disciplined way it would limit the number of ways a variable can be changed. In the case of setters you could also force a type.

Code: Select all

function setA_Var1($x){ $this->a_var1= (int)$x; }
        function setA_Var2($x){ 
                  if(is_array($x){
                        $this->a_var2=$x; 
                  } else {
                              $this->a_var2 = null;
                   }
        }
Thanks for the code example hawleyjr.
timvw
DevNet Master
Posts: 4897
Joined: Mon Jan 19, 2004 11:11 pm
Location: Leuven, Belgium

Post by timvw »

If i wanted to "enforce" the type i'd use a different language than PHP...

Anyway, here is another example where properties(getters and setters) are useful..

For example a class to generate a SQL query

Code: Select all

<?php
class SQLBuilder {
  var $select;
  var $from;
  var $where;
  var $groupby;
  var $having
  var $orderby;
}
?>
Who needs getters and setters? We can easily modify the object by setting the $where clause ourselves...
A couple of weeks later we want to change our SQLBuilder that it always adds a "name='timvw'" to the where clause..

Without a setter we have to modify all our code and change the $builder->where = "..." to $builder->where = "name='timvw' AND " . "..."

(Some people might think: I'm smarter than that, the only place where that additional "name='timvw'" is required is when we request the class to generate the actual SQL. So if we update the MakeSelectSQL function we can include the additional requirement without having to change all our code that modified $where or adding a setter.. But they're mistaken.. Because they would also have to modify their MakeCountSQL function..)
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

This is an interesting question, especially since it is from someone who is just (possible) moving to accessor methods (getters and setters). They are a first logical step from procedural programming to OO (rather than just treating objects like structs). The main reason for accessor methods is that they provide an abstract interface to the data. The values of interfaces cannot be underestimated as they are a concept central to OO. But I can understand why accessors might be of little value if you don't jump with both feet into OO.

The thing about an interface is that it allows you to refactor code inside the class without having to change the code that uses it. Think about the code posted:

Code: Select all

($value !== null)?$this->field=$some_value:$this->field=null;
If you decide that you want don't want to allow null values, then you will need to go around to everywhere that uses that class and change this check. If you move the check inside the class then you are making the class responsible for validating its data -- which is where is should be. You are also following DRY and getting some code reduction in the process (because you might change all those null value checks in our code, except you forget one (I've never done that! ;))

So you move the code inside to get

Code: Select all

class ... {
function setField ($some_value) {
    if ($this->value !== null) {
        $this->field = $some_value;
    } else {
        $this->field = null;
    }
}
}
Now we can make any changes we need to inside this method without having to change any client code.

But many designers have moved a little beyond the traditional (Java style) getters and setters in a similar way that they have moved passed inheritance toward composition. That's not to say that getters/setters and inheritance shouldn't be used or aren't useful. It just that there are possibly better ways the smart guys have found to solve problems with out them. You might want to investigate the concept of "Tell, Don't Ask" type design (as opposed to US military rules ;)). It is a more imperative style that is not very natural to programmers coming right from procedural programming.
(#10850)
User avatar
trukfixer
Forum Contributor
Posts: 174
Joined: Fri May 21, 2004 3:14 pm
Location: Miami, Florida, USA

Post by trukfixer »

OK.. I think I am understanding a lot more now.. a getter could even be something like a function within a data handler class that you can send your POST array into it, and let the getter grab the key you are looking for , and the class functions do the validation and cleanup work all in one place,
and return the clean value, while in procedural you could do the same with a bunch of functions, using about the same amount of code, I guess, you would be using less code in the main files you are building because you can just do

Code: Select all

$clean_data = $data_class->Get_Post_Value($_POST,'post_data_key');
and a setter, you can use to set variables, with less concern for whether they can be "poisoned" by register_globals = on, among other things.. do I have this concept right?

I think I understand the benefit to them - I havent really dug into php5 at all, since we run production, high traffic servers, and the vast majority of code would break horribly if we upgraded to php5..

however, there *are* plans in the works that I have to build myself a new development server to co-locate at work that I can put in mysql 5 and php5 and postgres (and possibly oracle as well, if I can get a dev copy) so I can work on porting some existing apps to run on php5 .. (another reason I do want to transition to more OOP programming.. that plus the fact that I am doing a lot of work in Python as well, which is very heavily object oriented)

This is turning out to be aa useful discussion .. :)

I'll probably start another threat on another OOP concept that I dont really *get* what the whole point of it is, but that will be separate.....

I'd like to get a solid grasp of this idea first so I can know where it's handy to use it, and where it's just overkill :)

Thanks guys!
Bri!
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

You're on the right track trukfixer, kudos.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

I think you might be suprised how well your PHP4 code runs under PHP5. There will be a few problems and when you fix them you will may even feel like the code has improved a little.

For your example, a little less procedural would be:

Code: Select all

// on configuration
$request = new Request($_POST);     // though it is a superglobal so the class could just use it internally

// somewhere else in your code
$clean_data = $request->getPostValue('post_data_key');
(#10850)
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

Also, (I don't believe anyone mentioned this), if you're mocking the object, you can actually tally and set multiple time-based return values for a getter, while you can set expectations on a setter. This can't be done by directly interacting with the variables.

To be quite honest, I generally don't put very much validation in my setters, and it's real annoying when I have a 40 variable class, and I have to hardcode every single getter and setter (it gets sooo repetitive). Plus, I like my classes to be as flexible as possible: if the setter does validation, I always want a way to set things without having it get validated, etc (mostly during unit testing to see how fault tolerant the class is).

Regarding the repetition thing, someone's gotta have written some code generating code right? ...
josh
DevNet Master
Posts: 4872
Joined: Wed Feb 11, 2004 3:23 pm
Location: Palm beach, Florida

Post by josh »

Ambush,

If you use protected/private variables does your class need to be fault tolerant? Also hardcoding the setters and getters might be time consuming, but what happens down the road when you need to perform an action to each variable.. it could mean updating it in thousands of places... This is also a good place for variable variables

Code: Select all

function setter($key, $value) {
   switch($key) {
      case 'foo':
         if (preg_match('@bar@',$value)) return(false);
      break;
    }
    $this->$$key = $value;

}

Since you only have on setter you don't have to hardcode many setters, only hardcode ones you need validated in the switch, also if you need to test fault tolerance just comment out the switch altogether.. The only disadvantage is it lets you set undeclared variables (you can just check with isset() if you really wanted to). I know several PHP libraries use this (or something along the lines of this), cURL for example

Still not convinced?
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Ambush Commander wrote:To be quite honest, I generally don't put very much validation in my setters, and it's real annoying when I have a 40 variable class, and I have to hardcode every single getter and setter (it gets sooo repetitive). Plus, I like my classes to be as flexible as possible: if the setter does validation, I always want a way to set things without having it get validated, etc (mostly during unit testing to see how fault tolerant the class is).
It's probably better to start with no validation and add it when it is really needed. I sounds like you unconsciously know that the validation doesn't belong there.
(#10850)
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

jshpro2 wrote:Since you only have on setter you don't have to hardcode many setters, only hardcode ones you need validated in the switch, also if you need to test fault tolerance just comment out the switch altogether.. The only disadvantage is it lets you set undeclared variables (you can just check with isset() if you really wanted to). I know several PHP libraries use this (or something along the lines of this), cURL for example
It sounds like you are headed toward a standard Container with your setter class. These have become very common in PHP recently. The basic interface is:

Code: Select all

interface Keyed {
    function get($key);
    function set($key, $value);
    function has($key);
}
I ends up that you can use it for many of the common objects in a normal app.
(#10850)
Post Reply