OOP design patterns in AHK

Talk about anything
User avatar
waetherman
Posts: 112
Joined: 05 Feb 2016, 17:00

OOP design patterns in AHK

11 Oct 2017, 03:09

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.
Image
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: OOP design patterns in AHK

11 Oct 2017, 03:20

I adjusted the code to make that easier.
Recommends AHK Studio
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: OOP design patterns in AHK

12 Oct 2017, 23:32

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
Recommends AHK Studio
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: OOP design patterns in AHK

13 Oct 2017, 02:49

Hello. I rearranged the example.
why you need self references

Code: Select all

this.that:=this
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

Code: Select all

tf := this.timerFn := objbindmethod(this,"method")
settimer(tf, 10)
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. :wave:
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: OOP design patterns in AHK

13 Oct 2017, 04:59

Then why didn't you just write circular references?
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
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: OOP design patterns in AHK

14 Oct 2017, 17:44

Then why didn't you just write circular references?
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.
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: OOP design patterns in AHK

17 Oct 2017, 08:19

I think I will split this into a discussion and a collection part since it get's less confusing.
Recommends AHK Studio
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: OOP design patterns in AHK

17 Oct 2017, 14:03

nnnik wrote:Name: super global automatically initialized Singleton
I enjoy seeing these ideas :thumbup:.
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. :!:
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: OOP design patterns in AHK

29 Dec 2017, 18:35

RUNIE wrote:Name: Run multiple __New methods automatically in an extended class
Hello, thanks for sharing your ideas :wave:.
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 :).
Only downside is that it'll store a variable in the instance called __NewInit.
Storing "" isn't much of a downside, we will however invoke __set if implemented ( not in v2 :thumbup: ). And we cannot pass parameters, for that we'd need to use the usual way of accessing base methods, that is base.method(...).

Regarding the __delete version, there will be problems when extending top, I guess.

Cheers.
User avatar
runie
Posts: 304
Joined: 03 May 2014, 14:50
Contact:

Re: OOP design patterns in AHK

29 Dec 2017, 19:44

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.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: OOP design patterns in AHK

14 May 2018, 13:15

evilC wrote:Not sure I agree with the Singleton implementations in here.
I think it is ok, and yours too, thanks for sharing :thumbup:

Code: Select all

c := this.__Class
this._Instance := new %c%()
You could do this._Instance := new this ;). (yes, I highlighted it manually, for no particular reason)

Cheers.
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: OOP design patterns in AHK

14 May 2018, 15:54

I would advise against using properties in general.
Also delegation over inheritance.
I changed something minor in my singleton impementation.
Recommends AHK Studio
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: OOP design patterns in AHK

14 May 2018, 17:53

nnnik wrote:I would advise against using properties in general.
If you mean property syntax, why?
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: OOP design patterns in AHK

15 May 2018, 01:09

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 13 guests