OOP design patterns in AHK

Put simple Tips and Tricks that are not entire Tutorials in this forum
User avatar
nnnik
Posts: 3482
Joined: 30 Sep 2013, 01:01
Location: Germany

OOP design patterns in AHK

11 Oct 2017, 01:49

Today I want to bring you closer to one of my most favourite topics in the world of programming: design patterns.
Design patterns are like larger bricks of code that you will encounter and repeat a lot once you start seriously programming in OOP. They have one thing in common - they tackle certain problems in a adjusteable way.
It's fine if you imagine them like large lego bricks of code that you can connect with additional code. It mainly helps you planning and then developing what you planned leading to a higher success rate and better software.

In the following topic we will collect design pattern that have been used in AHK and describe them:
Name:If there is an official name for it use the official name if there isn't just think one up
AHK only:(yes/source) tells us whether this is a thing you just found useful for ahk or if it has an external source - if yes I would like to link an external source if possible
Description:Here you should explain shortly how this design pattern works and what it does.
Use-cases:When is this design pattern useful and what kind of problem does it tackle.
Code:Provide an example code for certain design patterns and use comments to show wht you described.

This topic is just for collecting the patterns.
You can discuss them here: https://autohotkey.com/boards/viewtopic ... 17&t=38448
Recommends AHK Studio
User avatar
nnnik
Posts: 3482
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: OOP design patterns in AHK

11 Oct 2017, 01:49

Reserved for index
Recommends AHK Studio
User avatar
nnnik
Posts: 3482
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: OOP design patterns in AHK

11 Oct 2017, 02:32

Since the explination I gave doesn't exactly show what I mean I guess I will go ahead and show the first 2 patterns:


Name: Singleton
AHK only: a common pattern first introduced by the gang of four - they also wrote a famous book about design patterns you should check that out
Description: A Singleton pattern is a pattern where a class only has one instance - here it is combined with automatic initialization. It works by writing to a static variable in the constructor once and then returning it's content ins
Use-cases: This is especially useful for libraries
Code:

Code: Select all

class singleton
{
	
	__New()
	{
		static init
		;This is where the instance will be stored
		
		if init ;This will return true if the class has already been created
			return init ;And it will return this instance rather than creating a new one
		
		init := This ; this will overwrite the init var with this instance
		
		;Whatever you want to do with your class constructor
		;for example:
		This.foo  := "bar"
		This.time := A_TickCount ;measure the current time ( we will use that later )
	}
	
	;whatever else you want inside your class
	
}

;To show you what I mean:
startTime := A_TickCount ;This is just to reduce the amount of numbers in A_TickCount 
sleep 400 ;This is to make current time pass
Loop 2 ;this is to show that the time since test was constructed remains the same
{
	test := new singleton() ;This will return the instance that was created in the beginning rather than creating a new one
	Msgbox % "Time when test was constructed:" . test.time - startTime . " ms`ncurrent sime: " . A_TickCount - startTime " ms" ;show that test is indeed a single instance
}
Name: automatically initialized singleton
AHK only: That's one thing that probably could be used outside of AHK
Description: A singleton pattern is a pattern where a class only has one instance - here it is combined with automatic initialization. It works by setting a certain key on startup and checking that key in it's constructor.
Use-cases: This is especially useful for libraries. Imagine you have a class Version of GDIp and it automatically calls gdipStartup. Of course this is a simple example - there are way more complex libraries out there.
Code:

Code: Select all

class staticSingleton
{
	
	__New()
	{
		static init := new staticSingleton()
		;The static keyword mkes sure that this get's called when AutoHotkey is just done parsing everything
		;It will happen before the auto execute section
		;So this will make sure that it initializes ASAP
		
		if init ;This will return true if the class has already been created
			return init ;And it will return this instance rather than creating a new one
		
		;Whatever you want to do with your class constructor
		;for example:
		This.foo  := "bar"
		This.time := A_TickCount ;measure the current time ( we will use that later )
	}
	
	;whatever else you want inside your class
	
}

;To show you what I mean:
startTime := A_TickCount ;This is just to reduce the amount of numbers in A_TickCount 
sleep 400 ;This is to make current time pass
Loop 2 ;this is to show that the time since test was constructed remains the same
{
	test := new staticSingleton() ;This will return the instance that was created in the beginning rather than creating a new one
	Msgbox % "Time when test was constructed:" . test.time - startTime . " ms`ncurrent sime: " . A_TickCount - startTime " ms" ;show that test is indeed a single instance
}
Recommends AHK Studio
User avatar
RUNIE
Posts: 281
Joined: 03 May 2014, 14:50
GitHub: Run1e

Re: OOP design patterns in AHK

11 Oct 2017, 02:40

Name: Subclassed method
AHK-only: kinda specific to AHK syntax
Description: Creates a new temporary instance of a subclass when the subclass is called as a method via the main class.
Use-cases: Making a subclass that can be called as a method
Code:

Code: Select all

MyClass.SubClass("Test", "Second", 3)

Class MyClass {
	Class SubClass extends MyClass.Functor {
		Call(Params*) {
			msgbox % this.ParseObject(Params)
		}
		
		ParseObject(Params) {
			for Index, Param in Params
				ParamText .= "`n" Param
			return SubStr(ParamText, 2)
		}
	}
	
	Class Functor {
		__Call(NewEnum, Param*) {
			(new this).Call(Param*)
		}
	}
}
See here for a bit more detail: https://autohotkey.com/boards/viewtopic.php?f=7&t=38149
Helgef
Posts: 3241
Joined: 17 Jul 2016, 01:02
Contact:

Re: OOP design patterns in AHK

12 Oct 2017, 18:28

Nice topic nnnik, and interesting ideas from all contributors. :thumbup:

I give a small example, which I find useful sometimes,

Name: Proxy / router / implicit reference object
AHK only: Wikipedia has an article Proxy pattern which seems to be relevant.
Description: In short, operate on an object through another object.
Use-cases: In ahk it is especially useful when dealing with self-refrences and meta functions. (Comment limited by my imagination)
Code: Two simple examples, first, a proxy class which only forwards method calls to a referenced object,

Code: Select all

; Pattern:
class proxy {	; Simple Method call proxy
	__new(p){
		objrawset(this,proxy,p)	; Using the class as key for the reference is convenient if you want to implement __set and __get in the proxy class, any other "unlikely" key will do ofc.
	}
	__call(f,p*){
		return this[proxy][f](p*)	; forward the method call to the referenced object
	}
}
; Example usage:
myTestProxy:= new proxy(new test)
myTestProxy.g(1)

class test{
	g(a){
		msgbox % a
	}
}
Second, a more useful example, dealing with an object which has self-references,

Code: Select all

; Pattern:
class proxy {	 	
	__new(p){
		objrawset(this,proxy,p)
	}
	__delete(){					
		static prx := proxy			; Use static, in case the class has been deleted, especially useful in the __delete method where the class might have been deleted due to exitapp().
		this[prx].clear()			; when a derived proxy object is released, it calls its referenced object's clear() method,
									; which clears self-references, then the (referenced) object can be properly released
	}
}
; Example usage:
new o("A")	; not released
new op("B")	; released

class o {	; Object with self-references, you need to "manually" call clear() to break self-references
	__new(name){
		this.name:=name		; For visual purposes
		this.that:=this		; self-reference
	}
	clear(){
		this.that:=""
	}
	__delete(){
		msgbox % "released " this.name
	}
}

class op extends o {	; The proxy will call the clear() method for you
	__new(name){
		base.__new(name)
		return new proxy(this)	; Returns the proxy object instead of "this".
	}
}
Real use example: I use a proxy for the hash table data structure, which implements __set and __get to enable the familiar array ((val:=) obj[key]:=val) syntax while avoiding the possiblity to overwrite methods, eg, obj["methodName"] := 37 stores 37 under the key methodName in the hash table, without overwriting the method.

Cheers.
Last edited by Helgef on 13 Oct 2017, 02:22, edited 1 time in total.
User avatar
nnnik
Posts: 3482
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: OOP design patterns in AHK

17 Oct 2017, 09:05

Name: super global automatically initialized Singleton
AHK-Only: Probably
Description: A Singleton that is stored in the super global variable of it's base class and initializes itself automatically - it works like the other singletons I have shown so far exept that it also overwrites its base classes super global variable.
Use-Cases: Libraries - it has the advantage over static super global Singletons that __Get, __Set, __Call and __Delete work properly.
Code:

Code: Select all

class superGlobalAutoSingleton
{
	
	__New()
	{
		;These 5 lines are the important part
		static init := new superGlobalAutoSingleton()
		if init
			return init
		classPath := StrSplit( This.base.__Class, "." )
		className := classPath.removeAt(1)
		if ( classPath.Length() > 0 )
			%className%[classPath*] := This
		else
			%className% := This
		
		;...add the rest of the constructor here
		This.text := "Abc"
		This.text2 := "def"
		This.name := className . " Instance"
	}
	
	getText()
	{
		return This.text . This.text2
	}
	
	__Delete()
	{
		Msgbox % "deleting " . This.name ;To show that __delete still works
	}

}
Msgbox % superGlobalAutoSingleton.getText()
Recommends AHK Studio
User avatar
RUNIE
Posts: 281
Joined: 03 May 2014, 14:50
GitHub: Run1e

Re: OOP design patterns in AHK

25 Dec 2017, 21:59

Name: Run multiple __New methods automatically in an extended class
AHK only: Yes.
Description: If you extend a class, the __New method in the base class will not run if it's overwritten in the class that extends it.
Use-cases: If you want to replicate how for example python handles inheritance.

Code: Select all

new Top

Class Top extends Bottom {
	__New() {
		msgbox % A_ThisFunc
	}
}

Class Bottom {
	; this will be put in Bottom.__Init(), which runs when a new Top is instantiated
	; so by calling Bottom.__New with the instance from Top, we can run both __New methods.
	__NewInit := Bottom.__New.Call(this)
	
	__New() {
		msgbox % A_ThisFunc
		return ""
	}
}
Only downside is that it'll store a variable in the instance called __NewInit.
User avatar
RUNIE
Posts: 281
Joined: 03 May 2014, 14:50
GitHub: Run1e

Re: OOP design patterns in AHK

25 Dec 2017, 22:19

This is a continuation of the above, which also supports __Delete.

Code: Select all

new Top

Class Top extends Bottom {
	__New() {
		msgbox % A_ThisFunc
	}
	
	__Delete() {
		msgbox % A_ThisFunc
	}
}

Class Bottom {
	__NewInit := Bottom.__New.Call(this)
	
	__New() {
		msgbox % A_ThisFunc
		
		if this.base.HasKey("__Delete")
			this.base.__Delete := this.base.base.__Delete
		return ""
	}
	
	__Delete() {
		msgbox % A_ThisFunc
		
		if this.base.HasKey("__Delete")
			Func(this.base.__Class ".__Delete").Call(this)
	}
}
User avatar
nnnik
Posts: 3482
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: OOP design patterns in AHK

26 Dec 2017, 04:23

I think the __New is a good one.
However the __Delete one probably needs work
Recommends AHK Studio
User avatar
evilC
Posts: 4354
Joined: 27 Feb 2014, 12:30

Re: OOP design patterns in AHK

14 May 2018, 12:10

Not sure I agree with the Singleton implementations in here.
For me, a Singleton pattern in AHK would look like this (If you used a property, of course it could be done using a GetInstance() method too):

Code: Select all

MySingleton.Instance.Test()
MySingleton.Instance.Test()

class MySingleton {
    Instance[]
    {
        get {
            if (!this.HasKey("_Instance")){
                this._Instance := new MySingleton()
            }
            return this._Instance
        }

    }
    
    Test(){
        ToolTip % A_TickCount
    }
}
User avatar
evilC
Posts: 4354
Joined: 27 Feb 2014, 12:30

Re: OOP design patterns in AHK

14 May 2018, 12:36

Here is a version that you can derive from to create a singleton.
It also stops the user from trying to new one up directly.

Uses Runie's "Run multiple __New methods" technique

Code: Select all

MySingleton.Instance.Test()
MySingleton.Instance.Test()
a := new MySingleton()

class MySingleton extends SingletonBase {
	__New(){
		a := 1
	}
	
	Test(){
		ToolTip % A_TickCount
	}
}

class SingletonBase {
	__NewInit := SingletonBase.__New.Call(this)
	
	__New(){
		if (this._Instance != -1){
			Msgbox % "Error: Tried to instantiate Singleton"
			ExitApp
		}
	}
	
	Instance[]
	{
		get {
			if (!this.HasKey("_Instance")){
				this._Instance := -1
				c := this.__Class
				this._Instance := new %c%()
			}
			return this._Instance
		}
	}
}

Return to “Tips and Tricks”

Who is online

Users browsing this forum: No registered users and 6 guests