Interfaces are a contract to ensure that a Type Definition meets a standard when it's properties and methods are called via code.
For Example: Lets say you have a Type Definition - circle.sqf
TAG_typ_Circle = [
["#type", "Circle"],
["#create", {_self set ["Radius",_this];}],
["Color","ColorGreen"],
["Radius", 1],
["GetArea", { pi * sqr(_self get "Radius"); }]
]
and you have some lines of code in a function:
params ["_type"] // TAG_typ_Circle is passed in
_myShape = createhashmapobject [_type,5]
_myShape set ["Color","ColorRed"];
_areaofShape = _myCircle call ["GetArea"];
If someone set "Color" to an RGBA array for some reason -or- someone made a related TAG_typ_Square type but used “CalcArea” instead of “GetArea” as the method name, your function would be broken by those changes. To counteract that, you would have to check: Color exists and is a string, GetArea exists, or check if it’s a Circle or Square, etc…. and fail softly in several conditions that you can't account for.
An alternative is to use an Interface. Let's say, we have an Interface IShape
TAG_ifc_IShape = [
["Color","STRING"],
["GetArea", "CODE"]
]
We can use the XPS_fnc_checkInterface function to check all those conditions at once:
params ["_type"] // TAG_typ_Circle is passed in
_myShape = createhashmapobject [_type,5]
_myShape set ["Color",[1,0,0,1]];
private _continue = [_myShape ,["TAG_ifc_IShape"]] call XPS_fnc_checkInterface; // This will check both "Color" and "GetArea" exists AND are string / code respectively.
if (_continue) then {
_areaofShape = _myShape call ["GetArea"];
} else { "ERROR!!!" };
The above would result in the Error condition. Because setting "Color" to an array is invalid.
Interfaces keep types that have related functionality adhering to the same rules. Maybe we do want to implement a Square type. If we want it to adhere to the TAG_ifc_IShape rules, it would have to be defined like so:
TAG_typ_Square = [
["#type", "Square"],
["#create", {_self set ["Radius",_this];}],
["Color","ColorBlue"], // This property is required and must be a string
["SideLength", 1],
["GetArea", { sqr(_self get "SideLength"); }] // This Method is required and of course, must be of type "CODE"
];
To take things a bit further, we can declare within the type that the object IS conforming to the rules and ACCEPTS that it will fail to be defined if it breaks them. By adding the ‘@Interfaces’ property and using the Preprocessor and Type Builder, the definitions will fail to ‘compile’ if they don’t follow the Interface rules. This is helpful when developing the type because, if there is an unintended typo (e.g “Colour” or “color” - note lowercase C), then we can get an error in the RPT telling us so.
For example, creating a TAG_typ_Square and modifying TAG_typ_Circle to both apply the same Interface:
TAG_ifc_IShape = [
["Color","STRING"],
["GetArea", "CODE"]
];
TAG_typ_Square = [[
["#type", "TAG_typ_Square "],
["@interfaces",["TAG_ifc_IShape"]],
["#create", {_self set ["SideLength",_this];}],
["Color","ColorBlue"],
["SideLength", 1],
["GetArea", { sqr(_self get "SideLength"); }]
],false,true,true,true] call XPS_fnc_buildTypeDefinition; //will succeed because it conforms to the Interface it declares to follow
TAG_typ_Circle = [
["#type", "TAG_typ_Circle "],
["@interfaces",["TAG_ifc_IShape"]],
["#create", {_self set ["Radius",_this];}],
["Color","ColorGreen"],
["Radius", 1],
["CalcArea", { pi * sqr(_self get "Radius"); }]
],false,true,true,true] call XPS_fnc_buildTypeDefinition; //will fail, send an error msg to RPT, and TAG_typ_Circle will become nil because it fails to declare a "GetArea" method
Changing CalcArea to GetArea for the circle type above will correct the issue and both types can now be used and will always pass the XPS_fnc_checkInterface
checks.
Note: In the C# language and some others such as Java, an Interface also declares the signature of the Method (both its inputs and output). For example:
int Interface.MyMethod(int x, int y)
//With an object that implements it
int Object.MyMethod(int x, int y)
// is different from
int Object.MyMethod(string val) //This does not conform and is treated as such