OOP design patterns in AHK

Post a reply


In an effort to prevent automatic submissions, we require that you complete the following challenge.
Smilies
:D :) ;) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :!: :?: :idea: :| :mrgreen: :geek: :ugeek: :arrow: :angel: :clap: :crazy: :eh: :lolno: :problem: :shh: :shifty: :sick: :silent: :think: :thumbup: :thumbdown: :salute: :wave: :wtf: :yawn: :facepalm: :bravo: :dance: :beard: :morebeard: :xmas: :HeHe: :trollface: :cookie: :rainbow: :monkeysee: :monkeysay: :happybday: :headwall: :offtopic: :superhappy: :terms: :beer:
View more smilies

BBCode is ON
[img] is OFF
[flash] is OFF
[url] is ON
Smilies are ON

Topic review
   

Expand view Topic review: OOP design patterns in AHK

Re: OOP design patterns in AHK

Post by nnnik » 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.

Re: OOP design patterns in AHK

Post by swagfag » 14 May 2018, 17:53

nnnik wrote:I would advise against using properties in general.
If you mean property syntax, why?

Re: OOP design patterns in AHK

Post by nnnik » 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.

Re: OOP design patterns in AHK

Post by Helgef » 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.

Re: OOP design patterns in AHK

Post by runie » 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.

Re: OOP design patterns in AHK

Post by Helgef » 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.

Re: OOP design patterns in AHK

Post by Helgef » 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. :!:

Re: OOP design patterns in AHK

Post by nnnik » 17 Oct 2017, 08:19

I think I will split this into a discussion and a collection part since it get's less confusing.

Re: OOP design patterns in AHK

Post by Helgef » 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.

Re: OOP design patterns in AHK

Post by nnnik » 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 ] )
	}
}

Re: OOP design patterns in AHK

Post by Helgef » 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:

Re: OOP design patterns in AHK

Post by nnnik » 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

Re: OOP design patterns in AHK

Post by nnnik » 11 Oct 2017, 03:20

I adjusted the code to make that easier.

OOP design patterns in AHK

Post by waetherman » 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.

Top