- Use Allman style (not K&R/1TBS style). [Easier to read.]
- Functions should do most of the work, the class should just be a wrapper. [Easier to reuse/maintain.]
- Classes are essentially functions (methods/properties) + data (keys).
- Classes often have two layers of complexity: the 'functions' (methods/properties), and how they are arranged/organised.
- Even if every method were just a call to an external function, classes can get complicated quite easily. The structure can be difficult to manage, and can often be under review (i.e. being regularly reorganised). The structures of a class's ancestor and descendant classes, and their interrelationships, may also be under review. This is why I advocate separating classes into two: external functions that do most of the work, and the class as a wrapper, essentially as a clerical tool for organisation.
- Also, by having most of the core functionality as separate functions, it is easier to follow the logic of the class, and it is easier for other classes to make use of some or all of the core functionality, or indeed to just use the functions directly in scripts. Also, it is easier to copy an individual function to a quick script, versus having to copy a class and all of its ancestors, just to use one method.
- Generally speaking, I would recommend that every class should have function equivalents. Exceptions:
- A class that tries to act as a new type of object e.g. a case-sensitive/case-insensitive dictionary object. Or a class that is a slight variant of the built-in AutoHotkey object, that has less/more/slightly different functionality.
- A class that acts as a 'quick hack' e.g. that takes advantage of the fact that objects have a __Delete meta-function, so that a specific action occurs when you delete an object.
==================================================
- I thought that an object wrapper for the SplitPath command could be a good example to use to demonstrate object classes with different coding styles.
- I would be interested to see different people try to code this, using their preferred styles.
- I am looking for a 'FileGet' function to return an object that works as below:
[EDIT: People could also post other simple classes, or links to them.]
Code: Select all
oFile := FileGet(vPath)
MsgBox, % oFile.dir
MsgBox, % FileGet(vPath).dir
;optional, to demonstrate what a function equivalent would look like
MsgBox, % FileGetPart(vPath, "dir")
;the equivalent using existing functionality
SplitPath, vPath, vName, vDir, vExt, vNameNoExt, vDrive
MsgBox, % vDir
Code: Select all
;file get part (OOP version)
Loop 3
{
vNum := (A_Index = 1) ? "" : A_Index
vPath := A_AhkPath
;vPath := A_ScriptFullPath
oFile := MyFileGet%vNum%(vPath)
MsgBox, % oFile.dir
MsgBox, % MyFileGet(vPath).dir
MsgBox, % Format("path: {}`r`nname: {}`r`ndir: {}`r`next: {}`r`nNameNoExt: {}`r`ndrive: {}", oFile.Path, oFile.Name, oFile.Dir, oFile.Ext, oFile.NameNoExt, oFile.Drive)
}
return
;==================================================
;my preferred approach:
;- Allman style (not K&R style)
;- class has a function equivalent
;- function does most of the work (class is a wrapper)
MyFileGet(vPath)
{
return new MyFileClass(vPath)
}
class MyFileClass
{
__New(vPath)
{
this.path := vPath
}
__Get(vKey)
{
if (vKey = "path")
return
return MyFileGetPart(this.path, vKey)
}
}
MyFileGetPart(vPath, vPart)
{
SplitPath, vPath, vName, vDir, vExt, vNameNoExt, vDrive
if (vPart = "path") ;path
return vPath
else if (vPart = "name") ;name
return vName
else if (vPart = "dir") ;directory
return vDir
else if (vPart = "ext") ;extension
return vExt
else if (vPart = "NameNoExt") ;name no extension
return vNameNoExt
else if (vPart = "drive") ;drive
return vDrive
}
;==================================================
;another approach:
;- K&R style (not Allman style)
;- class has no function equivalent
;- class's __Get meta-function does all of the work (no function)
MyFileGet2(vPath) {
return new MyFileClass2(vPath)
}
class MyFileClass2 {
__New(vPath) {
this.path := vPath
}
__Get(vPart) {
if (vPart = "path")
return
vPath := this.path
SplitPath, vPath, vName, vDir, vExt, vNameNoExt, vDrive
if (vPart = "path") ;path
return vPath
else if (vPart = "name") ;name
return vName
else if (vPart = "dir") ;directory
return vDir
else if (vPart = "ext") ;extension
return vExt
else if (vPart = "NameNoExt") ;name no extension
return vNameNoExt
else if (vPart = "drive") ;drive
return vDrive
return
}
}
;==================================================
;another approach:
;- K&R style (not Allman style)
;- class has no function equivalent
;- class properties do all of the work (no function)
MyFileGet3(vPath) {
return new MyFileClass3(vPath)
}
class MyFileClass3 {
__New(vPath) {
this.path := vPath
}
name[] {
get {
SplitPath, % this.path, vName, vDir, vExt, vNameNoExt, vDrive
return vName
}
}
dir[] {
get {
SplitPath, % this.path, vName, vDir, vExt, vNameNoExt, vDrive
return vDir
}
}
ext[] {
get {
SplitPath, % this.path, vName, vDir, vExt, vNameNoExt, vDrive
return vExt
}
}
NameNoExt[] {
get {
SplitPath, % this.path, vName, vDir, vExt, vNameNoExt, vDrive
return vNameNoExt
}
}
drive[] {
get {
SplitPath, % this.path, vName, vDir, vExt, vNameNoExt, vDrive
return vDrive
}
}
}
;==================================================