Intermediate OOP with AHK

Helpful script writing tricks and HowTo's
User avatar
nnnik
Posts: 3981
Joined: 30 Sep 2013, 01:01
Location: Germany

Intermediate OOP with AHK

03 Feb 2018, 07:08

After starting to write my first OOP tutorial I slowly started to realize that the tutorial I had planned to write exceeded the limitations of things that should be said in a small beginners OOP tutorial.
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:
https://autohotkey.com/boards/viewtopic.php?f=7&t=41332
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.
  1. Get the idea for a problem that could be solved
  2. Gather information and expand the idea and define what your code should do and what it shouldn't be used for
  3. Subdivide into responsibilities and turn each resposibility into an object
  4. Group the responsibilities/objects into a structure that allows for code simplification using inheritance
  5. Write a basic piece of code that can help you get started
  6. Write the entire code
  7. Check/debug your code
  8. Maintain and advance code if neccessary
The skeleton work would be somewhere around step 4. Now let's get started by working of this list.

The design phase described:
The Idea:
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.

Gathering Information
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.
The file system mainly consists of drives, directories and files.
  • 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…
The more you research the more complete your final result is going to be.

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:
My 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:
  • 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.
  • Don't "overdesign"
    You might notice that you can spent a very long time designing your code to solve all specific problems.
    Especially if the resulting product is very large you might be tempted to do so.
    However humans have their limits - you can only handle so much information at once and will make significantly less progress than when you first started designing.
    Sometimes you will be stuck between 2 alternative routes that you can take and still want to think things through.
    Let me tell you though - you will probably find that many considerations you made were not as important as you thought.
    On the other hand you will also find that you did not make many very important considerations and find yourself facing problems that might be difficult to fix with your current design.
    If you design too much you will find yourself with a product which has many cool feeatures and uses good practices and was a lot of hard work, but doesn't solve the problems at hand.
    On the other end of the spectrum - if you dont do enough design - you will often find yourself stuck on problems that involve changing and rewriting existing code - a lot of work for little effect.
    You will often feel lost and feel like you don't know what to do and where to go from here. The final product often focuses only on a specific area and might not be extensible or downright useless for most cases.
    That is if you do not fail.
    Generally speaking every developer has their own method and own balance of design and programming.
    This balance might change and you might even finding yourself going back and forth between the 2 steps.
Recommends AHK Studio
User avatar
nnnik
Posts: 3981
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Advanced OOP with AHK

03 Feb 2018, 07:09

OOP Design Ideals - targeting good practice
In the last section I said a lot about the process of designing OOP code. How to execute it and whats there to watch out for.
In this section I want to discuss what you should aim for when designing OOP classes.
To achieve this we need to discuss how to properly use OOP classes to their fullest potential.

When writing OOP or using OOP developers tend to use the common ground that authors and users of code have established.
This common ground allows users to utilize the created code in a very powerful way and avoids rewrites.
It allows developers to reduce the amount of code they have to write or specifically define, while retaining the option to add new modifications without breaking old things.
Other people - like OS or Hardware Manufacturers - also try to define and modify this common ground to make things work in their OS or on their Hardware.
We call this comon ground:
Good Practice
There are several principles for good practice revolving around how to properly use specific features of OOP.
I want to discuss these features and how you should use them in your code to gain the most out of them.

Interfaces
In OOP Inheritance is a strict way to inherit features. Sometimes you don't want to inherit all featues - but only part of them.
At other times you might have to inherit from multiple classes to do what you need to do.
In all these cases OOP seems unwieldy and you need to find a solution to a problem which was caused by using OOP in the first place.
This is especially problematic in typed languages like C++ or Java.

But there is a solution for this - namely Interfaces.
Interfeaces define a set of methods and their parameters and say what they should result in.
However Interfaces do not tell how the methods work exactly - this is up to the objects that implement these Interfaces.
Anybody can write an object that implements this interface and therefore change the exact behavior of code using this interface.

A good example of this is our AHK built in file-object the file object offers many methods to read and write data.
Imagine you want to write a control which display movies on a GUI and give people the option to use it.
One part of this project will be letting users of your class define which video is currently shown.
Maybe you would ask for a file name - however I present a different alternative - you expect a fileObject that can be read.

The reason for that is simple a file-object can be created by anybody - and anybody can change the behavior of your code.
Since you expect a readeable file-object most cases will be covered and you don't even need to do a lot when you just have a normal file.
When you expect a file path you will be able to open a video that is present on the disk, and contains exactly one video file.
When you expect a file-Object the data could be anywhere - it could even be send across the net or inside a .zip etc.
If you use the file-object the entire time to read all the data bit by bit when it is available your users would even be able to display a lifestream from the internet.
You don't even have a lot of extra work to do all you need to do is force yourself and the users of your code to use one specific interface.

Sadly there is no direct method of defining interfaces in AHK code - but you can add them documentation for your code/class.
If you write for yourself - no need to document then - just remember which interfaces do what and where you draw the borders around them.
Interfaces are also a good way to get started with the design. When creating a new class or library you ask yourself: "What do I want to sell to my user?" and then you define a set of interfaces.
Like Tomatoes and Rabbits and Carrots and whatever you like really.

When defining interfaces it's important that you strictly seperate what they do:
Our file-object only defines methods to get the files contents and navigate them.
It does not offer any methods of getting information about the files current position.
If it would people might use that information and their code would be incompatible with file contents that are in strange places.
(e.g. if you stream file contents from the net or get file contents from a zip.)
Recommends AHK Studio
User avatar
nnnik
Posts: 3981
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Advanced OOP with AHK

03 Feb 2018, 07:09

Reserved
Recommends AHK Studio

Return to “Tutorials”

Who is online

Users browsing this forum: No registered users and 8 guests