Type Access Modifiers

BlitzMax Forums/BlitzMax NG/Type Access Modifiers

Brucey(Posted 2016) [#1]
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..
50
400


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

which outputs,
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.

:o)


BlitzSupport(Posted 2016) [#2]
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...


Derron(Posted 2016) [#3]
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

or
Compile error: Cannot create new instance of 'TSingleton', Instanciation set to private.



@ Protected
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.


bye
Ron


FireballStarfish(Posted 2016) [#4]
Wanting to access base class members from a derived class is common, so definitely +1 for Protected.


seriouslee(Posted 2016) [#5]
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.


Brucey(Posted 2016) [#6]
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.

Thanks!

:o)


FireballStarfish(Posted 2016) [#7]
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



Brucey(Posted 2016) [#8]
It should probably not be allowed to override members with a stricter modifier

Good catch. I'll sort that out. Thanks!


Brucey(Posted 2016) [#9]
Latest commit should have fixed that now.