UserGuide

API Design and Naming Guidelines

From Xojo Documentation

This a living document that will be updated periodically when necessary.

Introduction

The purpose of this document is to provide guidelines for creating APIs and naming them such that new APIs are clear, easy to understand and most importantly, consistent with the rest of the Xojo framework.

The goal of consistency is to make it possible for a user to guess the API of a class with which they have never worked rather than have to learn each one every time. This also makes understanding existing code faster as well. However, consistency should not reduce readability.

Consistency

Before creating a new API, review the existing APIs to see if there’s one that is similar to what you need for the new functionality.

Case

Class names are upper camel case.

Example: TextField

Class members are upper camel case.

Example: TextField.SelectedText

Parameters are camel case.

Example: ListBox.AddRow("First Name")

Namespaces are upper camel case, when used.

Example: Xojo.Core

General Naming

Names should provide the most intuitive identification not necessarily the most accurate. They should choose clarity over brevity. The name should make it immediately obvious what the item is for even if that name is not entirely accurate. For example, previously the name of the event that is called when a button is clicked was called Action rather than Clicked or Pressed because it can be triggered without clicking/pressing the button. That makes Action more accurate but it's also not at all intuitive. In API 2.0, Action has been renamed Pressed which, while less accurate, is far more intuitive. It's obvious what the event is for and the exceptions (such as the fact that a button can be triggered without pressing it - via accessibility features for example) are easy enough to understand. Another example is the Volume class in the context of FolderItems. Most people have to learn what this means. In the API 2.0 we use Drive instead since most volumes are Hard Drives, Flash Drives or Solid State Drives (SSD). Volume is more accurate but not as intuitive since it also has a meaning relating to sound volume.

Names should not use abbreviations or truncations unless they are so common that they are spoken that way. For example, Info is acceptable. Sel (for Selection) is not. Abbreviated Min and Max are not acceptable as part of compound framework terms. This does not apply to standard math functions, such as for Min and Max, which will will not change.

Class names should be a noun. For the case when there are two classes, one that can be modified and one that cannot, use the Editable prefix for the one that can be modified and no prefix for the one that cannot be modified. For example, Image and EditableImage.

Method names should be in the form of verb noun. For example AddInterval.

Functions (methods that return a value) should be named or prefixed with what they return. DesktopFolder is acceptable. GetDesktopFolder is not.

Event names should be past tense if the event itself has already taken place when the user’s code is called. Example: TextField.TextChanged is acceptable because the TextField’s value has already been changed.

Event names should be in the form noun verb. For example SelectionChanged.

Most controls should have a Pressed event. Controls whose primary form of user interaction involves the user clicking or tapping the control should have a Pressed event that is passed a parameter when necessary to indicate what was pressed.

Properties (and Constants) in most cases, should be nouns and named after what they contain.

Enumeration names should always be plural. Name of members of type enumeration should always be singular. For example, the Timer class has RunModes as an enumeration and RunMode as a member of type RunModes.

Omit needless words. For example, System.GetNetworkInterface should just be NetworkInterface (and also because functions should be named what they return). TextField has both TextChanged and SelectionChanged.

Group associated members. When possible, if members are associated with each other, use a common prefix so they are listed together alphabetically. For example, DragEntered and DragExited. However, do this only when other API guidelines can also be satisfied.

When a class member is specific to a particular platform, it should be prefixed with the name of the platform. The following are the designated prefixes: Mac, Windows, Linux, iOS, Android, Web and Mobile.

Boolean Properties

These prefixes are used with UI-related Boolean properties for situations when it is unclear how the UI may be changed.

Name Description Example
Allow Makes a change to the behavior or interface possible but not necessarily immediately. AllowFocusRing
Has Makes an immediate visual change to an element of the user interface of the object. HasCloseButton
Is Optionally used for read-only Boolean properties (and functions) when needed for clarity. IsRectangle

If Allow or Has results in awkward names, use something that is better.

Otherwise Boolean properties do not have a prefix and are named after what they indicate. For example, Enabled or DarkModeIsSupported.

Lists

List items are accessed via a value from the list or a 0-based index. When an index is used, the preposition “At” is appended which differentiates it from a method that looks up by value. Classes that provide access to a list should implement the following standard set of members and make use of the appropriate nouns to identify what is being changed. For example, AddRow would be used for controls that deal with rows, AddPanel for controls dealing with tabs or panels, etc.

Name Description Example
AddRow Adds a single item to the end of the list. AddRow("Hello")
AddRowAt Adds a single item to the list at the specified index. AddRowAt(5, "Hello")
AddAllRows Adds all items passed to the end of the list. AddAll(myList)
FirstRowIndex The index of the first row. Var first As Integer = Me.FirstRowIndex
RowCount Returns the number of items in the list. Var count = Me.RowCount
RowValue When list items are objects, this method accesses the object specified by the name that is passed. RowValue("SaveButton").Visible = False
RowValueAt Accesses the value specified by the index(s) passed. Use "Value" for simple types (numbers, String, etc.). Omit "Value" for objects. So just use RowAt if this returned an object. Var value As Integer = Me.RowValueAt(5)
RowTag When list items are objects that have tag properties, this method accesses the tag for the item specified by the value passed. value = RowTag("Hello")
RowTagAt Accesses the tag for the item specified by the index(s) passed. RowTagAt(5) = "Hello"
RemoveRowValue Removes the first item specified by the value passed. Call repeatedly to remove any additional rows with the same value. RemoveRowValue("Hello")
RemoveRowAt Removes the item specified by the index passed. RemoveRowValueAt(5)
RemoveAllRows Removes all items from the list. RemoveAllRows
LastRowIndex The index of the last row. Var lastRow As Integer = Me.LastRowIndex
LastAddedRowIndex The index of the last row added by AddRow, AddRowAt or AddAllRows. If no items have been added, an OutOfBoundsException is raised. Var last As Integer = Me.LastAddedRowIndex

For example the Toolbar class contains a list of buttons so it should implement the above listed methods, but use "Button" in place of "Row". The Dictionary class is considered a list, just an unordered one, so it should also implement the relevant methods above, without a qualifying noun. List classes should also always implement the Iterable and Iterator interfaces to allow looping with For Each...Next statements.

For two dimensional lists (probably only for multi-column listboxes), the *ValueAt method should take row and column indexes as parameters and could return the entire row (as an array of the appropriate type) if no column index is passed.

For list-type containers that have multiple things that are counts (row count, column count, for example) then be specific for all the types of count: RowCount, ColumnCount.

For user interface controls that are list-oriented (ListBox, PopupMenu, TabPanel, Toolbar, SegmentedControl, etc.) where the user can select an item from the list, the following members should be implemented (again, substituting an appropriate noun in place of "Row" if necessary):

Name Description Example
SelectedRowValue Returns the value of the item currently selected. If no row is selected, an OutOfBoundsException is raised. Var value As String = Me.SelectedRowValue
SelectedRowIndex Allows you to get or set the selected row. If you call this as a function and no row is selected, an OutOfBoundsException is raised. Var row As Integer = Me.SelectedRowIndex

Me.SelectedRowIndex = 5

SelectedRowCount Returns the number of rows selected. If Me.SelectedRowCount > 0 Then

Avoid hidden functionality. Avoid negative indexes for special behaviors. Instead add methods that provide the functionality. For example, Listbox.CellAt(-1, -1) is bad as it should really raise an OutOfBoundsException. Instead, a Listbox.Contents method that returns a two-dimensional array would be better. With no parameters this is the entire ListBox, with one parameter this is the specific row.

Errors

Errors should raise exceptions rather than return error codes. However, the exceptions raised can have members that provide error codes. Methods should not return booleans to indicate that they succeeded or not as that boolean value is really a simple error code. Instead, the function should throw an exception if it fails.

Enumeration Usage

Use enumerations in place of integer constants when the value is unimportant and they are not used as part of a calculation. For example, bit flags would need to be constants because they are used to calculate actual Integer values.