mods note: split from https://autohotkey.com/boards/viewtopic ... 7&p=175700
As I already said on Discord, that staticSingleton class is neat. I wish there was a list of OOP patterns in AHK when I started, because I was really confused at my beginnings in this language, even though I already had a firm grasp on OOP, MVC etc. in other languages. I remember that I searched for a singleton pattern quite recently, and I found a forum posts, where I think Lexikos asks why not just use a class and have static properties in it; indeed, it worked, until I realized that it was just a global variable containing an object. So how do you avoid polluting the namespace, being able to just pass the singleton around as it’s needed — using a real singleton of course! Except you still have a class, which name is polluting the namespace, so next question is can you avoid that; I never really found time and will to tinker and see if I can wrap a class definition in something, e.g. another class (which can be disposed later), so to follow this example, every module would be a separate class, which defines subclasses, which instances then can be passed between modules.
Anyways, I find it important to note that nnnik’s singleton is not a typical singleton, as it initializes on the beginning of the program (as his example shows), which should be fine most of the time, but you might find a situation, where it isn’t: e.g. when you create a library with a ton of singletons, where you want to give a user the choice which he instantiates due to performance reasons (CPU, memory); you may also want to initialize the singleton not asap, but once the most critical part of the application initialized, like the user interface, so it feels more responsive, and both of these problems are solved by the typical singleton, which creates an instance on the first constructor call.
OOP design patterns in AHK
- waetherman
- Posts: 112
- Joined: 05 Feb 2016, 17:00
Re: OOP design patterns in AHK
Hmm yeah the code that is the pattern should always be above the example code.
Just curious but I don't quite understand why you need self references
Just curious but I don't quite understand why you need self references
Recommends AHK Studio
Re: OOP design patterns in AHK
Hello. I rearranged the example.
An explicit self-reference like that probably isn't a good example, in AHK we might more often deal with less explicit self-reference like circular-references. A more common example could be something like
in which case clear() in the second example would need to delete the timer and then clear the variable, this.timerFn:="", before the object could be released. A boundFunc is another example of a proxy object, it is a proxy for a func object.
why you need self references
Code: Select all
this.that:=this
Code: Select all
tf := this.timerFn := objbindmethod(this,"method")
settimer(tf, 10)
Re: OOP design patterns in AHK
Then why didn't you just write circular references?
I think my class indirectReference creates all sort of Proxy objects:
I think my class indirectReference creates all sort of Proxy objects:
Code: Select all
/*
CLASS indirectReference
author: nnnik
description: A class that is able to create safe indirect references that allow access to an object without creating a reference.
This allows AutoHotkey to perform deletion of Object once all direct references are freed.
You can use this to avoid circular references.
usage: newIndirectReference := new indirectReference( object, modes := { __Set:0, __Get:0, __Delete:0 } )
newIndirectReference: The indirect reference towards the objects passed to the constructor
object: The object you want to refer to indirectly
modeOrModeStr: Controls how the indirectReference is connected to the object directly
e.g. with a __Call mode all method calls towards the indirectReference will end up calling the same method with the same parameters in the object
*/
class indirectReference
{
static relationStorage := {}
static performanceStorage := {}
static accessStorage := {}
static modeStorage := {}
static baseModes := { __Call:indirectReference.Call, __Set:indirectReference.Set, __Get:indirectReference.Get, __New:indirectReference.New, __Delete:indirectReference.Delete, _NewEnum:"" }
__New( obj, modeOrModeStr := "__Call" )
{
if !isObject( obj )
return
if isObject( modeOrModeStr )
{
str := ""
For modeName, val in modeOrModeStr
str .= modeName . "|"
modeOrModeStr := subStr( str, 1, -1 )
}
if !indirectReference.performanceStorage.hasKey( &obj )
{
indirectReference.performanceStorage[ &obj ] := []
indirectReference.accessStorage[ &obj ] := []
obj.base := { __Delete: indirectReference.DeleteObject, base: obj.base }
}
if ( !indirectReference.performanceStorage[ &obj ].hasKey( modeOrModeStr ) | deleteMode := inStr( modeOrModeStr, "__Delete" ) )
{
if !indirectReference.modeStorage.hasKey( modeOrModeStr )
{
newMode := {}
for each, mode in strSplit( modeOrModeStr, "|" )
{
newMode[ mode ] := indirectReference.baseModes[ mode ]
indirectReference.baseModes[ mode ]
}
indirectReference.modeStorage[ modeOrModeStr ] := newMode
}
newReference := { base: indirectReference.modeStorage[ modeOrModeStr ] }
indirectReference.accessStorage[ &obj ].Push( &newReference )
indirectReference.relationStorage[ &newReference ] := &obj
if !deleteMode
indirectReference.performanceStorage[ &obj, modeOrModeStr ] := newReference
else
return newReference
}
return indirectReference.performanceStorage[ &obj, modeOrModeStr ]
}
DeleteObject()
{
for each, reference in indirectReference.accessStorage[ &This ]
{
indirectReference.relationStorage.delete( reference )
Object( reference ).base := ""
}
indirectReference.accessStorage.Delete( &This )
indirectReference.performanceStorage.Delete( &This )
if ( isFunc( This.base.base.__Delete ) && This.base.base.__Delete.name != indirectReference.DeleteObject.name )
{
This.base := This.base.base
This.__Delete()
}
}
Call( functionName = "", parameters* )
{
if !( indirectReference.baseModes.hasKey( functionName ) && !This.base.hasKey( functionName ) )
return ( new directReference( This ) )[ functionName ]( parameters* )
}
Set( keyAndValue* )
{
Value := keyAndValue.Pop()
return ( new directReference( This ) )[ keyAndValue* ] := Value
}
Get( key* )
{
return ( new directReference( This ) )[ key* ]
}
New( parameters* )
{
newIndirectReference := This.base
This.base := ""
__class := new directReference( newIndirectReference )
return new __class( parameters* )
}
Delete()
{
return ( new directReference( This ) ).__Delete()
}
}
/*
CLASS directReference
description: creates direct References from indirect ones.
usage: object := new directReference( newIndirectReference )
newIndirectReference: A indirectReference created by calling new indirectReference( object )
object: The object that is refered to by the indirectReference
*/
class directReference
{
__New( reference )
{
return Object( indirectReference.relationStorage[ &reference ] )
}
}
Recommends AHK Studio
Re: OOP design patterns in AHK
A circular reference implies a self-reference, and the example, i.e., this.that:=this, is circular too. Making a more intricate and less explicit self-reference doesn't change the point of the example.Then why didn't you just write circular references?
Re: OOP design patterns in AHK
I think I will split this into a discussion and a collection part since it get's less confusing.
Recommends AHK Studio
Re: OOP design patterns in AHK
I enjoy seeing these ideas .nnnik wrote:Name: super global automatically initialized Singleton
A comment to those who considers doing this, remember to put the __new method below any static class variables and methods using static variables which __new uses or calls.
Re: OOP design patterns in AHK
Hello, thanks for sharing your ideas .RUNIE wrote:Name: Run multiple __New methods automatically in an extended class
Note that returning "" from bottom.__new, isn't necessary in general, an explicit call to __new() doesn't return this, only an implicit call via the keyword new causes this to be returned, unless the method does return this ofc .
Storing "" isn't much of a downside, we will however invoke __set if implemented ( not in v2 ). And we cannot pass parameters, for that we'd need to use the usual way of accessing base methods, that is base.method(...).Only downside is that it'll store a variable in the instance called __NewInit.
Regarding the __delete version, there will be problems when extending top, I guess.
Cheers.
Re: OOP design patterns in AHK
Yeah I was going to use the one with new and delete, but it caused annoying issues when mixed with my IndirectReference class because the bases changed. However if you don't mess with bases or extend more layers, it'll work.
Re: OOP design patterns in AHK
I think it is ok, and yours too, thanks for sharingevilC wrote:Not sure I agree with the Singleton implementations in here.
You could do this._Instance := new this . (yes, I highlighted it manually, for no particular reason)Code: Select all
c := this.__Class this._Instance := new %c%()
Cheers.
Re: OOP design patterns in AHK
I would advise against using properties in general.
Also delegation over inheritance.
I changed something minor in my singleton impementation.
Also delegation over inheritance.
I changed something minor in my singleton impementation.
Recommends AHK Studio
Re: OOP design patterns in AHK
If you mean property syntax, why?nnnik wrote:I would advise against using properties in general.
Re: OOP design patterns in AHK
Mainly 2 reasons:
- Dificulty of debugging:
If you have something that shows you the content of an object and its classes you probably know of this issue. You cannot know if a specific key is a property or just a random object.
You cannot find out the amount of parameters either or it's intended name. The function object provides way more information. - Invoking a specific object
When you work with methods .this is just the first parameter. This allows a simple and uniform way to apply the code thats inside those methods towards any object.
This allows for some very advanced techniques e. g. it's possible to create base objects that immitate multiple inheritance.
Properties cannot be applied to any object - even if you find out that something is a property you still need to change the composition of your object or base object to achieve something similar - and thats only asking for problems.
This is just an example case but I think it showcases that properties are less flexible than methods.
Recommends AHK Studio
Return to “Off-topic Discussion”
Who is online
Users browsing this forum: No registered users and 61 guests