No. I dont meant the whole “God created the earth in 7 days” business. Sorry but this isnt going to be a discussion of monotheism and the belief systems they have. Maybe some other time when I get tired of discussing it with my uncle, the ex-Catholic priest, and his wife, the ex-Catholic nun. It does make for some fun discussions though 🙂
No, this is about the whole question of creating objects in Xojo and how, in our code, we can make it so some code can create new instances using NEW and other code cannot.
This pattern occurs frequently in Xojo.
Pictures can create and return Graphics objects but we cannot use NEW to create one. Queries of database objects can create & return RecordSets, but we cant create one using NEW.
So how can we accomplish this in our own code ? If we should not be creating new instances using NEW it would be nice to have the compiler help us enforce that rule. Its easy to make it so we can NOT use NEW by adding private or protected constructors. But then NO code can create a new instance. And thats not quite what we want. We want it so most code cannot create new instances, but a VERY limited set can.
In many cases some will just suggest that you do this “by convention”. All this means is “dont do that when you shouldn’t”. Its nothing the compiler helps enforce. You just dont write code that uses new with certain classes. Doing things “by convention” is often error prone. And the compiler doesnt help us at all in these cases.
Personally I don’t like things that the compiler could help me with that aren’t designed in a way that it CAN help me with. And this is one where we absolutely can design code that the compiler can help us with.
In some languages you can nest classes inside others. By doing this you can limit what is exposed in the classes that are embedded in the others. Xojo doesn’t have any means of doing this.1 So we need some other mechanism.
In some cases in Xojo you can use an interface to hide certain details from other classes. But if the interface is globally accessible then every other bit of code can still use that interface to manipulate the classes that implement it. And an interface can’t be used to create new instance so using one doesn’t seem like it will work very well.
What we need is a way to have a globally accessible class that effectively has no, or only private constructors, the some code can call to create new instances.
Lets mimic Picture and Graphics as our example. We want to work towards the same goal of making it so the compiler disallows
dim g as Graphics = Picture.Graphics
Lets start with putting all this in a module. We’ll call it PictureMimicry. And in that module we’ll add two classes – MyPicture And MyGraphics.
We’ll add a method to MyPicture, MyGraphics, that returns a MyGraphics object.2
Function MyGraphics() as MyGraphics
Make sure that the scope property of MyPicture and MyGraphics are both global so you can use their names anywhere without having to qualify them with the module name.
Now in our App.open event we could write
dim p as MyPicture
dim g as MyGraphics
p = new MyPicture
but, we could also add
g = new MyGraphics
and this is what we want the compiler to catch for us and tell us this is not allowed.
You might be asking “Why the heck did we put all this in a module then make everything global?” And, it turns out that everything being in a module is part of how we can make everything work the way we want.
When you put code, classes, etc in a module you can set a Scope property on those items. And one of the scope settings is Private. What this means is that no code outside the module can access these items, but things INSIDE the module can. So how does that help us ?
At first you might think “Oh great I’ll put a private constructor on the MyGraphics class and that will solve things”. But it doesn’t. That constructor is then PRIVATE to the MyGraphics class and nothing else call it. Not even code in the module can use it.
So how then do we create new instances of the MyGraphics class, make it so only code IN the module or in other classes in the module can create new instances, and any code outside the module can not ?
This is absolutely possible.
Maybe I should leave off here and let you all ponder this for a while and then follow up with the answer in another blog post. Replies are welcome as to how you think we do this.
- I’m told by ex-compiler engineers that the compiler could handle this BUT its not exposed in any way in any portion of the language or the IDE.
- We could have used a computed property and only implemented the getter just as easily. Either works.