Type Access ModifiersBlitzMax Forums/BlitzMax NG/Type Access Modifiers
| I'm currently looking to implement Type access modifiers - that is, allowing Private/Public for Type fields/methods/functions.|
As you know, in BlitzMax, everything is Public. Which is great, but sometimes you may want to "hide" direct access to a field or method. This is called encapsulation ;-)
Here's a typical type and usage :
SuperStrict Framework BRL.Standardio Local da:TDirectAccess = New TDirectAccess(50) Print da.value da.value = 400 Print da.value Type TDirectAccess Field value:Int Method New(value:Int) Self.value = value End Method End Type
You set and fetch "value" as you wish, outputting..
Now, say you want to disable direct access to "value".. perhaps because you want to raise events whenever it changes, or for logging. With direct access there's no easy way to do this.
By forcing changes through a method, you gain more control...
SuperStrict Framework BRL.Standardio Local nda:TNoDirectAccess = New TNoDirectAccess(100) Print nda.GetValue() nda.SetValue(150) Print nda.GetValue() Type TNoDirectAccess Private Field value:Int Public Method New(value:Int) Self.value = value End Method Method GetValue:Int() Print "Getting value of " + value Return value End Method Method SetValue(value:Int) Print "Setting value to " + value Self.value = value End Method End Type
Getting value of 100 100 Setting value to 150 Getting value of 150 150
If you try to access the field directly..
nda.value = 100
your program will fail to compile
Compile error: Field TNoDirectAccess.value:Int is private
You can also make your methods private, including New().
With a private constructor, you can control who or when an instance of your type is created - for example, in Singleton pattern usage...
SuperStrict Framework BRL.Standardio Local s:TSingleton = TSingleton.instance() Type TSingleton Private Global _instance:TSingleton = New TSingleton Method New() End Method Public Function instance:TSingleton() Return _instance End Function End Type
If you try to create an instance of it yourself...
Local s:TSingleton = New TSingleton
You will get an error along the lines of
Compile error: Identifier 'new' not found.
because New is not visible outside of the Type.
I'm also considering adding a "Protected" access modifier.
Currently, Private limits access to within the Type. If you subclass the Type, your subclass will not have access to the Private fields or methods of its parent, since they are private to the Type.
Protected would in theory allow hierarchical access to fields and methods, but still be hidden from outside of the Type. (i.e. your subclass could call a protected method in its parent)
Is Private/Public enough, or should we also have Protected access?
Comments and suggestions welcome as always.
| I'd include Protected. Having recently got into learning C++ to some degree, I've come to really appreciate encapsulation and the formal control it gives.|
I can easily imagine wanting a subclass to access a private member, though, as if one of its own -- not that I have much practical experience in these, er, fields...
Compile error: Identifier 'new' not found.
This is a bit "misleading" as new also works if you do not have a custom private "method new()". I see it more as a "inbuilt" (like abs/min/max...).
So maybe better write it similar to the field-access-error:
Compile error: 'TSingleton.new()' is private
Compile error: Cannot create new instance of 'TSingleton', Instanciation set to private.
Of course it would be good to have that. Most often you will use "private/public" in framework code ("3rd party libs"). While it works now for "modules" (in vanilla) your implementation extends the availability to all types.
app/game-Framework-code is more likely to get extended ("TApp" -> "TMyApp" - or think of the wxMax-App-type). In that case "protected" would come in very handy.
Think next to it "auto getter/setter"-methods would be something considerable too ("method __setMyVar()" and "__getMyVar:type()").
Think this is another way of controlling the values returned or set - next to making things private.
| Wanting to access base class members from a derived class is common, so definitely +1 for Protected. |
| Also +1 for Protected -- that would be a great addition to have!|
And the more descriptive error messages Derron suggests I think would be good as well. I like his first suggestion best.
| First commit is in.|
I've done some basic testing and it seems to be working as expected - Private and Protected members are not accessible outside of the Type. Protected members are only visible to the type hierarchy (subclasses).
I'm finding that using Private in types completely (for me) changes how I use types now. It feels much more like using a "grown up" language like Java - that I work with every day. It's natural for me to do..
Type TMyType Private Field x:Int Field y:Int Public Method New(x:Int, y:Int) Self.x = x Self.y = y End Method ... etc End Type
.. without even thinking about it.
With that and Interfaces, I can write in a similar way I'm used to doing in my day-job.
Of course, it's completely optional, and you can carry on not using Private or Protected, and code as you always have in BlitzMax :-)
Any issues, apart from the error message (which I need to work on), please let me know.
| It should probably not be allowed to override members with a stricter modifier than the base member has, or to implement interface methods as anything but public, as it cheats the access restrictions:|
SuperStrict Framework BRL.StandardIO Type A Method M() Print "A.M" End Method End Type Type B Extends A Private Method M() Print "B.M" End Method End Type Local o:B = New B A(o).M ' "o.M" is not allowed, but this is, and it's calling the private method
It should probably not be allowed to override members with a stricter modifier
Good catch. I'll sort that out. Thanks!
| Latest commit should have fixed that now. |