Skip to main content

This is documentation for Caché & Ensemble.


Basic Ideas in Class Programming

If you are not familiar with class programming, this chapter is intended to give you a sense of how this kind of programming works. If you are familiar with class programming, you might find it helpful just to skim the code examples, so that you see what class programming in Caché looks like.

This chapter discusses the following:

The concepts in this chapter are largely independent of language, although the examples use ObjectScript.

Objects and Properties

In class programming, a key concept is objects. An object is a container for a set of values that are stored together or passed together as a set. An object often corresponds to a real-life entity, such as a patient, a patient diagnosis, a transaction, and so on.

A class definition is often a template for objects of a given type. The class definition has properties to contain the values for those objects. For example, suppose that we have a class named MyApp.Clinical.PatDiagnosis; this class could have the properties Date, EnteredBy, PatientID, DiagnosedBy, Code, and others.

You use the template by creating instances of the class; these instances are objects. For example, suppose that a user enters a patient diagnosis into a user interface and saves that data. The underlying code would have the following logic:

  1. Create a new patient diagnosis object from the patient diagnosis template.

  2. Set values for the properties of the object, as needed. Some may be required, some may have default values, some may be calculated based on others, and some may be purely optional.

  3. Save the object.

    This action stores the data.

The following shows an example that uses ObjectScript:

 //create the object
 set diagnosis=##class(MyApp.Clinical.PatDiagnosis).%New()

 //set a couple of properties by using special variables
 set diagnosis.Date=$SYSTEM.SYS.TimeStamp()
 set diagnosis.EnteredBy=$username
 //set other properties based on variables set earlier by 
 //the user interface
 set diagnosis.PatientID=patientid
 set diagnosis.DiagnosedBy=clinicianid
 set diagnosis.Code=diagcode
 //save the data
 //the next line tries to save the data and returns a status to indicate
 //whether the action was successful
 set status=diagnosis.%Save()
 //always check the returned status
 if $$$ISERR(status) {do $System.Status.DisplayError(status) quit status}
Copy code to clipboard

Note the following points:

  • To refer to a property of the object, you use the syntax object_variable.property_name, for example: diagnosis.DiagnosedBy

  • %New() and %Save() are methods of the MyApp.Clinical.PatDiagnosis class.

    The next section discusses types of methods and why you invoke them in different ways as seen here.


A method is a procedure (in most cases; Caché supports other kinds of methods as you will see in the next chapter). Methods can invoke each other and can refer to properties and parameters. The next chapter gives the details for the rules in Caché.

There are two kinds of methods in a class language: instance methods and class methods. These have different purposes and are used in different ways.

Instance Methods

An instance method has meaning only when invoked from an instance of the class, usually because you are doing something to or something with that instance. For example:

 set status=diagnosis.%Save()
Copy code to clipboard

For example, suppose that we are defining a class that represents patients. In this class, we could define instance methods to perform the following actions:

  • Calculate the BMI (body mass index) for the patient

  • Print a report summarizing information for the patient

  • Determine whether the patient is eligible for a specific procedure

Each of these actions requires knowledge of data stored for the patient, which is why most programmers would write them as instance methods. Internally, the implementation of an instance method typically refers to properties of that instance. The following shows an example definition of an instance method that refers to two properties:

Method GetBMI() as %Numeric
 Set bmi=..WeightKg / (..HeightMeter*2)
 Quit bmi
Copy code to clipboard

To use this method, your application code might include lines like this:

 //open the requested patient given an id selected earlier
 set patient=##class(MyApp.Clinical.PatDiagnosis).%OpenId(id)  
 //get value to display in BMI Display field 
 set BMIDisplay=patient.GetBMI() 
Copy code to clipboard

Class Methods

The other type of method is the class method (called a static method in other languages). To invoke this type of method, you use syntax that does not refer to an instance. For example:

 set patient=##class(MyApp.Clinical.PatDiagnosis).%New()
Copy code to clipboard

There are three very general reasons to write class methods:

  • You need to perform an action that creates an instance of the class.

    By definition, this action cannot be an instance method.

  • You need to perform an action that affects multiple instances.

    For example, you might need to reassign a group of patients to a different primary care physician.

  • You need to perform an action that does not affect any instance.

    For example, you can write a method that returns the time of day, or a random number, or a string formatted in a particular way.

Methods and Variable Scope

A method typically sets values of variables. In nearly all cases, these variables are available only within this method. For example, consider the following class:

Class GORIENT.VariableScopeDemo

ClassMethod Add(arg1 As %Numeric, arg2 As %Numeric) As %Numeric
    Set ans=arg1+arg2
    Quit ans

ClassMethod Demo1()
   set x=..Add(1,2)
   write x

ClassMethod Demo2()
   set x=..Add(2,4)
   write x

Copy code to clipboard

The Add() method sets a variable named ans and then returns the value contained in that variable.

The method Demo1() invokes the method Add(), with the arguments 1 and 2, and then writes the answer. The method Demo2() is similar but uses different hardcoded arguments.

If the method Demo1() or Demo2() tried to refer to the variable ans, that variable would be undefined in that context and Caché would throw an error.

Similarly, Add() cannot refer to the variable x. Also the variable x within Demo1() is a different variable from the variable x within Demo2().

These variables have limited scope because that is the default behavior of Caché classes (and is the usual behavior in other class languages).

Within class definitions, you pass values almost entirely by including them as arguments to methods. This is the convention in class programming. This convention simplifies the job of determining the scope of variables.

In contrast, when you write routines, it is necessary to understand the rules that control scoping. These are discussed in Using Caché ObjectScript.

Class Constants (Parameters)

Sometimes it is useful for a class to have easy access to a constant value. In Caché classes, such a value is a class parameter. Other languages use the term class constant instead. The following shows an example:

Parameter MYPARAMETER = "ABC" ;
Copy code to clipboard

A class parameter acquires a value at compile time and cannot be changed later.

Your methods can refer to parameters; that is why you define parameters. For example:

 set myval=..#MYPARAMETER * inputvalue
Copy code to clipboard

Class Definitions and Types

The following shows an example of a class definition, which we will use to discuss types in class definitions:

Class MyClass Extends %Library.Persistent
Parameter MYPARAMETER = "ABC" ; 

Property DateOfBirth As %Library.Date; 

Property Home As Sample.Address; 

Method CurrentAge() As %Library.Integer 

ClassMethod Addition(x As %Library.Integer, y As %Library.Integer) As %Library.Integer

Copy code to clipboard

This class definition defines one parameter (MYPARAMETER), two properties (DateOfBirth and Home), one instance method (CurrentAge()), and one class method (Addition()).

In class programming, you can specify types in the following key places:

  • For the class itself. The element after Extends is a type.

    Each type is the name of class.

  • For parameters. In this case and in the remaining cases, the element after As is a type.

  • For properties. For the Home property, the type is a class that itself contains properties.

    In this case, the type has an object value. In the example here, this is an object-valued property.

    Object-valued properties can contain other object-valued properties.

  • For the return value of a method.

  • For the value of any arguments used by a method.


In most class-based languages, a major feature is inheritance: one class can inherit from other classes and thus acquire the parameters, properties, methods, and other elements of those other classes. Collectively, the parameters, properties, methods, and other elements are known as class members.

Terminology and Basics

When class A inherits from class B, we use the following terminology:

  • Class A is a subclass of class B. Alternatively, class A extends class B.

    Sometimes it is said that class A is a subtype of class B.

  • Class B is a superclass of class A.

    Sometimes it is said that class A is the child class and class B is the parent class. This terminology is common but can be misleading, because the words parent and child are used in quite a different sense when discussing SQL tables.

When a class inherits from other classes, it acquires the class members of those other classes, including members that the superclasses have themselves inherited. The subclass can override the inherited class members.

It is possible for multiple superclasses of one class to define methods with the same name, properties with the same name, and so on. Therefore it is necessary to have rules for deciding which superclass contributes the definition that is used in the subclass. (See “Inheritance Rules in Caché,” in the next chapter.)

In the Caché class library, superclasses usually have different purposes and have members with different names, and conflicts of member names are not common.


The following shows an example from Ensemble:

/// Finds files in a FilePath directory and submits all that match a FileSpec wildcard to 
/// an associated BusinessService for processing within Ensemble 
Class EnsLib.File.InboundAdapter Extends (Ens.InboundAdapter, EnsLib.File.Common) 
Copy code to clipboard

This example is presented solely to demonstrate how a class can combine logic from different superclasses. This EnsLib.File.InboundAdapterOpens in a new window class inherits from two classes that do quite different things:

In EnsLib.File.InboundAdapterOpens in a new window, the methods use logic from both of these classes, as well as their superclasses.

Use of Inherited Class Members

When you see the definition of a class in an editing tool, you do not see the inherited members that it contains but your code can refer to them.

For example, suppose that class A has two properties, each of which has a default value, as follows:

Class Demo.A  
Property Prop1 as %Library.String [InitialExpression = "ABC"];

Property Prop2 as %Library.String [InitialExpression = "DEF"];
Copy code to clipboard

Class B could look like this:

Class Demo.B Extends Demo.A  
Method PrintIt()
 Write ..Prop1,! 
 Write ..Prop2,! 

Copy code to clipboard

As noted earlier, a subclass can override the inherited class members. For example, class C could also inherit from class A but could override the default value of one of its properties:

Class Demo.C Extends Demo.A  
Property Prop2 as %Library.String [InitialExpression = "GHI"];

Copy code to clipboard

Use of Subclasses

If class B inherits from class A, you can use an instance of class B in any location where you can use an instance of class A.

For example, suppose that you have a utility method like the following:

ClassMethod PersonReport(person as MyApp.Person) {
 //print a report that uses properties of the instance
Copy code to clipboard

You can use an instance of MyApp.Person as input to this method. You can also use an instance of any subclass of MyApp.Person. For example:

 //id variable is set earlier in this program 
 set employee=##class(MyApp.Employee).%OpenId(id)
 do ##class(Util.Utils).PersonReport(employee)
Copy code to clipboard

Similarly, the return value of a method (if it returns a value) can be an instance of a subclass of the specified type. For example, suppose that MyApp.Employee and MyApp.Patient are both subclasses of MyApp.Person. You could define a method as follows:

ClassMethod ReturnRandomPerson() as MyApp.Person
 Set randomnumber = $RANDOM(10)
 If randomnumber > 5 {
     set person=##class(MyApp.Employee).%New()
 else {
     set person=##class(MyApp.Patient).%New()
 quit person
Copy code to clipboard

Classes as Containers of Methods

As noted earlier, a class definition is often a template for objects. Another possibility is for a class to be a container for a set of class methods that belong together. In this case, you never create an instance of this class. You only invoke class methods in it.

For examples, see the classes in the Caché %SYSTEM package, which is introduced earlier in the section “$SYSTEM Special Variable.”

Abstract Classes

It is also useful to define abstract classes. An abstract class typically defines a generic interface and cannot be instantiated. A method definition within the class declares the signature of the method, but not its implementation.

You define an abstract class to describe an interface. Then you or other developers create subclasses, and in those subclasses, implement the methods. The implementation must match the signature specified in the abstract class. This system enables you to develop multiple, parallel classes with slightly different purposes but identical interfaces. Many of the system classes have common interfaces for this reason; for example, see the stream classes and collection classes introduced in the chapter “Caché Objects.”

It is also possible to specify that a method is abstract even if the class is not.

FeedbackOpens in a new window