Suppose you are creating a new container control to encapsulate some functionality onto this new control. You’re already thinking about “encapsulation” which is a good thing.
Why, when you create this new container, would you then make all the controls on it public so code outside the container can change it ? That seems like it would be a good way to ruin the encapsulation you’re trying to achieve.
And, if you do this, any changes to your nicely set up container then may require broader changes in your application if, or when, you make changes to your container.
Lets walk through a quick and simple example that illustrates the issue.
Start a new desktop project.
Add a container control to the project – it will cleverly be named ContainerControl1.
Add a Generic Button to this container. It will be called “PushButton1”.
Make sure its scope is “Public”
Now add an instance of this container to Window1.
Add the Open Event to Window1.
In there put
ContainerControl11.Pushbutton1.enabled = false
And when you run the pushbutton on the container will indeed show up as disabled.
Now imagine you add a lot of other functionality to your application and there are several places where your code enables or disables this button under various conditions. And perhaps you reuse this container on several other windows.
And then you decide to make some changes to the container. And one of those changes is to rename the button to something more meaningful.
In the project we’ve built so far this isnt so bad. If we rename PushButton1 to “ContainerButton” and try to run again we’ll get an error saying
Window1.Open, line 1
Type "ContainerControl1.ContainerControl1" has no member named "PushButton1"
ContainerControl11.PushButton1.Enabled = false
Now in a small application this is manageable. You simply hunt for and change the names and off you go.
But, you may have to do this many times depending on how many times you set the enabled state of the button. And you have to update them all.
And none of this was necessary.
If instead of letting code outside the container reach in and manipulate the control directly we provided an API to do this and hid the innards then we could be sure we have fewer places to fix and that no code outside the container isnt somehow changing the setup in some way that will cause other issues.
In the example lets do this.
In ContainerControl1 add a method
SetButtonEnabled(assigns isEnabled as boolean) ContainerButton.enabled = isEnabled End Sub
Make the container buttons scope Private.
Now if you run you will get an error in the Window1.Open event saying
Window1.Open, line 1
Type "ContainerControl1.ContainerControl1" has no member named "Pushbutton1"
ContainerControl11.Pushbutton1.enabled = False
This is because the push button is now private and hidden to code outside the container control.
So we’ll fix this one line to
ContainerControl11.SetButtonEnabled = False
Now if you run the button is once more disabled. But now when we make changes to the ContainerControl we can focus on the container control and the methods it exposes to the rest of our code and not have to worry that we will have to fix a lot of other code elsewhere in our application.
Lets try this out.
Open the container and change the buttons name to “foobar”. And change our method we had for setting the enabled state to
foobar.enabled = isEnabled
And run the sample again.
We made two small changes and no other code had to change. If we had used this container many many times the time savings could have been significant. And the chance for odd bugs caused by code outside the container altering the containers contents significantly reduced.
Make your controls private and expose an API for them and save yourself a lot of grief.
My problem with methods is that you can’t set the inspector behaviour on them, so your custom controls aren’t easily dragged onto a window and their properties set in the IDE.
That’s why I would use computed properties – they are a pair of methods (with attached property) BUT can be used to show their properties in the IDE. Best of both Worlds.
Except computed properties cant be overridden by subclasses
They can be shadowed, but as my previous posts about shadowing have noted there are a couple glitches with that esp if you use locking
My problem is that you make it sound (especially to inexperienced programmers) as if using computed properties on ContainerControls to get/set the property of inside controls is the wrong way to do it. It isn’t.
Eg you have a TextField inside a ContainerControl, you add a .Text property to the ContainerControl, and access it using the Get and Set methods.
Now where is the problem with that?
I make no such assertion that one is better or worse. They both have pro’s and cons.
I happened to use a get / set pair of methods. The upside is that in Xojo a publicly exposed property, a computed property and a get / set pair of methods can be a mostly invisible implementation detail. And IF you happen to need to change it then you change the class that declares this and away you go. No outside user of this code is the wiser.
EXCEPT if you happen to rely on setting the Inspector Behaviour because you cant expose a get / set pair in the same way you can computed properties. So there is some nice capabilities there by using computed properties. But computed properties are also not virtual like a get / set pair of methods. So subclasses have to be aware of how to “properly shadow”, which makes them kind of fragile if new classes are inserted into the hierarchy, OR the base class uses a get / set pair of methods which are virtual and can be overridden in the subclass but cant be put in the Inspector Behaviour.
Choices Choices Choices