Quantcast
Download
(15 Kb)
Download
Updated: 08-27-17 10:04 AM
Compatibility:
Shadows of Argus (7.3.0)
Tomb of Sargeras (7.2.0)
Return to Karazhan (7.1.5)
Legion (7.0.3)
Updated:08-27-17 10:04 AM
Created:08-23-17 03:23 PM
Downloads:173
Favorites:0
MD5:
7.3.0

LibObjectLua

Version: 2.2
by: Mayron [More]

1: About LibObjectLua

  • LibObjectLua is a framework intended on making object-oriented programming easier for Lua developers.
  • You can create classes and call them to instantiate new instances/objects modeled from those classes.
  • You can create interfaces that enforce functions to be implemented by classes.
  • You can enforce strict typing rules to class and interface function parameters and return values.
  • Each class can inherit from at most one parent class. All classes either directly, or indirectly, inherit from the Object class.
  • There are many useful Object functions that all classes inherit and can use.
  • You can create and export packages, and import packages or separate classes and interfaces.
  • There are many classes that can be imported from the framework, including a collections framework (similar to Java).
  • Eventually, more classes will be created and added to the framework.

1.1: NOTE:

There is a Test.lua file to see other working examples of how to use the Library!

2: Creating a Class

Lua Code:
  1. local LibObjectLua = LibStub:GetLibrary("LibObjectLua");
  2.  
  3. local MyPackage = LibObjectLua:CreatePackage("MyPackage");
  4.  
  5. local MyClass = MyPackage:CreateClass("MyClass");
  6.  
  7. function MyClass:Print(private, message)
  8.     print(message);
  9. end
  10.  
  11. function MyClass.Static:Foo()
  12.     print("This is a static (non-instance) function!");
  13. end
  14.  
  15. local Instance = MyClass();
  16. Instance:Print("Hello, World!");

Line 3 creates a new package. This package is only available to the developer until they choose to export it. All entities (classes and interfaces) are created using
the package object and can be found inside the package. This will be explained in later.

Line 5 creates a new class with the class name "MyClass". No parent was assigned to it (2nd argument), so it directly inherits from the Object class.
It does not implement an interface (3rd argument).

Line 7 declares an instance function for this class. It cannot be called directly from the class. It can only be called from an instance of the class.
The first argument is automatically given to the function by LibObjectLua when called. This is the instance's private data that can only be accessed
when inside the function body (unless giving explicit access by the developer, usually through getters and setters).

Line 11 shows how to create a static class function. These functions can and should be called directly from the Class table rather than from a class instance.

Line 15 demonstrates how to create an instance from a class; you simply call the class like you would a function.

You may have noticed that line 14 calls the Print method with one string argument. This is passed as the 2nd argument to MyClass.Print declaration
because the first argument is always the instance's private data.

3: Getters and Setters

Lua Code:
  1. local LibObjectLua = LibStub:GetLibrary("LibObjectLua");
  2. local MyPackage = LibObjectLua:CreatePackage("MyPackage");
  3.  
  4. local MyClass = MyPackage:CreateClass("MyClass");
  5.  
  6. function MyClass:GetTimeRemaining(private)
  7.     return private.timeRemaining;
  8. end
  9.  
  10. function MyClass:SetTimeRemaining(private, timeRemaining)
  11.     private.timeRemaining = timeRemaining;
  12. end
  13.  
  14. local Instance = MyClass();
  15. Instance:SetTimeRemaining(60);
  16. local timeRemaining = Instance:GetTimeRemaining();

Instance private data is passed in as the first argument to any instance (non-static) function call. This data is only accessible from within the function body
unless it is made explicitly accessible by the developer who created the class. This can be achieved using getter and setter functions as shown in the example
above. An external value is passed to the setter function (SetTimeRemaining) on line 15 and can be retrieved using a getter function as shown on line 16.
This technique can help to prevent outside interference against important class logic.

4: Function Definitions

This feature can help enforce strict rules upon your defined classes to ensure that parameter arguments and return values of class functions are the expected variable types. Note that including strict typing is completely optional.

Using the same example from the previous section, we can improve on this by adding some strict typing rules/definitions to the SetTimeRemaining
instance function:

Lua Code:
  1. local LibObjectLua = LibStub:GetLibrary("LibObjectLua");
  2. local MyPackage = LibObjectLua:CreatePackage("MyPackage");
  3.  
  4. local MyClass = MyPackage:CreateClass("MyClass");
  5.  
  6. function MyClass:GetTimeRemaining(private)
  7.     return private.timeRemaining;
  8. end
  9.  
  10. MyPackage:DefineParams("number");
  11. MyPackage:DefineReturns("number");
  12. function MyClass:SetTimeRemaining(private, timeRemaining)
  13.     private.timeRemaining = timeRemaining;
  14. end
  15.  
  16. local Instance = MyClass();
  17. Instance:SetTimeRemaining(60);
  18. local timeRemaining = Instance:GetTimeRemaining();

A package contains information relating to classes and function definitions, therefore declaring parameter and return value types must be done using the package object.

Lines 10 and 11 must be included straight before declaring the function, otherwise, the instructions for strict typing will not be set to the correct function definition. However, as mentioned previously, you can also choose to not include the definition calls at all as enabling strict typing of parameter and return values is optional. The first argument (the private instance data) can be ignored when creating strict typing definitions. Line 10 states that the first argument passed to the SetTimeRemaining function must be of type "number", which in this case it is. Any developer using the class must comply with this enforced rule.
Line 11states that the return value must be of type "number". The developer who created the class must comply with his own promise by returning a string value.

You can also declare that any value should be returned, as long as it is not nil:

Lua Code:
  1. MyPackage:DefineParams("any");
  2. MyPackage:DefineReturns("any");
  3. function MyClass:SetTimeRemaining(private, timeRemaining)
  4.     private.timeRemaining = timeRemaining;
  5. end

In this case, the return value and first argument can be a table, number, string, boolean or function, but not nil.

4.1: Optional Function Parameters and Return Types

Additionally, you can declare optional parameter arguments and return values by adding a question mark("?") character in front of the definition type.
However, they must be declared at the end of the declaration list after any non-optional definitions:

Lua Code:
  1. MyPackage:DefineParams("string", "number", "?table");
  2. MyPackage:DefineReturns("any", "function", "?boolean");

4.2: Object and Widget Names as Function Parameters and Return Types

Because all objects inherit the "GetObjectType()" function from the Object class, you can also validate parameter and return values to check whether or not
they are of a certain class type:

Lua Code:
  1. MyPackage:DefineParams("Frame", "Button", "?MySlider");
  2. MyPackage:DefineReturns("IHandler", "MyClass", "?SomeWidget");


5: Exporting a Package

Entities (such as classes and interfaces) do not need to be exported. These are created from a package and are automatically added to the package.
You should only be exporting packages. By default, packages are not made available outside of the file they are created in. To export a package, you need to
supply a namespace. A namespace is similar to a key. It is used to locate a package during the import process.

You can export in 2 ways. Using the libraries Export function directly, or when creating the package by supplying the namespace as the 2nd argument instead:

Lua Code:
  1. local LibObjectLua = LibStub:GetLibrary("LibObjectLua");
  2. local MyPackage = LibObjectLua:CreatePackage("MyPackage");
  3. LibObjectLua:Export(MyPackage, "RootPackage.AnotherPackage");

Lua Code:
  1. local LibObjectLua = LibStub:GetLibrary("LibObjectLua");
  2. local MyPackage = LibObjectLua:CreatePackage("MyPackage", "RootPackage.AnotherPackage");

In this example, both "RootPackage" and "AnotherPackage" are other packages, where "AnotherPackage" is a sub-package of "RootPackage".
Both of these packages do not have to be explicitly created using the CreatePackage function; instead, these are both created during the exporting process.
"MyPackage" is now a sub-package of "AnotherPackage".

If a developer wanted to create a class that inherits "MyParent" from another file where no reference to "MyParent" exists, they would either need to supply the full namespace:

Lua Code:
  1. local MyChild = MyPackage:CreateClass("MyChild", "MyPackage.MyClasses.MyParent");

Or, import it first using the full namespace and then including the parent class directly to the 2nd argument:

Lua Code:
  1. local MyParent = LibObjectLua:Import("MyPackage.MyClasses.MyParent");
  2. local MyChild = MyPackage:CreateClass("MyChild", MyParent);

6: Importing Packages or Entities

Unlike the Export function, the Import function can import not just another package with a valid namespace (a package that has previously been exported will have been
assigned a namespace), but it can also import a single entity (a class or interface).

Lua Code:
  1. -- File 1:
  2. local LibObjectLua = LibStub:GetLibrary("LibObjectLua");
  3. local MyPackage = LibObjectLua:CreatePackage("MyPackage");
  4. LibObjectLua:Export(MyPackage, "MyAddOn.Widgets");
  5.  
  6. local CheckButton = MyPackage:CreateClass("CheckButton");
  7. local Button = MyPackage:CreateClass("Button");
  8. local Slider = MyPackage:CreateClass("Slider");
  9. local TextArea = MyPackage:CreateClass("TextArea");
  10. local FontString = MyPackage:CreateClass("FontString");
  11. local Animator = MyPackage:CreateClass("Animator");
  12.  
  13. -- File 2:
  14. local CheckButton = LibObjectLua:Import("MyAddOn.Widgets.MyPackage.CheckButton");
  15.  
  16. local WidgetsPackage =  LibObjectLua:Import("MyAddOn.Widgets.MyPackage");
  17. local Button = WidgetsPackage:Get("Button");
  18. local Slider = WidgetsPackage:Get("Slider");
  19. local TextArea = WidgetsPackage:Get("TextArea");
  20. local FontString = WidgetsPackage:Get("FontString");
  21. local Animator = WidgetsPackage:Get("Animator");

Line 14 shows how you can import just one entity.
Lines 16 - 21 show how you can import an entire package and then use the Package's Get function to retrieve individual entities.
You can also create new entities to be added to the package that you have imported.

7: Creating an Interface

Interfaces can be a great way of specifying rules/patterns for a system. Interfaces cannot inherit or implement other entities.
"MyPackage:CreateInterface()", therefore, only takes one argument: the interfaces name. Interfaces, like classes, can be both exported and imported.

Lua Code:
  1. local LibObjectLua = LibStub:GetLibrary("LibObjectLua");
  2. local MyPackage = LibObjectLua:CreatePackage("MyPackage");
  3.  
  4. local IEventHandler = MyPackage:CreateInterface("IEventHandler");
  5.  
  6. MyPackage:DefineParams("string", "function");
  7. MyPackage:DefineReturns("boolean");
  8. function IEventHandler:Register(eventName, func) end
  9.  
  10. function IEventHandler:Execute() end
  11.  
  12. local Frame = MyPackage:CreateClass("Frame", nil, IEventHandler);
  13.  
  14. MyPackage:Implements("Register");
  15. function Frame:Register(private, eventName, func)
  16.     -- do stuff
  17.     return true;
  18. end
  19.  
  20. MyPackage:Implements("Execute");
  21. function Frame:Execute(private)
  22.     -- do stuff
  23. end
  24.  
  25. local myFrame = Frame();
  26.  
  27. myFrame:Register("PLAYER_ENTERING_WORLD", function()
  28.     print("Hello, World!")
  29. end);

Lines 8 and 10 show 2 interface functions being created. Interface functions do not, and should not, have any function body code used for implementation.
Instead, classes that are declared as "implementing the interface" must supply the function's implementation.

Lines 14 and 20 show how to declare that you're implementing an interface function using "MyPackage:Implements('functionName')". Just like defining strict typing rules for parameter argument and return value types, this must be included straight before defining the function. Defining the parameter argument and return value types yourself is not needed as the interface function definitions will be used instead.

All functions must be implemented for each interface attached to the class. You cannot implement from 2 or more interfaces that share the same function name as this would cause a clash.

You can implement many interfaces, but only inherit from one parent class:

Lua Code:
  1. local MyClass = MyPackage:CreateClass("MyClass", MyParent, IInterface1, IInterface2, IInterface3);

8: Constructors and Destructors

A class can be assigned a constructor and a destructor. A constructor is a function that is called when an instance is first created from that class. A destructor is called either by the garbage collector if the reference for an instance ceases to exist, or by explicitly calling "Destroy()" on the instance.

Lua Code:
  1. local LibObjectLua = LibStub:GetLibrary("LibObjectLua");
  2. local MyPackage = LibObjectLua:CreatePackage("MyPackage");
  3.  
  4. local Parent = MyPackage:CreateClass("Parent");
  5.  
  6. function Parent:Talk(private)
  7.     print(private.Dialog);
  8. end
  9.  
  10. function Parent:__Construct(private)
  11.     private.Dialog = "I am a parent.";
  12. end
  13.  
  14. local Child = MyPackage:CreateClass("Child", Parent);
  15.  
  16. function Child:__Construct(private)
  17.     private.Dialog = "I am a child!";
  18. end
  19.  
  20. function Child:__Destruct(private)
  21.     private.Dialog = nil;
  22.     print("Child object Destroyed!");
  23. end
  24.  
  25. local child = Child();
  26.  
  27. child:Talk(); -- prints "I am a child!"
  28.  
  29. child:Destroy(); -- prints "Child object Destroyed!"

The private instance data from the child object is passed to the parent function. This is why line 27 prints "I am a child!" and not "I am a parent.".
__Construct and __Destruct do not need to be manually called. They are implicitly called by LibObjectLua when required.

9: Calling Parent Functions from a Child Instance

An important Object function is "Parent()". Using parent on an instance object, whose class has a parent class, will cause the parent function to be executed instead.
This means that if a child and a parent class both have implementations of the same function (where both functions have the same key/name), the parent function
will be called and the private data sent to the function will be the original, child object.

Lua Code:
  1. local LibObjectLua = LibStub:GetLibrary("LibObjectLua");
  2. local MyPackage = LibObjectLua:CreatePackage("MyPackage");
  3.  
  4. local SuperParent = MyPackage:CreateClass("SuperParent");
  5. local Parent = MyPackage:CreateClass("Parent", SuperParent);
  6. local Child = MyPackage:CreateClass("Child", Parent);
  7. local SuperChild = MyPackage:CreateClass("SuperChild", Child);
  8.  
  9. function SuperParent:Print(data)
  10.     print(data.origin == "SuperChild");
  11.     return "This is SuperParent!";
  12. end
  13.  
  14. function Parent:Print(data)
  15.     return "This is Parent!";
  16. end
  17.  
  18. function Child:Print(data)
  19.     return "This is Child!";
  20. end
  21.  
  22. function SuperChild:Print(data)
  23.     return "This is SuperChild!";
  24. end
  25.  
  26. function SuperChild:__Construct(data)
  27.     data.origin = "SuperChild";
  28. end
  29.  
  30. local instance = SuperChild();
  31.  
  32. assert(instance:Print() == "This is SuperChild!");
  33. assert(instance:Parent():Print() == "This is Child!");
  34. assert(instance:Parent():Parent():Print() == "This is Parent!");
  35. assert(instance:Parent():Parent():Parent():Print() == "This is SuperParent!");

Line 10 will print "SuperChild", because line 35 passes the private instance data belonging to the SuperChild instance object, through the chain of "Parent()" calls,
to the SuperParent class.

All of the assert functions in this example will not trigger an error. This shows that each function implementation is being called correctly.

10: Object Functions

As previously mentioned and demonstrated, classes are either assigned a parent class manually or are implicitly given the Object class as their default parent class. This means that all classes inherit from Object directly or indirectly (Object is the parent of the parent's, parent's class, etc...). Therefore, all Object functions can be used by all classes.

Below is a list of all Object functions (without including the private instance data as the first argument):

Object:GetObjectType()
Returns the class name (string).
Object:IsObjectType(objectName)
@param objectName (string): The class or interface name.

Returns a boolean value indicating if the instance type is directly, or indirectly, the same type as the objectName supplied.
If the class of the instance inherits from a parent (or a parent's parent, etc...), or implements an interface whose name is
equal to the objectName argument, then the instance is said to be of that type and true will be returned.
Object:Parent()

Returns the parent class ready to be used with a parent function. Usually, you cannot call a non-static function on a class object, however the original
private instance data from the instance object that called the Parent() function (or chain of Parent() functions) is passed along to the final function call.
Object:Equals(other)
@param other (table): Another LibObjectLua class (or value).

Returns a boolean value indicating if two instances are equal. Two classes can be equal if all private data key and value pairs match
regardless of whether the instance table references are different.
Object:GetParentClass()
Returns the Parent Class of the instance.
Object:GetClass()
Returns the Class of the instance.
Object:Clone()
Returns a new instance whose private data key and value pairs match the original, cloned instance.

Example:
Lua Code:
  1. local Instance2 = Instance:Clone();
  2. print(Instance:Equals(Instance2)); -- prints true
Object: Destroy()
Removes all private instance data keys and calls the __Destruct function.
11: Other Package Functions not Previously Demonstrated

Package:ProtectEntity(class)
If you create a class or interface, you can mark it as protected. Protected entities cannot have functions re-assigned or modified.
Package:AddSubPackage(package)
You can add a previously created package to as a sub-package to another package without needing to export either of the packages.
Package:ForEach(func)
@param func (function): Apply a function to all entities found inside the package

Can be used to print all entity names using GetObjectType().
Package:Size()
Returns the size of the package, i.e. the total number of items found inside the package.
Package:GetName()
Returns the name of the package.
12: Other LibObjectLua Functions not Previously Demonstrated

LibObjectLua:SetSilentErrors(silent)
@param silent (boolean): LibObjectLua errors will be added to the error log if set to true, rather than being alerted in-game.
LibObjectLua:GetErrorLog()
Returns the error log (table), consisting of all accomulated errors triggered while LibObjectLua was set to silent mode (LibObjectLua:SetSilentErrors()).
LibObjectLua:FlushErrorLog()
Empties the error log.
LibObjectLua:GetNumErrors()
Returns the number of errors collected.

2.1:
-- Export function parameters changed
-- Export can only export 1 package at a time

2.0:
-- Added Package Class
-- Can only export packages!
-- Removed Import modifiers (*, +, -)
-- Added Object:Parent()
Optional Files (0)


Archived Files (4)
File Name
Version
Size
Author
Date
2.1
14kB
Mayron
08-27-17 09:17 AM
2.0.1
14kB
Mayron
08-27-17 03:39 AM
1.0.0 (renamed)
15kB
Mayron
08-24-17 12:07 AM
1.0.0
14kB
Mayron
08-23-17 03:23 PM


There have been no comments posted to this file.
Be the first to add one.



Category Jump: