Discoverability vs. Compatibility: The Dilemma of Evolving the Access Object Model
Stringly-typed properties: a necessary evil for maintaining backward compatibility in Access?
Maintaining backward compatibility is a top priority for the Microsoft Access development team.
This means that when they make changes to the Access object model, they have to be very careful not to break existing code. If they do, all .accde and .mde files created with previous versions of Access would need to be recompiled to work with the new version. This would be a major headache for Access developers and users alike.
So how does the Access team add new features and extend the object model without breaking binary compatibility?
The "Stringly-Typed" Properties Workaround
One workaround the Access team uses is adding "stringly-typed" properties to the Access object's .Properties
collection.
By using "stringly-typed" properties, the Access team can add new functionality without changing the underlying object model. This preserves backward compatibility with existing .accde and .mde files. Developers can access these new properties using string literals, rather than strongly-typed object references.
However, this approach comes with its own set of trade-offs:
- Reduced Compile-Time Safety
- Reduced Discoverability
Let's explore each of these trade-offs in more detail.
Reduced Compile-Time Safety
When using "stringly-typed" properties, we lose the benefits of compile-time type checking.
If we misspell a property name or pass the wrong data type, we won't know until runtime. This can lead to harder-to-detect bugs and slower development. It's a significant downside of using "stringly-typed" properties.
Reduced Discoverability
"Stringly-typed" properties are harder to discover and understand than strongly-typed object model members.
They don't show up in IntelliSense, and their purpose and usage may not be clear from their names alone. This can make it harder for developers to learn and use new Access features.
Encapsulating "Stringly-Typed" Properties
So what can we as Access developers do to manage these trade-offs in our own projects?
One strategy is to encapsulate "stringly-typed" properties behind strongly-typed wrapper classes or modules. By creating our own abstractions on top of the "stringly-typed" properties, we can improve compile-time safety and discoverability for our own code. We can also provide better documentation and examples for how to use these new features.
For example, let's say we want to create a strongly-typed version of the Image Control's ImageCtl.Properties("ControlSource")
property. We could create a custom class module named ImageControlWrapper
with the following code:
Option Explicit
Private m_ImageCtl As Access.Control
Public Property Get ControlSource() As String
ControlSource = m_ImageCtl.Properties("ControlSource")
End Property
Public Property Let ControlSource(ByVal NewValue As String)
m_ImageCtl.Properties("ControlSource") = NewValue
End Property
Public Property Set ImageControl(ByVal NewImageCtl As Access.Control)
Set m_ImageCtl = NewImageCtl
End Property
Then, in our form code, we could use this ImageControlWrapper
class like so:
Option Explicit
Private m_ImageWrapper As ImageControlWrapper
Private Sub Form_Load()
Set m_ImageWrapper = New ImageControlWrapper
Set m_ImageWrapper.ImageControl = Me.MyImageControl
End Sub
Private Sub SetImageControlSource()
m_ImageWrapper.ControlSource = "SomeFieldName"
End Sub
This is a bit of a contrived example, but you get the idea.
By encapsulating the "stringly-typed" property access behind a strongly-typed wrapper class, we get the benefits of compile-time checking and improved discoverability via IntelliSense. We can also add our own error handling, validation, or default values in the wrapper class.
Final Thoughts
Ultimately, the Access team has to make difficult decisions about how to evolve the Access object model over time.
By prioritizing backward compatibility, they ensure that existing Access applications continue to work with new versions of Access. But this compatibility comes at the cost of reduced discoverability and reduced compile-time safety. We can reintroduce discoverability and compile-time safety with wrapper classes, but that does create an additional maintenance burden onto us as developers.
The tension between adding new features and maintaining backward compatibility is yet another reminder that everything in life is a tradeoff.