Dave Stearns
Microsoft Corporation
June 1998
Every component developer has to design a programming model. When you write a Component Object Model (COM) control or dynamic-link library (DLL), you must decide how that component will be programmable or, in other words, how developers will write code to manipulate that component.
This usually opens up a whole set of questions: When should I use a property and when should I use a method? How should I name my properties and methods? How should I use and name enumerations? When should I raise events? Developers often can't find any answers to these questions and are left to figure out rules by examining existing published programming models. This can be quite hazardous, especially when the designers of those existing models were not operating by any logical rules either. This article presents some basic rules that I've learned by designing a number of programming models for Microsoft products. Any theory presented here was arrived at following lots of trial and error (sometimes more error than success!) and I have found that by following these rules, one can create usable, understandable and powerful programming models.
You may be confused by the term programming model, but it's really just the correct term for what most people call object model. By programming model I mean the set of interfaces, properties, methods, and events exposed from a component that allows a developer to write programs to manipulate it.
Designing a good programming model is just as important as designing a good user interface (UI) and, not so coincidentally, many of the principles used in UI design can be directly applied to programming model design. Good UI design attempts to let the user work at a much higher level of abstraction than the internal implementation. The UI presents a logical view of the functionality as opposed to the physical reality of that functionality. It expresses things in a way that matches how the intended user thinks—not necessarily how the system actually works.
In the same way, a good programming model doesn't just expose internal structures—it exposes its functionality at a higher level of abstraction so that the customer (the developer) can concentrate on what he or she wants to do and not on how to accomplish a simple task.
Before I discuss the basic rules of designing a good programming model, I first have to stop and explain what I mean by the term "programming model." Most Microsoft materials (documentation, articles, conference talks, and so on) use the term "object models" to refer to the set of interfaces, properties, methods, and events that are exposed from a component such as a COM control or DLL. However this is actually a misuse of the term.
This distinction in terminology may seem insignificant at first, but I have found that "programming model" is a much better term for describing what we are attempting to design. We must be fundamentally concerned about how other developers will construct entire programs using our components. It's not about exposing the objects that we use internally in the component—these are often way too granular and don't provide the developer (our customer) with a high-enough level of abstraction, which will ultimately make our customer's job easier. I'll discuss this difference between internal objects and higher-level abstractions in more detail below in the "Design Using Scenarios" section.
Designing a good programming model is just as important as designing a good user interface (UI) and, not so coincidentally, many of the principles used in UI design can be directly applied to programming model design. Good UI design seeks to let the user work at a much higher level of abstraction than the internal implementation. That's why we can drag and drop icons into folders to manage files, and the user doesn't need to know what mechanism is being used to track it all. The UI presents a logical view of the functionality as opposed to the physical reality of that functionality. It expresses things in a way that matches how the intended user thinks and not necessarily how the system actually works. The UI, in combination with how it reacts to user interaction, is often described as the "user model."
In the same way, a good programming model expresses its functionality in a way that matches how the developer wants to think about it. A good programming model doesn't just expose internal structures—it exposes its functionality at a higher level of abstraction so that the customer (the developer) can concentrate on what the customer wants to do and not on how one has to accomplish a simple task. For a concrete example of this, see the section below entitled "Design Using Scenarios."
The following sections concentrate on some basic rules I've learned in the process of designing a number of programming models for Microsoft products. Any theory presented here was arrived at by lots of trial and error (sometimes more error than success!) and I have found that by following these rules, one can create usable, understandable, and powerful programming models. Where possible, I'll note instances from real programming models that illustrate the concept.
Note that these rules are just guidelines. It's perfectly appropriate to break the rules when it makes sense to do so. The topics below should give you some understanding of the basic principles at work in designing programming models, so they will also help you to know when it's appropriate to break those rules.
The most important rule of all is to be consistent. Being consistent happens at two different levels. First, you must be consistent within your own library—nothing frustrates developers more than having something work one way in one part of your library and a different way in another part. Second, you should be consistent with other programming models with which your customers will likely be working. Note that I said "should"—sometimes it's difficult because there are few generally followed conventions and sometimes other libraries completely contradict one another.
For any design decision you have to make regarding your programming model, there is probably a wealth of precedent established in the component community (those writing COM controls and DLLs). However, not all those precedents are good or make sense, so don't blindly follow a convention established in your favorite library unless you determine that it meets your customer's needs. Consistent stupidity is still stupidity.
If consistency is the most important rule, designing your programming models around scenarios is a very close second. This is typically the most often overlooked rule.
"Scenarios" is a word we use quite a bit at Microsoft. It's a fancy word that simply means "things real people will typically do with your stuff." When we design UI, we think about what people will commonly do in the UI and we strive to make those things easy and automatic.
In the same way, our programming models need to be designed with common scenarios in mind. What will developers typically want to do with your component? Which features are more for advanced users and which features should be very easy to use? Note that the focus here is on the features and not on the internal objects, data structures, or functions.
For any new programming model, first decide what the top 5 to 15 programming scenarios are and document them. Then, after you've thought through the basic structure of the model, but before you've done any detailed work, write the code for those scenarios. Write the code that makes the most sense for the scenario—in other words, write the simplest, most logical code segment that you, as a developer using the component, would expect to have to write. You will quickly realize where shortcuts need to be made and where your overall structure is too complicated.
For example, suppose you were designing the programming model for a component that exposed the functionality of the file transfer protocol (FTP). The implementation of this component would have to deal with hosts, sockets, headers, streams, directories, files, and so on. But that's not what developers using this component want to think about (that's why they want to use a component and not develop it themselves). They want to do things like (in order of priority):
The first two tasks are by far the most important, and should be incredibly simple to code. For example, to copy a file form a host to a client, I would expect to have to write code like this:
'assuming the control or object is created and assigned to "MyFTP"
My ""
My "foo/bar/somefile.zip", "c:\temp\somefile.zip"
My
This is only three lines of code, but it could be even simpler. For example, the connect/disconnect methods could be made optional, allowing the host name to be included in the source file name (for example, ""). If they were not used, the component could automatically do connection pooling, providing adequate performance in most common situations. Also, the second parameter of CopyFile could be made optional, where the destination file is automatically named after the original and placed in the current program directory.
Keep in mind that all of that advanced functionality, such as explicit connection management, asynchronous copying with events to notify of progress, and custom stream implementations that let the developer stream the file into a database or a Exchange server folder, can still be enabled. The point is that the developer should not have to deal with these when doing simple, common tasks.
This kind of design uses the principle: "Make the common things easy and make the advanced things possible." It creates a design that works a bit like most VCRs. Just about anyone can push a videotape in and watch a movie. That person does not have to know that the VCR can be programmed with 50 different events, that they can manually adjust the tracking, or that it can freeze the picture and move frame by frame forwards and backwards. These are all features that are there for use, but they are not exposed to the novice user.
While the first two rules are more philosophical, the following topics deal with some more mundane rules that have mostly been established through precedence. However, I'm always surprised to see how many people have never really heard them stated definitively before, so even more experienced designers will still find these sections useful, at least for explanations of why things are the way they are.
The first question people will often ask me is, "When should I make something a property and when should I make it a method?" Note that in COM everything is really a method. Properties are always just a pair of get and set methods that have been marked as a property in the Interface Description Language (IDL). For Microsoft® Visual Basic® developers, this is accomplished by using Property Get and Property Let/Set procedures instead of functions and subroutines. When the developer marks something as a property, Automation clients such as Visual Basic and Visual Basic Scripting Edition (VBScript) can then refer to that as if it were a data member of the object, but code is always run in the class. In fact, if you just mark a class-level variable in your Visual Basic class as Public, Visual Basic automatically creates a simple set of property procedures for you.
To determine if something should be a property or method, use the following rules:
This last point is important. I have often designed something as a method first and then later realized that it also would be useful to get the value of the state affected by the method. This sometimes means turning the method into a Read/Write property, and other times just means adding a Read-only property that is affected by the existing method call. The right choice depends on your design style and what you are trying to expose to the developer.
There are a few simple but important rules for naming properties and methods:
This one I learned the hard way. In RDO (Remote Data Objects) 2.0, we introduced the ability to create the rdoConnection object independently. That meant we needed to have a method exposed to connect to the data source. However, we already had defined a property in RDO 1.0 called "Connect" which held the ODBC connection string. "Connect" is a verb and would have been perfect as a name for the method used to connect, but we had already used it for a property (something we never should have done in the first place). Developers were constantly confused by this and many thought they opened the connection by setting the Connect property. That's why in ADO we called it "ConnectionString" as this made it a noun and made the purpose of the property much clearer.
Beyond the simple rules above, there are some other general rules that apply to specific types of properties and methods.
Boolean properties (sometimes called flags) are very common in programming models, and if you look at most models today, there is little consistency between the names of these types of properties.
More importantly, Boolean properties commonly serve one of a few different functions, and your naming of the properties should denote the function the property serves. The different functions I have identified over the years are discussed below. The prefixes I suggest are purely my own opinion and there are many others that would work just as well—just make sure you remain consistent in whatever you choose. Write down your rules for your group so that everyone works from a common base. The point is to realize the difference in the functions these flags perform and to be consistent in classifying the flag into one of these functions and naming it accordingly.
The observant reader will notice that I propose using verbs in some of the prefixes below. These are special cases where I believe the verb usage does not hurt the understandability of the property. You may disagree, but just be consistent.
Flags controlling existence or current state
This first class of Boolean properties reflect the existence of something or the current state of something. Examples of this are Visible, Enabled, State, or HasTitle. The last example is a property from Excel charting where a value of False literally means that the Title object exposed by the Chart does not exist (that is, the Chart's Title property will return Nothing/NULL).
There is a debate going on over whether these types of flags should have a prefix at all. The standard properties in Visual Basic, such as Visible and Enabled, do not have prefixes, but newer languages and models (such as JavaBeans) put forth the standard that they should have an "Is" or "Has" prefix (for example, "IsVisible" and "IsEnabled").
It makes sense to follow Visual Basic's convention if you are developing a component that is to be used in Visual Basic. It will be much easier for developers to figure out how to make your control invisible when there is a "Visible" property than an "IsVisible" property, since all other objects in Visual Basic have a "Visible" property. Therefore, whenever you are designing a property that is standard in Visual Basic, you should follow Visual Basic's naming convention.
However, if you are designing a new property that is not standard to your intended or preferred host, you will have to make a decision yourself. Whatever you do, be consistent.
Flags controlling visibility of UI elements
A second class of Boolean properties consists of those that control visibility of UI widgets like +/- buttons, toolbars, status bars, title bars, and so on. For purely programmatic components, these kinds of flags are never used, but they usually show up throughout COM controls.
I typically use a prefix of "Display" with these types of flags. Examples include "DisplayExpandIndicator," "DisplayStatusBar," or "DisplayToolbar." An alternative prefix you could use is "Show." Again, decide for your group and keep it consistent.
Note that if the UI element being shown or hidden has a programming interface unto itself (for example, if you have a toolbar class/interface in your model), it is better to put a Visible property on that class' interface and make that the mechanism for hiding and showing the UI element. Many times you will not have a separate object for the UI element (such as the expand/collapse indicators) so that is when you create flags to control the element's visibility.
Flags controlling end-user functionality
A third class of Boolean properties consists of those that control the way the user interacts with the UI and what the user is allowed to do in the UI. For purely programmatic components, these kinds of flags are never used, but they usually show up throughout COM controls.
Since these types of flags control what is allowed in the user interface, I typically use a prefix of "Allow." Examples include "AllowExpand," "AllowFiltering," or "AllowDragDrop."
You may think that setting a flag like this to False may make the UI widget that enables the functionality invisible (for example, setting AllowFiltering to False may remove a filter button from the UI), so it's sometimes hard to distinguish between this kind of property and the kind above. A general rule of thumb is to figure out if the property causes the functionality to disappear or just to be disabled. If it just disables the UI element, it belongs in this class and should follow the naming convention for these types of flags.
Flags controlling engine-level functionality
The fourth class of Boolean properties consists of those that control engine-level functionality. By "engine-level," I mean functionality that is accessible through code as well as through UI manipulation. Purely programmatic components will have properties like these, as will COM controls.
Since these types of flags enable or disable engine functionality, I typically use the prefix "Enable" for these. Examples include "EnableUndo," "EnableLogging," or "EnableEvents." The prefix of "enable" can sometimes confuse developers, especially when the component also has UI elements that can be enabled or disabled, so you can alternatively use a different prefix. However, the use of "Enable" for engine level functionality is consistent with the notion of enabling or disabling UI elements, so it can work well in COM controls.
The key to distinguishing these flags from the previous kind is to ask whether this flag controls what code or script is allowed to do. If setting the flag to False prohibits code from doing the action, then it falls into this category. If it only prohibits the UI mechanism, it belongs to the category of flags controlling end-user functionality.
There are a number of property names that have been established quite firmly by conventional use over the years. This section lists a number of the most common ones, though it is not meant to be exhaustive. If you are considering creating a new property, check to see if the concept exists in other programming models and if there seems to be a canonical name for it already.
Note that controls that will be put in containers (like forms) should not define Right, Left, Width, or Height properties. These are automatically added to your programming interface by the host, as the host controls the placement and size of your window in the form.
This section lists a number of the most common and established method names, though it is not meant to be exhaustive. If you are considering creating a new method, check to see if the concept exists in other programming models and if there seems to be a canonical name for it already.
Note that Open is a reserved word in VBA, so Visual Basic developers can't use this method name.
Note that Close is a reserved word in VBA, so Visual Basic developers can't use this method name.
When designing methods on your programming interfaces, you must also consider the names, data types, and defaults for parameters to those methods. Again, there are a few basic rules that you should follow when designing the parameters of your methods:
<param> = <default value>
If rs.AbsolutePosition = adPosUnknown
Following these rules when designing your parameters will lead to a better programming model that will make your customers' lives easier.
As a programming model designer, enumerations are one of your best weapons against chaos and confusion. In general, define an enumeration anytime you have a property or parameter that takes one of a fixed set of possible values. Then mark the data type of that parameter or property as the enumeration. In Visual Basic, this is accomplished by code such as:
Public Enum MyEnum
Value1 = 1
Value2 = 2
Value3 = 3
End Enum
Public Property Get MyProperty() As MyEnum
…
End Property
Now when you use MyProperty in code, a drop-down list containing the values in your enumeration appears.
This gives your customers a quick listing of the possible values for the property or parameter and helps them avoid errors due to bad typing or misunderstanding what values are acceptable.
Note that Automation clients such as VBA and VBScript do not actually do run-time checking to make sure that the value the client passes is in the enumeration. This is bad and good. It's bad because you don't get the really strong type checking that you might want, but it's also good because you can let developers pass other values that might be valid and use the enumeration for the values with special meaning. The AbsolutePosition property is an example of a property that uses this technique.
There are two types of names to worry about with enumerations: the name of the enumeration itself and the names of the various members within a particular enumeration. The naming conventions you use for these two categories is up to you, but following is a list of rules that I typically use and that many Microsoft components also follow. Again, the most important thing is to be consistent first within your library, and then with other libraries that your customer will likely use along with yours.
For an example of how enumeration prefixes work between shared enumerations and those that are specific to one control within a library, see the Microsoft Windows Common Controls library (comctl32.dll).
The script engines for VBScript and JavaScript do not expose enumerations because they have no concept of data types or namespaces and, therefore, no ability to read type libraries for things like enumerations. (This is one of the reasons why they can stay so small.) The scripting environments just see your property as having a Long data type and they do not automatically list your enumeration members while writing script.
There are essentially two ways to solve this problem (three if you count the option of just not caring). The first option is to write and distribute to scripting clients an include file that defines global variables for all your enumerations and constants. This is similar to what component developers had to do in Visual Basic version 3.0. It's not a great solution because it creates a new file that you must distribute and explain, and it can be very time-consuming to keep the file synchronized with your component's code.
The other option is to add something to your programming model that exposes constants to scripting clients. In some of the new programming models we are defining in Microsoft Office, we have included a Constants property on the main programming interface. This property points to another interface that contains a set of properties, one for each constant or enumeration member. For memory consumption and other reasons, we define this as a pure dispatch interface (using only late binding).
Events are a very powerful mechanism and many programming model designers will shy away from them or expose very few events for the first release of their component. Events definitely introduce much more complexity than properties and methods do, both for the programming model designer and for the developer creating the component.
In general, events are used to notify the client that something interesting has happened and give that client a chance to run some code. COM controls obviously use events quite frequently, because they need to let the developer know when the control has been clicked, double-clicked, or manipulated in some way. But purely programmatic components can also raise events at interesting times and enable the creation of very powerful programs that use them.
One of the trickiest things you must do when designing events is to define the reentrancy behavior of your component. This means you must define what happens when a developer writes code that manipulates your component during an event your code raised.
You might be thinking "I don't need to worry about reentrancy—I'm not writing multithreaded code." However, by reentrancy here I mean code reentering your component in ways you might not expect. Imagine a control that fires a Change event, and during that Change event, the developer writes code that changes the content of the control again. Should the Change event fire again?
Your support for reentrancy can range from nothing being allowed to most anything being allowed. However, you will often find yourself needing to define something in the middle, and this is where clear definition is needed to avoid confusion and bug reports from your customers.
There are two important things to define with respect to events and reentrancy. The first is if your events will be raised both in response to UI manipulation and in response to programmatic manipulation. For purely programmatic components, this must obviously be answered as the latter, but for COM controls it's an important question. The answer chosen by existing controls available on the market is definitely not consistent, even within the control itself. For example, if you write a calendar control that raises a SelDateChange event when the currently selected date changes, should the event fire both when I select a new date in the UI and when I write code that changes the currently selected date? Or should it only fire when changed through the UI?
The answer to this question will determine the structure of your customer's code. Developers commonly want to keep similar code in one place, so they would commonly like to handle the case when the date changes in one function. They will most likely begin by putting code in your SelDateChange event. Now if they happen to change the date in code, they would probably like to have the event fire so it would be handled appropriately. But what happens if they need to change the date, but don't want their handler code to run (say, when they first initialize the form)? They now have to use a module variable to ignore the event. This may be an acceptable thing because it may be much more common that developers will want the event to always fire than not. The specific answer depends on your component and the scenarios (there's that rule again) for which you are designing.
The second thing to define is what should happen if a developer writes code to modify your component during an event that was raised from your component. This is especially important when the developer modifies the component in such a way that the same event would naturally fire again, creating a never-ending loop that will eventually overflow the stack and crash your customer's program.
Typically, components will freeze other or further events from being fired while raising an event. Component developers use a simple private class-level flag that is set to On just before raising an event (using RaiseEvent in Visual Basic). Before setting the flag to On, the developer first checks to see if it's already on, and if so, does not raise the event. When the original event returns (the line after RaiseEvent in Visual Basic), the developer sets the flag to Off, and events will be allowed again.
If you decide to allow the further raising of events during an event, you must be very careful to avoid recursive loops innocently caused by your customer. Remember that they won't care about the intricacies of software engineering when they can't figure out your component and it keeps crashing. They will more likely throw it away and get another similar component from a different vendor.
There is a raging debate going on as to how events should be named.
On one side of the fence are those that think after events (events that are fired after something has happened) should be named with an "On" prefix, such as "OnClick," "OnKeyDown," and "OnLoad." This is how all events are named in the DHTML programming model as well as in JavaBeans.
However, the official COM standard differs, advocating no prefix for after events (for example "Click," "KeyDown," and "Load"). This standard is followed by Visual Basic as well, in forms and in all components shipped with Visual Basic.
So, if your component is designed to primarily run in Visual Basic, you should follow suit. Again, it will make it easier on your customer if you are consistent with other programming models the customer commonly uses. If you are designing your control to primarily run in Microsoft Internet Explorer, you will most likely want to follow the DHTML convention, unless your target customers will be more familiar with Visual Basic or will be using other COM controls that don't use prefixes for after events.
There are a few other classes of events that designers typically use. These are commonly called before events, during events and overriding events.
"Before" events are those that are raised before an action will take place, and they commonly provide a mechanism for letting the listener cancel the operation or make last-minute adjustments to settings. Before events should have a "Before" prefix as in "BeforeConnect" or "BeforeExecute." To let the developer cancel the action, designers typically put a Cancel parameter as the last parameter in the event that is passed by reference. The parameter is initially False, but if the developer sets it to True during the event, the action is cancelled and an error is raised (exception thrown in some systems) back to the code that called the method that triggered the event.
"During" events are those that fire while an asynchronous action is happening but before it finishes. They are commonly used to let the client update UI elements such as status bars or animations that let the user know things are still progressing. During events should use a name ending in "ing" if possible, such as "SelectionChanging" or "CommandExecuting." Sometimes designers treat these like after events and call them something like "ProgressUpdate." Both designs are equally usable, but I find the "ing" ending makes it more obvious as to why and when the event is firing.
"Overriding" events are those that fire just before an action will take place. Instead of just allowing the operation to be cancelled, these events allow for a complete override of the action. A success or failure code is returned that lets the component know if it should raise an error from the method that caused the event to fire. Overriding events are uncommon and should only be used in certain situations. The standard naming for these kinds of events is to prefix it with "Will." One example of this kind of event is the WillUpdateRows in RDO 2.0.
This section lists a number of the most common and established event names, though it is not meant to be exhaustive. If you are considering creating a new event, check to see if the concept exists in other programming models and if there seems to be a canonical name for it already.
Finally, component development requires good documentation. In fact, I would argue that it requires much more documentation than an end-user product. If your customer is a developer seeking to integrate your component into another application, he or she will need to understand your programming model.
Microsoft products typically include embedded help, such as ToolTips, status bars, and so on, in the application, to provide quick help that may keep the user from having to open the help files and derail his or her intended task. In the same way, you can embed bits of helpful information into your programming model that will assist developers in knowing what a particular property, method, event, or class is for without requiring them to open the help system. It will also serve as a reminder to the more experienced developers. The way you do this is by embedding documentation strings (or "doc strings" as they are commonly called) in your type library. These strings show up in most development tools in the object browsers and sometimes right in the code editors themselves.
For C++ developers, this simply involves including a helpstring attribute for each property, method, event, and interface in your type library, using the following syntax:
helpstring("This method does . . .…")
For Visual Basic developers, this string can be set in the Procedure Attributes dialog box available from the Tools menu. Type your help string in the Description text box. This dialog box is rather clumsy, so if you are trying to set a number of these all at once, you may find it easier to open the source files in a text editor and add the line that is highlighted in the example below:
Public Sub MyMethod()
Attribute MyMethod.VB_Description = "The MyMethod does ..."
Visual Basic persists the description from the Procedure Attributes dialog box in this manner, so if you insert the string manually via a text editor, it will load correctly when you reopen your project in Visual Basic.
These documentation strings may not seem relevant to you, but any developer using your component will thank you many times over if you include meaningful and accurate descriptions of your programming interface as documentation strings. When you are designing your programming models, write these strings down in your specifications as you design the property, method, event, or interface and make sure they are included during development.
Now that I have listed some of my rules for designing better programming models, I will share with you the most important rule: Don't blindly follow rules you read somewhere without verifying that it makes sense for your situation!
The only rule from above that I would argue must be followed at all times is "be consistent." Designing for scenarios and embedding documentation strings are also rules that should be followed unless there's a very, very good reason not to do so. The other rules are just helpful advice that I have collected over the years and have found works well. At their core, they contribute to the overall goal of designing with your customer's needs in mind and not your own. The details (such as what prefix to use when naming certain types of Boolean properties) are open for modification. Just make sure you remain consistent.
For every detailed rule above, you can probably find one or more libraries that have done things differently. Sometimes you may find that it would be better to be consistent with a not-so-good design decision made in another library than to be inconsistent with it. However, be wary of just copying design patterns from other programming models as they may be quite poor designs, and you're not helping anyone when you do that.
There are plenty of bad examples out there, but if you keep the principles I've outlined in this paper and your customer's needs in mind when designing, you will likely start to see the bad design choices in your favorite programming models and keep yourself from making the same mistake in your own programming models.