Delphi Object Pascal Coding Standard
&
Project Organization Standard
Created: 10 Apr 2000 2:51 PM
Last Modified: 06/27/01 5:24 PM
Created by: Michael P. Hollis and Mark S. Lauter
Document Revision: 2.5.0-HTML Version
This version was adapted from the MS Word document that can be found here:
http://marklauter.dyndns.org/codestandards/CodeStandards.doc
Authors’ Note: This document was originally written for a specific development team. We found what worked for us over a six-month period and put it on paper. It may not work your team “as is.” Feel free to modify it to fit your needs. Some of the project layout recommendations may seem “thin” because we put each developer through training. This document was only intended as a quick reference. The most important thing to remember when creating a programming standard is this – everyone on the team must buy in to the standard 100% or you’re wasting your time. Don’t insist on forcing people to do things they don’t want to do. Instead, concentrate on finding a happy medium where all developers can work together within a common development/coding environment.
For information about this document or Buschwood Partners, email mhollis@mphokie.com or marklauter@hotmail.com.
Copyright 2001, Buschwood Partners Inc.
Table of Contents (meaningless for the web document)
Introduction.........................................3
Naming Standard......................................4
General Standard.....................................6
Rules of Thumb.......................................9
Project Organization Standard.......................10
Project Directory Structure.......................11
Recommended Delphi Project Build Options:.........12
Directory Example:................................13
Required Software Release Documentation.............13
Introduction
The purpose of this document is to set forth a set of rules and guidelines that insure quality of software development. The authors developed these rules and during practical experience leading American and Russian software teams. The rules are intended to increase the speed and quality of the programming process.
Follow Borland’s Object Pascal Style Guide. However, the guidelines contained within this document take precedence over the Object Pascal Style Guide. The style guide can be found here: http://community.borland.com/article/0,1410,10280,00.html.
Code Complete by Steve McConnell, ISBN 1-55615-484-4, is recommended reading for all developers.
Code that does not conform to the Delphi Object Pascal Coding Standard will not be accepted in official product builds.
Developers who are unable to follow Delphi Object Pascal Coding Standard, as determined by formal Team member code reviews, should receive additional training from their team leader.
This document is considered to be continually under revision. All Team members are invited to provide input for future revisions. All suggestions will be considered during a gathering of the Senior Team Leaders.
Rules are made to be broken. Conscious Coding Standard violations should be viewed as an explicit warning by the programmer that the code block in question has been specially written to overcome some difficulty that could not be overcome while adhering to the standard. Comments should be added to the code to indicate intentional violation of the standard. As of this publishing the authors have not encountered any problem which required a violation of this standard.
Naming Standard
Excessive Hungarian notation is not acceptable. We recently saw a coding standard that defined 8 pages of 2 to 6 letter prefixes for objects. This is absurd.
Local variables are prefaced with v. The only exception to this rule is loop counters.
Example local variable definition:
var
vList: TList;
vImage: TImage;
i: integer;
Field variables are prefaced with F.
Example field variable definition:
FList: TList;
Global variables, also known as interface variables are prefaced with g.
Example interface variable definition:
var
gList: TList;
Arguments are prefaced with A.
Example procedure definition:
procedure MyProc(const AList: TList);
Reserved words always start with lowercase letters.
Example:
// this code block is correct
for i := 1 to 100 do
begin
MyProcedure(i);
end;
// this code block is incorrect
For i := 1 to 100 Do
Begin
MyProcedure(i);
End;
Units are named for the class they contain. For example, if you have a class called TEmployee, the unit containing the class would be called Employee.pas.
Projects (packages and executables) are named for the functional group of classes that they contain. For example, our database helper classes are contained within the BP_DatabaseAccess package.
Project names are always prefaced with some abbreviation that represents the company, for example Buschwood Partners would use BP_.
Do not use an obscure abbreviation for any name. For example, use FConnection instead of FConn.
To avoid naming conflicts with 3rd party libraries, always append a BP to all classes coded by <<company name>> Corporation. For example, a name such as TRow or TCell is likely to be used by a third party table component. TRow would become TBPRow and TCell would become TBPCell.
Constants are declared in ALL_CAPS with underscores. Actually, data belongs outside of the code, but if you must use constants, they belong in ALL CAPS.
Never name a variable Tmp or Temp.
General Standard
Only include one class per unit. Because of Delphi's implicit "friend" mechanism, including multiple classes per unit violates encapsulation. By adhering to this rule, the inclusion of two classes per unit can be viewed as an explicit declaration of friendship.
Arguments are always passed with the const keyword. This prevents side effects and increases performance when passing strings.
Use the DelFor code formatter, with common options across the team, to format your code before checking it into the version control system. The latest copy of DelFor can always be found here: http://www.slm.wau.nl/wkao/DelForExp.html.
Follow the principle of symmetry and ownership. The class with the responsibility of creating an object has the responsibility of deleting the object.
Always call inherited before executing any other code in a constructor
Always call inherited after executing any other code in a destructor.
When ever possible, use return values instead of raising exceptions to report success or failure. This increases execution speed. Use Boolean true for success and false to indicate failure. Never use integers as return values. Instead use enumerated types to indicate state beyond success or failure.
Conditional compiler directives will not be used. Conditional compiler directives make the code difficult to read. If there are really two ways to build the project, then create two separate projects.
When accessing a private field from within the parent class, if access methods are available for reading and writing, use them. Do not access them from a public property as this adds overhead and slows execution speed.
Use // style comments. Do not use old style Pascal comments because they are difficult to notice outside of Delphi’s code editor.
Circular dependencies between units will not be tolerated. When a dependency is circular there is a problem with the design. Circular dependencies break encapsulation.
Never use local functions within procedures or functions. Local functions have two drawbacks: 1. They cannot be optimized by the Delphi compiler. 2. They make code exceedingly difficult to read.
If a unit is required, included or “used” by another unit, the used unit name belongs in the appropriate uses section (interface or implementation). If the unit is used by the interface, then the interface uses section should contain the name of the unit. However, if the used unit is used in the implementation section only, then the implementation uses section should contain the name of the unit. Do not use this method to create circular dependencies between units. Units should be listed in the following order: Delphi Units, 3rd Party Units, and Internally Developed Units.
If a class visibility section is unused, remove it from the class.
Example:
// this is correct
TMyClass = class(TObject)
private
FMyField : TMyField;
function GetMyField; TMyField;
public
property MyField : TMyField read GetMyField;
end;
// this is wrong
TMyClass = class(TObject)
private
FMyField : TMyField;
function GetMyField; TMyField;
protected // this section is empty. remove it.
public
property MyField : TMyField read GetMyField;
published // this section is empty. remove it.
end;
When declaring a class, explicitly declare the parent class from which it is derived. Even though TObject is the implicit parent of all objects unless otherwise declared, classes should be explicitly declared as descendents of TObject.
Do not use comments between parentheses. Misplaced comments make the code difficult to read.
Example:
procedure MyProcedure(
{AThisIsWrong : string;} AParam : string);
Always surround the code between create and free with try finally. This prevents a memory leak if an exception occurs between create and free.
Example:
MyObject := TMyObject.Create;
try
for i := 0 to 9 do
MyObject.DoSomeThing(i);
finally
MyObject.Free;
MyObject := nil;
end;
Tabs should be set at 2 space intervals starting with 3.
1234567890123456789
T T T T T T T T T
Don't code beyond the column 80. Unlike MS Visual Basic, Object Pascal is a structured programming language that allows developers to make proper use of the enter key. Use it.
Variable declarations should be listed in alphabetical order.
Method declarations and implementations should be listed alphabetical order.
Rules of Thumb
Procedure and function bodies should be limited to less than about 40 lines or one screen height.
Methods should be limited to a single purpose. Besides making the code easier to read and comprehend, it makes choosing a method name easier.
If a procedure or function has more than one loop, then it probably has more than one purpose. Reevaluate the procedure and consider breaking it into as many methods as there are loops.
Optimize code only after getting it to work and after using a performance profiler to identify bottlenecks. Comment out original, working code with a comment noting that the code has been optimized. This helps maintain readability. Its better to have slow, readable code that works than to have fast, unreadable code that doesn't work.
Data and persistence belong outside of the code. The code should be written in such a way as to allow data to change without having to recompile. This seems like common sense, but the authors have found from experience that it isn't.
Only comment the purpose of methods if the purpose seems unclear. Excessive commenting is typically not well maintained, even by the most disciplined programmers. The corollary to this rule is this: Reading comments to determine the functionality of a code block will often lead to misconceptions about how the code works or what it does. Trust the code, not the comments.
Don’t break encapsulation.
Don’t break encapsulation.
Don’t break encapsulation.
See 7, 8 and 9.
Programmers don't make changes to an object’s designed public interface. If the programmer sees a problem, she should discuss it with her team leader or a senior member of the design team. The decision to change a system's design affects many teams other than the software team. The final decision to make changes to design will be made by senior team members who understand the big picture.
Don’t make use of side effects in code. A function should be used that returns an appropriate value instead.
Don’t hide exceptions by raising different exceptions or by “swallowing” them with empty except blocks. Always re-raise the original exception. By re-raising the original exception the exception class information is preserved. It is acceptable to add your own text to an exception message, but don’t remove text from it.
Project Organization Standard
This standard takes effect 1 August 2000. Project Organization Standards are required for international teams who are synchronizing source code from multiple projects during scheduled milestone builds.
This standard addresses the following issues.
· Software project layout requirements
· Documentation required for projects.
Project Directory Structure
Directory
Extensions
Description
MasterProjectsFolder
BPG, DPR, DPK, RES, CFG, DOF
Contains a project group containing ALL projects under development and all project files.
BIN
DCP, BPL, DLL, EXE
Contains all binary files. DCU files do not belong in here.
DCU
DCU, OBJ
Contains compiled object files.
WebInterface
HTML, JS, CSS
Contains files that are used by the web interface. These files will usually be installed in subfolders under the QuickDev directory.
<ProjectName> or
<ProjectGroupName>
NO FILES
Contains Delphi project directories. Folder name matches the name of the DPK or DPR file name, or if it is to contain code for multiple projects, then the folder name should match the name of the project group, not the name of the product. May contain project sub folders as needed.
CODE
PAS, INC, CPP, H, JAVA, PL, etc.
Contains all source code for the project. May be contained under project sub folders.
DataModels
ER1, ERX, DM1, and others
Contains data model. Data models must be capable of actually generating all relevant database objects. May be contained under project sub folders.
DBScripts
SQL
Contains scripts for creating database objects. All scripts must be capable of running without errors. Scripts should be capable of completely creating all of a project’s required database objects. May be contained under project sub folders.
Documents
DOC, VSD, MPB, MDL and others
Contains design documents written in MS Word, VISIO Model Maker, Rational Rose, etc. Sub folders may be added at the team leader's discretion. May be contained under project sub folders.
TestApp
*
Contains any related applications required for unit testing the project. Sub folders may be added at the team leader's discretion. All business objects must be demonstrated to work in a test app before being integrated into existing code. May be contained under project sub folders.
Recommended Delphi Project Build Options:
1. Conditional builds are NOT allowed.
2. DebugSourceDirs can be used, but the project should never be checked in to the version control system unless this path is empty.
[Build Options]
{$ALIGN ON}
{$ASSERTIONS OFF}
{$BOOLEVAL OFF}
{$DEBUGINFO ON}
{$EXTENDEDSYNTAX ON}
{$IMPORTEDDATA ON}
{$IOCHECKS ON}
{$LOCALSYMBOLS ON}
{$LONGSTRINGS ON}
{$OPENSTRINGS ON}
{$OPTIMIZATION ON}
{$OVERFLOWCHECKS ON}
{$RANGECHECKS ON}
{$REFERENCEINFO ON}
{$SAFEDIVIDE OFF}
{$STACKFRAMES OFF}
{$TYPEDADDRESS OFF}
{$VARSTRINGCHECKS ON}
{$WRITEABLECONST ON}
{$MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$RUNONLY}
{$IMPLICITBUILD OFF}
[Directories]
OutputDir=BIN
UnitOutputDir=DCU
PackageDLLOutputDir=BIN
PackageDCPOutputDir=BIN
SearchPath=BIN; DCU
[Compiler]
A=1
B=0
C=0
D=1
E=0
F=0
G=1
H=1
I=1
J=1
K=0
L=1
M=0
N=1
O=1
P=1
Q=1
R=1
S=0
T=0
U=0
V=1
W=0
X=1
Y=1
Z=1
ShowHints=1
ShowWarnings=1
Directory Example:
..\MasterProjectsFolder\
..\MasterProjectsFolder\BIN\
..\MasterProjectsFolder\DCU\
..\MasterProjectsFolder\WebInterface\
..\MasterProjectsFolder\DBAccess\
..\MasterProjectsFolder\DBAccess\CODE\
..\MasterProjectsFolder\DBAccess\DBScripts\
..\MasterProjectsFolder\DBAccess\Documents\
..\MasterProjectsFolder\DBAccess\TestApp\
..\MasterProjectsFolder\MyProject\
..\MasterProjectsFolder\MyProject\Parser\CODE\
..\MasterProjectsFolder\MyProject\Parser\DBScripts\
..\MasterProjectsFolder\MyProject\Parser\Documents..\MasterProjectsFolder\MyProject\Parser\TestApp\
Required Software Release Documentation
The following documents must be in each Documents directory:
Changes.doc
List of all files that have been modified, what changes were made to them, what major functionality has been added, removed, etc.
FilesRequiredForBuild.doc
Include a description of each file.
FilesRequiredForInstall.doc
Include a description of each file.
InstallProcedure.doc
Install instructions must be detailed so that someone completely unfamiliar with the system can install the project.
[class name].doc
Each class must be documented. The naming convention for class documentation files is the class name plus the doc extension. Example: TMyClass.doc. Class document files should be created with the ClassDocumentation.dot template.
Copyright 2001, Buschwood Partners Inc.