I then made the plan to write more tutorials - this is the second one and it might be the last in this series if I have planned everything correctly.
You can find the first OOP tutorial here:
In this tutorial we will once again split it into 3 different topics that I will handle: OOP theory, Design stuff and the practical part.
Intermediate OOP in AHK
In the last tutorial we mostly discussed how to handle the basic aspects of Object Oriented Programming - what it is and some standards.
In this tutorial we are mostly going to talk about how to design objects so that their user can make full use of the OOP power.
If we remember back we were just done with copying our old methods, from our first class to our second skeleton.
With this we had finished our skeleton work or?
Sadly we shouldn't even have started that yet. Normally before you write Skeleton or any other code, you start by designing the capabilities of your object.
At least thats the case if you do everything strictly in order.
- Get the idea for a problem that could be solved
- Gather information and expand the idea and define what your code should do and what it shouldn't be used for
- Subdivide into responsibilities and turn each resposibility into an object
- Group the responsibilities/objects into a structure that allows for code simplification using inheritance
- Write a basic piece of code that can help you get started
- Write the entire code
- Check/debug your code
- Maintain and advance code if neccessary
The design phase described:
The idea is it to create a class that people can relate to and that is capable of showcasing the differences between procedural programming and OOP. ( since I want to use it in a OOP tutorial )
After some thinking about it, I settled on the file system.
The design phase is where you clarify what those responsibility actually mean for your code.
We should start by collecting information to correctly create a good class that is useable later on.
- Information regarding the thing your class represents ( e. g. a File Class representing a file )
You can gather information regarding relationships with other objects, attributes and methods
- Information regarding the code environment ( similar functionality in other classes/functions or already working APIs in other languages )
You can easily simplify coding later on if you find an already working APi that you could relate your class to.
For example we can take most of the most of the names, used for our methods and properties of our file class, directly from the File Commands that are included in AutoHotkey.
Without this we would have to do the naming ourselves and these new names would probably confuse users that already know that API.
- Information regarding APIs you want to use ( e. g. the FileCommands of the AutoHotkey language )
If you want to successfully design your class it is really important that you become familiar with it before designing your class.
Of course you will always be more familiar with it after your class is written but getting used to it to some degree by doing a few tests comes first.
Try to rebuild a part/the core of the functionality that you want to have later.
- Information regarding tasks the object should handle ( e. g. the File class should be able to execute/run files )
You can get many ideas about how your object should look like by envisioning tasks or jobs that your class should handle.
This is very important if there is nothing that stands behind your object ( e. g. if you design a game object thats part of a game engine you design )
In this case becoming aware of other libraries or specific conventions found in other libraries or languages, also gains a lot of importance.
- Drives are containers of files and directories - they take up a specific part of the disks they are stored on. They can be empty or full depending on what kind of directories or files they contain. They have a formatting…
- Directories can contain other directories and can contain files…
- Files are only contained. They have data with a specific size that they point to…
Defining the "problem region"
After you have gathered some information define what you want to do and what you want to leave out.
The actual name for this is the "problem region":
I want to represent the file systems Directories, Drives and Files without having to care about the formatting of the drive.
Subdividing the responsibilities
After you have defined what you actually want to do you subdivide the responsibilities:
The single responsibility principleMy entire library is responsible for allowing access to the file system.
There should be something that is resplonsible for representing files, directories and drives.
There should be something that is responsible for representing file paths and file sizes.
Design tips:When applying the single responsibility principle, you make sure that every class is only responsible for one thing.
That means there is not one class that handles both the drives and directories. These are seperate classes.
It also means that there is no class that handles both files and file size - those are seperate classes.
The only time you won't need a subclass is when you have direct values.
- If you have similar classes or a "thing" that your class represents you can orientate yourself along them.
You might still find that you do not like the way other classes work - try to find the reason for that it might give you some new insight to your classes.
When you rewrite your classes they will probably be better already as you found flaws in your design that you can't fix.
- Try to seperate classes that handle seperate tasks in a way that allows you to reuse the most code.
For example a class that handles all the things your GUI does might not be a good idea since you won't be able to reuse anything.
However having a seperate ListView class and a seperate TreeView class already enables you to reuse these 2 while probably being less chaotic and easier to code. ( It would probably take a little longer though )
Of course writing code that you can reuse only makes sense if you want to reuse it.
Seperating general tasks, from tasks that are specific or handled in a specific way to your code, helps a lot.
Also this is one of the things you will fail at the most in the beginning - it takes experience to know the tasks you want to reuse and properly accounting for them in the beginning.
- Think of it in the terms of front end and back end
These are terms taken from Web Development. Front end describes what you user sees and how it's possible to interact with the site.
Backend describes the workings behind the scene that your user has no access to.
Similarily you might have classes that your user never gets to see directly.
Highlighting that fact by hiding backend classes a bit more and/or making it easier or more direct to access front end classes makes using your code easier on its user.
The back end will connect the front end with the underlying API/s.
Finding a way to directly connect the APIs with the front end while retaining all of its capabilities and making it easy to use, is one of the the main tasks of writing good code easily.