OOP design patterns in AHK
OOP design patterns in AHK
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
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
Re: OOP design patterns in AHK
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:
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:
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
}
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
Re: OOP design patterns in AHK
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:
See here for a bit more detail: https://autohotkey.com/boards/viewtopic.php?f=7&t=38149
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*)
}
}
}
Re: OOP design patterns in AHK
Nice topic nnnik, and interesting ideas from all contributors.
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,
Second, a more useful example, dealing with an object which has self-references,
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.
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
}
}
Code: Select all
; Pattern:
class proxy {
__new(p){
objrawset(this,proxy,p)
}
__delete(){
this[proxy].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
; Note: if __delete is called after all onExit routines has been called, the variable proxy might be blank,
; you could do this[ objgetbase(this) ].clear() to work around. However, in general it is recommended to ensure
; __delete is not called after all onExit routines has been called
}
}
; Example usage:
new o("A") ; not released
new op("B") ; released. Note, currently, if you assign the result of this line to a variable called z (but not a), the object will not be released. See comments in proxy.__delete for more details.
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".
}
}
Cheers.
Last edited by Helgef on 08 Feb 2019, 03:48, edited 2 times in total.
Re: OOP design patterns in AHK
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:
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
Re: OOP design patterns in AHK
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.
Only downside is that it'll store a variable in the instance called __NewInit.
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:
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 ""
}
}
Re: OOP design patterns in AHK
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)
}
}
Re: OOP design patterns in AHK
I think the __New is a good one.
However the __Delete one probably needs work
However the __Delete one probably needs work
Recommends AHK Studio
Re: OOP design patterns in AHK
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):
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
}
}
Re: OOP design patterns in AHK
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
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
}
}
}
Re: OOP design patterns in AHK
Sharing this, maybe can be useful for somebody:
Name: Superglobal instance of class.
AHK only: Yes.
Description: Replace class superglobal variable with its object instance.
Use-cases: Take advantage of meta functions when using static class method calling (e.g. to check if an object has been instantiated).
Name: Superglobal instance of class.
AHK only: Yes.
Description: Replace class superglobal variable with its object instance.
Use-cases: Take advantage of meta functions when using static class method calling (e.g. to check if an object has been instantiated).
Code: Select all
MsgBox, % Test.Hello()
obj := New Test(1)
MsgBox, % obj.Hello()
Class Test
{
Static _ := (Test := new Test) && 0
__New(bFlag)
{
this.FLAG := bFlag
}
__Call()
{
If ( !this.FLAG )
Return "Object not instantiated"
}
Hello()
{
Return "Hello!"
}
}
Last edited by cyruz on 08 Feb 2019, 06:04, edited 1 time in total.
ABCza on the old forum.
My GitHub.
My GitHub.
- vvhitevvizard
- Posts: 454
- Joined: 25 Nov 2018, 10:15
- Location: Russia
Re: OOP design patterns in AHK
AHK: v2 (tested with alpha100)
Name: The shortest
Description: I'm an apologist of super-compact writing style. Here is my version of self-initializing prototype class with __Get, __Call, etc meta-functions functioning.
Code: Test example:
and 2 variations w/o instantiation:
Name: The shortest
Description: I'm an apologist of super-compact writing style. Here is my version of self-initializing prototype class with __Get, __Call, etc meta-functions functioning.
Code: Test example:
Code: Select all
Class1:={base:Class1}
class Class1{
static __Call:=(_, _method, _args*)=>_args[1]
}
MsgBox(Class1.a2("1st arg"))
Code: Select all
class Class4 extends Class4.Class5{
class Class5{
static __Call:=(_, _s, _a*) =>_a[1]
}
}
class Class3{
static __Call:=(_, _s, _a*) =>_a[1]
}
class Class2 extends Class3{
}
;tests
msgbox(Class4.a2("1st arg"))
msgbox(Class2.a2("1st arg"))
Re: OOP design patterns in AHK
Code: Select all
class proxy {
__new(p){
; no idea whats that supposed to do
; a 'proxy' object is instantiated
; the 'proxy' super-global class is the used as a key in the instantiated 'proxy' object, to assign to this key, the object that the 'proxy' wraps
; why
objrawset(this,proxy,p)
}
__delete(){
; before the script starts, store a reference to the super-global variable 'proxy' (which is what contains the implementation of the proxy class),
; inside 'proxy.__delete()' method's 'prx' static variable
; why is this done?
static prx := proxy
; use the stored reference to the super-global var containing the class 'proxy' as a key,
; to access the instantiated 'proxy' object's member variable, which contains a reference to the object wrapped by the instantiated 'proxy'
; call this wrapped reference's 'clear()' method
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
}
}
the same can be achieved with:
Code: Select all
class proxy {
__new(p){
objrawset(this, "reference_to_wrapped_object",p)
}
__delete(){
this.reference_to_wrapped_object.clear()
}
}
Re: OOP design patterns in AHK
You can use whichever key you like, the point with using the class object was that it would not be used by the wrapped object.
When the program starts to terminate, eg after exitapp is called, variables are released in unknown order, so when the delete method is called, the proxy variable might be blank, hence I used have static variables since they seem to live longer, it is a bad idea and I will change it. We could use objgetbase I guess. Further, using onexit to release stuff is recommend over relying on things existing near the termination of the script.
Cheers.
When the program starts to terminate, eg after exitapp is called, variables are released in unknown order, so when the delete method is called, the proxy variable might be blank, hence I used have static variables since they seem to live longer, it is a bad idea and I will change it. We could use objgetbase I guess. Further, using onexit to release stuff is recommend over relying on things existing near the termination of the script.
Cheers.