Navigation menu
Personal tools
Not logged in
Talk
Contributions
Create account
Log in
Namespaces
WoW
Talk
English
Views
Read
Edit
History
More
Search
Navigation
Home
Random page
Help using wiki
Editions
for WoW
for WildStar
for Solar2D
Documentation
for WoW
for WildStar
Reference
WoW
⦁ FrameXML
⦁ AddOns
⦁ API
⦁ WoW Lua
WildStar
⦁ AddOns
⦁ API
⦁ WildStar Lua
Engine
Tools
What links here
Related changes
Special pages
Page information
Site
Recent Changes
Editing
WoW:Object-oriented programming
(section)
Jump to navigation
Jump to search
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
== Classes == Classes are very similar to namespaces, and are implemented almost identically in Lua. Many programmers consider classes to be the fundamental concept of Object Oriented Programming, and the building block upon which other concepts can be created. They, like namespaces, are containers, but containers that can be duplicated. You can have many instances of a class, even though it's only declared once. Take the following C++ example: class Foo { public: int bar; -- Creates a variable "bar" }; -- The class "Foo" can now effectively be considered a variable type -- and thus we can make more "Foos." Foo baz, quux; -- Makes two Foos -- We can now access the int inside our Foos: baz.bar = 1; -- Note that these are quux.bar = 2; -- two different variables Classes allow reuse of code and organization of blocks which could be considered one element. For example, we could create a class "character," which stores the character's name, level, race, and class. Obviously all four of these are related, so putting the variables in a single class is better than creating four variables out in the open. Furthermore, we could have many characters, so instead of creating 4 variables every time, we could just create 1 (a new character) which then handles making all of the appropriate variables that are stored for that character. In the examples that follow in this section, we will use this example of storing characters in a class "Character," so that we can consistently provide examples to establish a better understanding. === Lua Classes === Lua classes are handled similarly to Lua namespaces. Like many concepts in Lua, their implementation relies on the power of Lua tables to perform their task. One thing Lua namespaces don't have to do, however, that Lua classes do, is create themselves. As stated earlier, classes need to be reproducable atomically -- that is, multiple instances of classes need to be able to be instantiated. This is done through a Lua concept called "[[metatables]]." Metatables are a complex concept. For information regarding them, see the [[http://www.lua.org/manual/5.1/manual.html Lua documentation]]. It is not, however, necessary to understand their entire purpose to create a class; it is only necessary to understand how they can be employed to accomplish our goal. ==== Instantiation and Construction ==== A class is constructed typically by a function. This provides a single point of creation which is very modular. Typically this function is called "new," though it is not required. First, the function must create a blank table. Then, it must imbue the table with the properties of the class. This may be functions, variables, or other contents which the class must store. the following example shows a simplistic class which creates a Character instance, which stores only the name of the character. Character = {}; function Character:new() local self = {}; -- Create a blank table self.name = "Unknown"; -- Make a name variable in the class return self; -- Return the instance end We can now create multiple instances of the class very quickly: local player1 = Character:new(); -- Create a Character local player2 = Character:new(); -- Create another player1.name = UnitName("player"); player2.name = UnitName("target"); We will discuss later why the name "self" is logical for this table we create, but it can actually be any name of your choosing. It is, however, typically seen as "self" because it has logical connections to the syntax used in functions, as will be discussed later. ==== Member Variables ==== A "member variable" is a term used to identify a variable that exists in a class. Member variables are not explicitly declared in Lua, but rather they are typically created during construction (when the class is instantiated). function Character:new() local self = {}; self.name = "Unknown"; self.level = 0; self.class = "Unknown"; self.race = "Unknown"; return self; end As you can see, the variables are created upon construction, much in the manner which you would find in a namespace. A table is created, and inside the table, variables are created and stored. That table is the new instance of the class. In this manner, Lua classes are different from many other languages, as variables can be added to a class at any time, but their initial state is always the same. ==== Member Functions ==== Like member variables, member functions are simply functions that are contained within the class. Even more interestingly, as functions are variables in Lua, functions can differ between classes. Since tables can store functions, adjusting the value of one of those functions can change the operation for that one instance, but leave the other instances identical. Sometimes this is preferred. However, sometimes, we want a change to propagate to all classes. ===== Explicit Function Creation ===== One form of creating member functions is shown in the following example. Like member variables, these functions can be created during construction, and thus it can be quickly seen how each class has its own instance of a function, as well, thus they can be changed in the future if desired: function Character:new() local self = {}; -- <Create variables as above here> self.isAlliance = function() if( self.race == "Gnome" or self.race == "Human" or self.race == "Night Elf" or self.race == "Dwarf" or self.race == "Draenei" ) then return true; else return false; end end return self; end So, it is possible to create functions simply by defining variables. But there is an inherent problem with this system. Let's take the example that this class is being used in a character list of everyone you've ever seen. This list could be in the thousands, and as such, we will need to make thousands of instances of Character. That means creating thousands of copies of the exact same function. Certainly this function does not need to be unique to each instance of the class. So how do we avoid this obvious waste of memory? By using a metatable. ===== Metatables ===== Metatables are defined by the Lua documentation as "an ordinary Lua table that defines the behavior of the original value under certain special operations" [http://www.lua.org/manual/5.1/manual.html]. We want to control one of these special operations. Specifically, we want to control how the table accesses variables that do not exist. While this may not seem immediately logical, it is a simple trick that we can use to convince Lua of functions that are defined for all instances of the class. First, let's create the function. The function is created much like you would find any other function available. Normally, a function is created using the following syntax: function foo() -- Code block end In our case, we need the function to be a member of the class we are defining. Thus, it needs to be contained within the base table, which in our case, is called "Character." For the above example "isAllance()," we would want to create the function like this: function Character:isAlliance() -- Code block end Note the slight difference: We use a colon instead of a period here. This is typically used for non-static functions (functions which rely upon the state of the class, or the variables inside the instance of the class, to determine their operation). This is needed because we need to know the variable "race" inside our instance. This determines whether the function returns true or false. The use of this colon states that, when this function is called, a [[Lua Scope#Function-local Variables|function-local variable]] will be created. The variable will be named "self" and is a reference to the instance of the class. Thus, we can use the variable "self.class" to determine the class of that one particular character. The Lua reference manual states the following[http://www.lua.org/manual/5.1/manual.html]: :The colon syntax is used for defining methods, that is, functions that have an implicit extra parameter self. Thus, the statement ::function t.a.b.c:f (params) body end :is syntactic sugar for ::t.a.b.c.f = function (self, params) body end We still have one problem, though. The instance of our class. While it is created by the Character table, it is not actually Character table itself, thus if we do the following: local me = Character:new(); DEFAULT_CHAT_FRAME:AddMessage(me:isAlliance()); This will fail, because me:isAlliance is undefined (me.isAlliance is never created; only Character.isAlliance). This is where metatables come in. A metatable can tell a table where to find indexes that do not exist. Thus, we can use it to help find functions which appear to not exist. Specifically, we specify that the instance should look up unknown indexes in the table "Character," so that when it tries to look for me.isAlliance, and does not find it, it will proceed by searching for Character.isAlliance, which does exist. This is done via the metatable's "__index" field (note that is two underscores, not one). The __index field states that if a variable cannot be found, it will be searched for in the table specified by __index. Thus, we can do the following during creation: Character = {}; -- To create a class, we must make a table Character.__index = Character; -- Set the __index parameter to reference Character function Character:new() local self = {}; setmetatable(self, Character); -- Set the metatable so we used Character's __index -- Imbue the class return self; end function Character:isAlliance() -- Perform calculation end Now, every instance of Character also has a way to access isAlliance(). Thus, we only create one copy of the function, so we save a lot of memory, but every character has a way to access that function and use it appropriately. Even more usefully, if another addon later hooks Character.isAlliance(), you will find that all instances will use the new, hooked function. This way, we don't have to modify every instance of the class. === Example Class === This is the fully written class we have created through the above methods. It has a constructor, creates the required fields, and gives each instance two functions: isAlliance and isHorde, which returns true if the character's race is set to an Alliance or Horde race, respectively. It also has a function load(), which loads data given a Unit ID via the Blizzard API. Character = {}; Character.__index = Character; function Character:new() local self = {}; setmetatable(self, Character); self.name = "Unknown"; self.race = "Unknown"; self.class = "Unknown"; self.level = 0; return self; end function Character:load(uid) if ~UnitExists(uid) then return false; end self.name = UnitName(uid); self.race = UnitRace(uid); self.class = UnitClass(uid); self.level = UnitLevel(uid); return true; end function Character:isAlliance() if ( self.race == "Human" or self.race == "Night Elf" or self.race == "Dwarf" or self.race == "Gnome" or self.race == "Draenei" ) then return true; else return false; end end function Character:isHorde() if (self.race == "Unknown") then return false; else return ~self:isAlliance(); end end And we can quickly use this class to get information about our 40 raid members, and store them in a table: local units = {}; for x = 1, 40 do units[x] = Character:new(); units[x]:load("raid"..x); end
Summary:
Please note that all contributions to AddOn Studio are considered to be released under the Creative Commons Attribution-NonCommercial-ShareAlike (see
AddOn Studio Wiki:Copyrights
for details).
Submissions must be written by you, or copied from a public domain or similar free resource (see
AddOn Studio Wiki:Copyrights
for details).
Cancel
Editing help
(opens in new window)