This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
BigTwisty's profile picture

Set/get Members In An Object Oriented Class

Started by BigTwisty, 31 August 2013 - 02:52 AM
BigTwisty #1
Posted 31 August 2013 - 04:52 AM
I'm pretty new to this community, and to Lua in general, but I am hoping I can bring something fresh to the table. Please feel free to let me know if this has been done before.

I've recently found that by combining Closures with Metatables, I was able to create an inheritable object that supports get/set members much like a more typical object oriented language.

First, we start with the basic closure style definition. This is done by creating a table within the confines of a function. This allows us to define local variables within that function that will only be accessible from within functions added to the table from within the function. This would be a typical closure.


-- Closure function
function Class()
  local _privateVariable = 1
  local self = {}
  self.getVariable = function()
	return _privateVariable
  end
  return self
end

-- Create an instance of Class
object = Class()
print(object.getVariable()) --> 1
print(type(object._privateVariable)) --> nil

The primary advantage of this is that _privateVariable is completely inaccessible from outside the instance, which is a feature otherwise not present in Lua.

Where my particular implementation shines is in its use of metatables combined with the closure, as follows.


-- Closure function
function Class()
  local _members = { }
  -- Returns the instance
  return setmetatable( { },
  {
	__newindex = function(self, k, v)
	  if k=="_member" then
		assert(type(v)=="table", "Invalid member")
		assert(v.key~=nil, "Invalid member")[/size]
		_members[v.key]={get=v.get, set=v.set}[/size]
	  elseif _members[k] == nil then[/size]
		rawset(self,k,v)[/size]
	  else
		assert(_members[k].set~=nil,"Attempt to write to a read-only member: ",k)
		_members[k].set(v)
	  end
	end,
	__index = function (self, k)
	  if _members[k]~= nil then
		if _members[k].get~=nil then
		  return _members[k]:get()
		end
	  end
	end
  })
end
instance = Class()
instance._member = {key="type", get=function() return "class" end}
print(instance.type) --> class
instance.type = "something else" --> crashes.

Lets break this down.


return setmetatable( { },
{

This combines these two lines from the previous example and generates the metatable:


local self = {}
return self

The metatable provides the following functionality:


__newindex = function(self, k, v)

This function is called any time something tries to write a value to a key that doesn't exist in the instance. For instance, if the "print(type(object._privateVariable))" line were called on the second example, it would call the __newindex member of the metatable. The self variable, of course, references the instance, k is the key and v is value to be written.


if k=="_member" then
  assert(type(v)=="table", "Invalid member")
  assert(v.key~=nil, "Invalid member")
  _members[v.key]={get=v.get, set=v.set}

This section allows us to generate new members for the Class. It can be called from an instance of Class() or from another object that inherits from Class(). This would be called by "instance._member = { key="getSomething", get=function() return _privateVarialbe end }. If there were some private variable defined as _privateVariable, this member would make it accessible as a read-only member.


elseif _members[k] == nil then
  rawset(self,k,v)

This allows classes inheriting this base class to create new items, as long as they don't interfere with existing members.


  else
	assert(_members[k].set~=nil,"Attempt to write to a read-only member: ",k)
	_members[k].set(v)
  end
end,

This crashes with a warning that the program tried to write to a read only member. This can be handy for debugging. If it doesn't crash, it calls the set function to handle the value. This can be used when changing a member needs to do more than just update a value.
Note: The assert statement can be changed for an if statement if you do not want the program to crash. This will just cause the instance to ignore any attempts to write to a read-only member. It might be best to tie this to a global "debugging" boolean. Hmm…


__index = function (self, k)
  if _members[k]~=nil then
	if _members[k].get~=nil then
   return _members[k]:get()
end

This is called whenever a program tries to read a value from a key that doesn't exist in the object. If a member exists, and there is a get function for it, that function will be called.

This class really finds it's niche when inherited.


function InheritedClass()
  local self = Class()
  local _privateVariable = 3
  local function _privateVariableSetter(v)
	_privateVariable = 2 * v
  end
  Class._member = { key="var",
					set=_privateVariableSetter,
					get=function() return _privateVariable end }
  return self
end

This class inherits all the functiony goodness of the Class() object, but adds a local private variable, as well as get and set accessors for it.

I hope you've enjoyed this, and I look forward to your feedback!

Edit: I can't seem to fix the tab issues. I copied in from Notepad++ which I have set up to replace tabs with spaces, and I tried deleting the lines and retyping spaces manually. Nothing works, but I got weird issues that showed up randomly. Any ideas on how to visually clean up my code blocks?
Bubba #2
Posted 31 August 2013 - 08:57 AM
Nice! I haven't actually seen this done around here before. Well done :)/>
Molinko #3
Posted 31 August 2013 - 02:23 PM
I've recently been learning lua OOP even though im not experienced i keep hearing "Its not real OOP". I cant really understand why its seen that way.. Im curious to ask, why would you need an object to have private fields and why would you use a get and set system instead of just querying the field itself?? <– noobie question… Thanks for any explanation. Nice work BTW.
Bubba #4
Posted 31 August 2013 - 02:33 PM
I've recently been learning lua OOP even though im not experienced i keep hearing "Its not real OOP". I cant really understand why its seen that way.. Im curious to ask, why would you need an object to have private fields and why would you use a get and set system instead of just querying the field itself?? <– noobie question… Thanks for any explanation. Nice work BTW.

Lua is a strange language in that it is capable of Object Orientation but does not require it. That's probably why people say "it's not a real OOP".

And you would want to have setters/getters typically for large apis in order to prevent invalid usage.
Kingdaro #5
Posted 31 August 2013 - 02:41 PM
I've recently been learning lua OOP even though im not experienced i keep hearing "Its not real OOP". I cant really understand why its seen that way.. Im curious to ask, why would you need an object to have private fields and why would you use a get and set system instead of just querying the field itself?? <– noobie question… Thanks for any explanation. Nice work BTW.

From what I've read, using getters and setters instead of accessing the method directly allows you to make changes in the code more easily.

This isn't a good explanation (as I'm only scraping the surface of OOP myself), but here's an example. Let's say you have a game with a Player object, and that player has a health property. Wherever the player gets damaged, you would simply decrease the health. Maybe we have an enemy that damages the player, along with lava or spikes. (I've made comments in some of the methods to explain why we would need to rewrite them like that.)


Player = { health = 100 }

Enemy = {}
function Enemy:onHit(player)
  player.health = player.health - 10
end

Spikes = {}
function Spikes:onHit(player)
  player.health = player.health - 10
  --# maybe the spikes retract or break after being hit?
end

Lava = {}
function Lava:onHit(player)
  player.health = player.health - 10
  --# maybe the lava spawns a couple of nice particles?
end

And that's that. But what if we need to do something else after the player gets damaged? Perhaps push the player away from the obstacle, or turn the player red for a second. In that case, we would need to add multiple lines of code for each instance we decrease the player's health.

In this case, it's better to use a method to change the player's health, rather than change the behavior of many objects that do the same thing.


Player = { health = 100 }
function Player:damage(n)
  self.health = self.health - n
  self:getPushed()
  self:flash()
  self:doWhatever()
end

That way, the damaging behavior is easily implemented wherever we need, and we can make changes here without having to go through the rest of the code.
BigTwisty #6
Posted 31 August 2013 - 03:19 PM
You're fairly close, kingdaro, but not quite. Your Player:damage(n) is still a function, not really a member.

A get/set property approach as implemented in C# would be more like this:


private int _health
int health
{
  get
  {
	return this._health
  }
  set
  {
	if (val < this._health)
	  // Flash to show damage
	else
	  // Make cheezy hearts float up from your head
	this._health = val
	// Do some other stuff to update the character, I.E. you
	// could change textures depending on health level
   }
}[/size]

Then you would simply call


player.health = 22

And the rest of the functionality would occur automatically.

As to read-only properties, imagine you have a bunch of controls on the screen, each with a unique ID. You would want access to reading those IDs, say through "object.id". If you allowed the end user of an API to change the values of object.id you can imagine how that would wreck the integrated functionality of those objects. Read only is important to writing a robust and reusable API.
Kingdaro #7
Posted 31 August 2013 - 03:33 PM
It's supposed to be a function. In order to obtain the functionality you describe, you'd need to use metatable trickery with __index and __newindex, which quite frankly isn't necessary at all, when you can just define a function and tie it to a table/class.

Plus, :damage() isn't even specifically meant to be a getter or a setter, it's meant as a function representing the action of damaging the player. Adding getters and setters specifically for the purpose of them existing is a shitty idea.
BigTwisty #8
Posted 31 August 2013 - 05:27 PM
I believe you may have missed the point of the tutorial. The point of having it as a getter/setter is that the variable can be treated exactly as a variable would by the anything using the instance of that object. The point of encapsulation is that the user doesn't need to know what is happening behind the closed doors when they change the health variable, it will just happen.

The other advantage is the ability to include type validation in the setter like this…


assert(type(val)=="number","Invalid health value")

…and always be sure that no user of the API inadvertently changed "health" to some function or string on accident, breaking the internal function of the object. If you left it as a Damage(amount) function, you run the risk of over-writing that function down the roadbecause you didn't remember it was there.

Encapsulation is probably the toughest thing for new object oriented programmers to learn. The concept is that you make this THING that has a fool-proof interface, so you CAN'T mess anything inside it up, and then you never need to worry about how it does what it does again. It greatly increases code re-usability, which is kind of the primary point of the API system, is it not?

Edit:
Adding getters and setters specifically for the purpose of them existing is a shitty idea.

Use of getters and setters is just plain good code practice. It encapsulates your objects and gives you more control, period. Sorry you think thats "shitty". :)/>
ElvishJerricco #9
Posted 31 August 2013 - 08:05 PM
I had a tutorial somewhere around here about closure based OOP. I'm in the process of creating a new implementation of the idea. In my opinion, it's important that the writer of a class can write their closure function with no more than declaring locals and globals.

function Class()
    local privateVar
    function method()
        self.globalVar = 3
        print("method!")
    end
end
Being able to write like that, then using a simple convenience function to instantiate from that function and to set the class's superclass is really nice. By using function environments, this is easy. But the code I had in my old system had some major annoyances. I'm working on a new one that's based on this, but also many of the ideas behind Objective C. For one, you have no access to an object's data. You can only use methods, and there's convenience statements to auto generate setters and getters for private variables. And also the idea that classes are objects. That one's really powerful.
BigTwisty #10
Posted 31 August 2013 - 11:00 PM
I understand what you mean about being able to instantiate simply. I've been playing around with a slightly more advanced version of the Class function I showed for the tutorial. It is nice that the metatable magic only had to be done the once. I set it up so that all of the cool property type functionality could be completely inherited and managed by child objects, and it is making my life much easier.
Molinko #11
Posted 31 August 2013 - 11:38 PM
I've recently been learning lua OOP even though im not experienced i keep hearing "Its not real OOP". I cant really understand why its seen that way.. Im curious to ask, why would you need an object to have private fields and why would you use a get and set system instead of just querying the field itself?? <– noobie question… Thanks for any explanation. Nice work BTW.

From what I've read, using getters and setters instead of accessing the method directly allows you to make changes in the code more easily.

This isn't a good explanation (as I'm only scraping the surface of OOP myself), but here's an example. Let's say you have a game with a Player object, and that player has a health property. Wherever the player gets damaged, you would simply decrease the health. Maybe we have an enemy that damages the player, along with lava or spikes. (I've made comments in some of the methods to explain why we would need to rewrite them like that.)


Player = { health = 100 }

Enemy = {}
function Enemy:onHit(player)
  player.health = player.health - 10
end

Spikes = {}
function Spikes:onHit(player)
  player.health = player.health - 10
  --# maybe the spikes retract or break after being hit?
end

Lava = {}
function Lava:onHit(player)
  player.health = player.health - 10
  --# maybe the lava spawns a couple of nice particles?
end

And that's that. But what if we need to do something else after the player gets damaged? Perhaps push the player away from the obstacle, or turn the player red for a second. In that case, we would need to add multiple lines of code for each instance we decrease the player's health.

In this case, it's better to use a method to change the player's health, rather than change the behavior of many objects that do the same thing.


Player = { health = 100 }
function Player:damage(n)
  self.health = self.health - n
  self:getPushed()
  self:flash()
  self:doWhatever()
end

That way, the damaging behavior is easily implemented wherever we need, and we can make changes here without having to go through the rest of the code.

i see so youre saying that self:getPushed() is easier than doing self.pos.x = self.pos.x - 10, self.pos,y …etc
So calling a setter is really a way to keep readability and convenience when modifying an object from another method…Thank you for the replys fellas. This really is a great community.
theoriginalbit #12
Posted 01 September 2013 - 12:43 AM
The idea of using getters and setters in Object-Oriented programming is a joke! Using getters/setters breaks the paradigm of OO programming.

There is a problem with making getters/setters. Assume that we have the following Object (not in Lua for ease of conveyance)

public class Person {
	private boolean alive;

	Person() {
		alive = true;
	}
}

Now if we do getters/setters


public class Person {
	private boolean alive;

	Person() {
		alive = true;
	}

	public boolean isAlive()
	{
		return alive;
	}

	public void setAlive(boolean newValue)
	{
		alive = newValue;
	}
}

Now lets assume we want to use a hitpoint system for the person, we have to change the private variable and thus to conform to the getters/setters, we also need to change those. The code is now:


public class Person {
	private int hitPoints;

	Person() {
		hitPoints = 100;
	}

	public int getHitPoints()
	{
		return hitPoints;
	}

	public void setHitPoints(int hp)
	{
		hitPoints = hp;
	}
}

The problem occurs now with this that we've just broken potentially 100s or 1000s of method calls to those getters and setters, meaning we must now go through and change each one of those calls to the new methods.

So to stick to OO principles, we would actually code the above example, something like this:

public class Person {
	private boolean alive;

	Person() {
		alive = true;
	}

	public boolean isAlive()
	{
		return alive;
	}

	public void kill()
	{
		alive = false;
	}
}

Now with the above structure if we wish to change the internals of our Object, we can, without worrying about the effects it has on other classes. To show this to be true, lets change the above example to now use a hit point system

public class Person {
	private int hitPoints;

	Person() {
		hitPoints = 100;
	}

	public boolean isAlive()
	{
		return hitPoints > 0;
	}

	public void kill()
	{
		hitPoints = 0;
	}
}

Notice how the method signatures stay the same, meaning no broken code, but a completely different backend to the Object… This is the point to OO programming, a Class should not expose any of it's internals, making getters/setters something that breaks the fundamental OO principle.

There are several other reasons for not using getters/setters in OO programming, I've detailed just one here now, if you want to read up more about why not to use them I suggest Googling the issue, you'll find lots of discussion as it is a large point of contention in the programming community.
BigTwisty #13
Posted 01 September 2013 - 10:34 AM
I understand your point, but completely disagree. Lets take a GUI API, which is what I wrote this for. If I have an onscreen object with a property called "visible" I can either set visible to true or false or read its value. If I set visible to true I don't want to have to do anything else to make it visible as OOP principles state that the object should maintain itself. If I set a control.visible to true it should handle whatever code is necessary to draw it on screen without any fuss. The function method means that now I have to separate 2 functions to read, show or hide that control. Not to mention the fact that someone could overwrite that function somehow since lua stores function references as variables.
MudkipTheEpic #14
Posted 01 September 2013 - 10:56 AM
I'm not trying to make a flamewar against/for setters/getters in OO, but you wouldn't have to change the calls to the getters/setters, just the methods themselves, overloading them. (Correct me if I'm wrong, my Java is a little rusty.)




public class Person {
		private boolean alive;

		Person() {
				alive = true;
		}

		public boolean isAlive()
		{
				return alive;
		}

		public void setAlive(boolean newValue)
		{
				alive = newValue;
		}
}

While adding hitpoints, could be changed to:



public class Person {
		private int hitPoints;

		Person() {
				hitPoints = 100;
		}

		public int getHitPoints() 
		{
				return hitPoints;
		}

		public boolean isAlive()
		{
				return (hitPoints>0); //If he's not dead, he's alive. Maintains the old function call.
		}

		public void setHitPoints(int hp)
		{
				hitPoints = hp;
		}

		public void setAlive(boolean alive)
		{
				 hitPoints=(alive)?100:0; //Sets hitPoints to 100 if alive, 0 otherwise. Maintains the old function call.
		}
}

As I said before, correct me if I'm wrong.
theoriginalbit #15
Posted 01 September 2013 - 11:41 AM
-snip-


True, I understand where you're coming from as well, but there is a flaw to the example you gave, and a few things you're not thinking of… instead of detailing the first in words, I'll just do some Java code (again 'cause it's easier to convey)

Here is an example of a window object that doesn't use getters/setters, but still allows getting and setting of the visibility of the window


public class Window {
	public boolean visible;

	Window() {
		visible = true;
	}

	public void hide() {
		visible = false;
		// do anything else appropriate to hide it, maybe redraw?
	}

	public void show() {
		visible = true;
	 // do anything else appropriate to show it, maybe redraw?
	}

	public boolean isVisible() {
		return visible;
	}
}

Now yes you may argue that there is one more method there, but the method signature shows more intention of the outcome to the developer, doing `window.setVisible(true)`, while it should make sense as to what that does, in my opinion (and others that share the same getter/setter belief as me) the `window.hide()` is much easier for the developer to understand what will be done without exposing the inner workings.

Otherwise yes, you do have a valid point about how the user can overwrite the function pointer in the variable with something else. Where as with what you've created it is not possible to override the getters and setters, and the getters and setters are in your control. Don't get me wrong, what you've made here is a great use of Lua, and it provides a fantastic method of protecting variables, but I just believe that the use of getters and setters is not one you should be using when doing OO. However if you made a way to define the method name for the getter/setter then I would have no problem with this what so ever…. Oh also just noticed, remove all the 's from your example code… :)/>


-snip-
Yes that could be a way of making sure not to break existing code, and here is where my example was somewhat lacking… Sometimes it is not possible to actually do this, sometimes there is so much that can change in a backend that even if there was a getter the logic at the other end of the getter would be messed up… Also just as a point, adding more methods and using them in new objects, and leaving/modifying old ones just so you don't have to go and fix all their calls is not a good solution… It is messy, it is bad coding practise, and it means that your code has more upkeep in the event that you wish to change again later (among other reasons as well).

EDIT: slight fix… what was I thinking when I typed that o.O
Edited on 01 September 2013 - 09:42 AM
Engineer #16
Posted 01 September 2013 - 11:56 AM
The point of getters and setters can be interpreted in many different ways, and in my opinion it really depends on what you are doing with your code.
Lets take this little java code:

public class x {
  private int[] x = {};

  public int[] getX(){
    return x.clone();
   }

  public void addX( int newValue ){
     // checking if newValue is valid for our purpose (which we dont really have)
     x[ x.length  ] = newValue;
  }
}

As you can see I give it a get method, but I dont really give the array to the user. If I would do that, the user can bypass our checks and just set the heck it want. So if we add a method which just sets and checks if the integer is valid, then we are quarenteed to have an array full of numbers which we need, there is no useless crap in it.

However there are cases where setters are very usefull, for example that GUI API. If you write it correctly, you let the user just set all the values at once, and they can differntiate every render how its going to look. But therefor, we still have our checks. I see that something like this:


public class GUI {
    private int xMin;
    private int xMax;
    private int yMin;
    private int yMax;

   public int[] getValues(){
      int[] values = new int[4];
      values[0] = xMin;
      values[1] = xMax;
      values[2] = yMin;
      values[3] = yMax;
      return values;
   }

   // every setter looks like this:
   public boolean set_xMin( int value ){
      // checks
      if(!( value < 0 &amp;&amp; value > width_of_screen)){
         this.xMin = value;
         return true;
      }
      return false
   }

}

So again, it really depends in my opinion
theoriginalbit #17
Posted 01 September 2013 - 12:09 PM
-snip-
Look, positioning is a really hard one for me to say don't use getters/setters for… The main reason being is naming those variables something that fits a good naming convention is difficult, and they will only ever server one purpose, to position things, a position is never ever going to change from a single int (or float) location specifying a single axis… This positioning really is the only time I tend to accept getters and that is because, well, for the most part there is no better way to do it, getX, getY, etc. makes sense. I guess you could only accept a vector object and then set the x and y based off the vector x and y, but that's just overkill, verbose and kinda weird :P/> … As for setters, no, I do not agree that positioning should use setters, here is a setter I used recently in a game I made for uni (written in C++).


bool Player::move(int xOffset, int yOffset) {
  x += xOffset;
  y += yOffset;

  if (!currentWorld->isValidPosition(x, y)) {
	x -= xOffset;
	y -= yOffset;
	return false;
  }

  return true;
}

If I want to move the player up I do

player->move(-1, 0);
to move them to the right I do

player->move(0, 1);
and to move them diagonally down and to the left I do

player->move(1, -1);

For any parties interested, here is the full implementation of the Player class.
Spoiler


#include "Player.h"

Player::Player(World *world, int startX, int startY) {
  currentWorld = world;
  x = startX;
  y = startY;
  alive = true;
};

int* Player::getPosition() {
  return new int[2]{x,y};
}

int Player::isAlive() {
  return alive;
}

void Player::kill() {
  alive = false;
}

bool Player::move(int xOffset, int yOffset) {
  x += xOffset;
  y += yOffset;

  if (!currentWorld->isValidPosition(x, y)) {
	x -= xOffset;
	y -= yOffset;
	return false;
  }

  return true;
}

string Player::validMoves() {
  string dirs = "";

  if (currentWorld->isValidPosition(x, y-1)) {
	dirs = dirs.append(", N");
  }

  if (currentWorld->isValidPosition(x, y+1)) {
	dirs = dirs.append(", S");
  }

  if (currentWorld->isValidPosition(x+1, y)) {
	dirs = dirs.append(", E");
  }

  if (currentWorld->isValidPosition(x-1, y)) {
	dirs = dirs.append(", W");
  }

  return dirs;
}
Note that here I have `getPosition`, not `getX` and `getY`, while this method name is not ideal, at the time I couldn't really think of anything better than `getPosition`. :P/>
BigTwisty #18
Posted 01 September 2013 - 02:25 PM
First off, I would like to thank the latest posters for not allowing this to become a flame war. It has morphed into a good, positive discussion on different opinions of OOP principles. I'm not sure it exactly belongs in the Tutorials section, but as it directly relates to the tutorial at the top perhaps the mods will let it stay. :)/>

–Snip–


I think there may be some confusion as to getter/setter use here. In all of the getters and setters I'm seeing in examples, a public function is directly used to get or set the property. My original class makes it so that to the API user, these properties are used just like variables, making them in my opinion simpler and easier to use. Just because a function is called internally to the class when the API user changes or reads a property doesn't mean that the API user needs to know that.

Also, please keep in mind that different approaches are appropriate in different situations. Take Windows programming for example. A windows form has Show() and Hide() methods, but individual controls only have a Visible property. This makes sense when you consider the scope of the object in question. It is all about appropriate use of the available tools for the situation at hand. The purpose of my tutorial was not to enforce the use of class properties for everyone all the time, but simply to add one more available tool to the toolbox, to be used when appropriate.