Back Home Up Next

5 DOMAIN IMPLEMENTATIONS

5.1   Modeling Structure and Behavior
5.2   Implementation of Composite Domains
5.3   Implementation of Selection Domains
5.4   Implementation of Enumeration Domains
5.5   Implementation of Optional Domains
5.6   Implementation of Repetitive Domains
         5.6.1 Repetitive Domains as part of Composite Domains
         5.6.2 Individual Repetitive Domains
5.7   Implementation of Sub-domains
5.8   Implementation of Equivalent Domains
5.9   Implementation of Behavior and  Structure-related Operations
5.10 Operations on Elements of Collections 
5.11 Guidelines
5.12 Summary

 

The fourth step of the domain modeling process is to implement the domain specifications in Elisa. The domain specifications are the domain definitions, as defined by step 2 (Chapter 3), together with the domain operations, as specified by step 3 (Chapter 4).

In this chapter we will discuss how domain specifications can be translated into Elisa programs.

 

5.1 Modeling Structure and Behavior

In the preceding chapters we have seen how concepts are translated into corresponding domain definitions and domain operations. The domain definitions define the relationships of (new) domains with other domains. They provide the static information structure of the system to build. The domain operations define the dynamic behavior of the system.

Accordingly, the static structure of a domain model is based on the domain definitions while the dynamic behavior of the model is determined by the interactions between the operations.

The translation of domain specifications into Elisa programs is dependent on the structure of the domain definitions and the associated operations.

Based on the domain definitions and the associated operation specifications we are able to implement Elisa components, depending on the different kinds of domains. This will be described in the following sections.

 

5.2 Implementation of Composite Domains

Composite domains are represented as descriptor types. Every composite domain specification is implemented by means of a component.

As discussed earlier, a composite domain definition has the following structure:

P = A + B + C + ...

where P is the name of a new domain and A, B, C, ... are domain elements. In this section we assume that A, B, C, ... are simple domain names; later on we will discuss other domain elements such as optional domains and repetitive domains.

From the domain definition a number of structure related operations can be derived, as has been specified in the preceding chapter (see Figure 4-1).

Based on domain definition for composite domains and the associated domain operations a skeleton of a component for composite domains can be specified (see Figure 5-1). The operation specifications are included in the interface section of the component, while the domain definition forms the basis of the corresponding descriptor as used in the implementation section.

component Ps; 			<< Ps is plural of P >>
  type P;
  << constructor operations: >>
  P (A, B, ...) -> P;
  P (A, B, C, ...) -> P;
  << accessor operations: >>
  A (P) -> A;
  B (P) -> B;
  C (P) -> C;
        .
   << mutator operations: >>
  change_A (P, A) -> nothing;
  change_B (P, B) -> nothing;
  change_C (P, C) -> nothing;
        .
 << specifications of behavior and usage related operations not included here >>
begin
  P (A, B, C, ...) = P:[ A; B; C; ...];   << descriptor of P >>
  << accessor operations: >>
  A (P) = P.A;
  B (P) = P.B;
  C (P) = P.C;
  << mutator operations: >>
  change_A (P, A) = [ P.A:=A ];
  change_B (P, B) = [ P.B:=B ];
  change_C (P, C) = [ P.C:=C ];
  << definitions of behavior and usage related operations not included here >>
end component Ps;

Figure 5-1: Skeleton of a component for Composite Domains

The following translation rules have been applied:

1. The name of the component is derived from the domain name P. As a convention we use the plural of P.

2. In the interface section of the component is P introduced as a type name.

3. The structure related domain operations specified for the P domain are included in the interface section (see also Figure 29-1). Only one form of constructor operation is used.

4. In the implementation section the definitions for the operations are included.

5. The first definition in the implementation section defines the layout of the descriptor for P. All the domain elements of P are used as descriptor elements. In this skeleton we assumed that all initial values are provided by the corresponding constructor operation. This is not always the case as has been discussed in the preceding chapter.

6. The accessor operations are implemented as simple access operations.

7. The mutator operations are implemented as simple assignment operations.

8. The behavior and usage related operations are not specified in the skeleton, because they are dependent on how the concept behavior should be modeled and how the component is used by other components.

To illustrate the use of the skeleton for composite domains, we will apply it to the example of the Person domain as already defined in previous chapters. We used as a domain definition for Person:

Person = Name + Address + Birthdate

If we compare this with the general form of a composite domain definition:

P = A + B + C + ...

then we can equate the names used in the general form to the example names in the following way:

P = Person
A = Name
B = Address
C = Birthdate

and apply this to the skeleton of Figure 5-1. By substituting for the skeleton names the equated names, a specific component is generated. In our example, the result is a specific component for the domain of Person (see Figure 5-2).

component Persons;
 type Person;
      Person (Name, Address, Birthdate) -> Person; 
      Name (Person)      -> Name;
      Address (Person)   -> Address;
      Birthdate (Person) -> Birthdate;
      change_Name (Person, Name)           -> nothing;
      change_Address (Person, Address)     -> nothing;
      change_Birthdate (Person, Birthdate) -> nothing;
begin
      Person (Name, Address, Birthdate) = Person :[ Name; Address; Birthdate ];
      Name (Person)      = Person.Name;
      Address (Person)   = Person.Address;
      Birthdate (Person) = Person.Birthdate;
      change_Name (Person, Name)          = [ Person.Name:=Name ]; 
      change_Address (Person, Address)    = [ Person.Address:=Address ];
      change_Birthdate (Person,Birthdate) = [ Person.Birthdate:=Birthdate];
end component Persons;

Figure 5-2: Example of a specific component for a composite domain

Notice that there are no behavior and usage related operations defined for the Person domain.

Exercises:

5.1 Derive from the skeleton of Figure 5-1 a specific component for the domain:

name = firstname + familyname

5.2 Derive from the skeleton of Figure 5-1 a specific component for the domain:

Country = CountryName + Capital + Area + Population

 

5.3 Implementation of Selection Domains

Selection domains are represented in Elisa as category types. Every selection domain specification is implemented by means of a component.

A selection domain definition has the following structure:

S = P | Q | R | T |...

where S is the name of a new domain and the domain elements P, Q, R, T,... are names of other domains.

Suppose that the following operations have been specified for the S domain:

<< accessor operations: >>
A (S) -> optional (A);
B (S) -> optional (B);
C (S) -> optional (C);

<< mutator operations: >>
change_A (S, A) -> optional (boolean);
change_B (S, B) -> optional (boolean);
change_C (S, C) -> optional (boolean);

Let us also assume that P and Q are names of composite domains and that R and T are names of (other) selection domains then a skeleton of a component for selection domains can be specified (see Figure 5-3).

component Ss;            << Ss is plural of S >>
  type S = category (P, Q, R, T, ...);
	<< accessor operations: >>
       A (S) -> optional (A);
       B (S) -> optional (B);
       C (S) -> optional (C);
        << mutator operations: >>
       change_A (S, A) -> optional (boolean);
       change_B (S, B) -> optional (boolean);
       change_C (S, C) -> optional (boolean);
begin
	 << accessor operations: >>
       A (P: X) = A (X);
       A (Q: Y) = A (Y);
       A (Z)    = ( A (R: Z), A (T: Z) );
       B (P: X) = B (X); 
       B (Q: Y) = B (Y);
       B (Z)    = ( B (R: Z), B (T: Z) );
       C (P: X) = C (X);
       C (Q: Y) = C (Y);
       C (Z)    = ( C (R: Z), C (T: Z) );
         << mutator operations: >>
       change_A (P: X, A) = [ change_A (X, A); true ];
       change_A (Q: Y, A) = [ change_A (Y, A); true ];
       change_A (Z, A)    = ( change_A (R: Z, A), change_A (T: Z, A) );
       change_B (P: X, B) = [ change_B (X, B); true ];
       change_B (Q: Y, B) = [ change_B (Y, B); true ];
       change_B (Z, B)    = ( change_B (R: Z, B), change_B (T: Z, B) );
       change_C (P: X, C) = [ change_C (X, C); true ];
       change_C (Q: Y, C) = [ change_C (Y, C); true ];
       change_C (Z, C)    = ( change_C (R: Z, C), change_C (T: Z, C) );
end component Ss; 

Figure 5-3: Skeleton of a component for Selection Domains

The following rules have been used for the skeleton selection component:

1. The name of the component is derived from the domain name S. As a convention we use the plural of S.

2. In the interface section of the component S is introduced as a type name, representing a category of P, Q, R, ...

3. The domain operations specified for the S domain are included in the interface section.

4. In the implementation section the definitions for the operations are included. Each definition consists of a number of definition rules. A definition rule tests in a parameter expression for a type corresponding to one of the members of the category. The last rule of a definition assumes that the argument belongs to another category. See the Language Description, Chapter 14, "Categories" for a detailed discussion about categories of classes and categories of categories.

In the skeleton it is assumed that the name of an operation on the S domain and the names of the corresponding operations of the member domains P, Q, R, T, ... are the same. However, that will not always be the case in practical situations; renaming will then be necessary. The parameter names X, Y, Z are local names.

5. The behavior and usage related operations are not specified in the skeleton, because they are dependent on how the concept behavior should be modeled and how the component is used by other components.

To illustrate the use of the skeleton for selection domains, we will apply it to the example of the domain of customers which we will define here as:

Customer = Person | Organization | Institution

with the following operations:

<< accessor operations: >>
Name (Customer) -> Name;
Address (Customer) -> Address;
Telephone (Customer) -> Telephone;

<< mutator operations: >>
change_Name (Customer, Name) -> optional (boolean);
change_Address (Customer, Address) -> optional (boolean);
change_Telephone (Customer, Telephone) -> optional (boolean);

We assume that Organization and Institution are both names of other selection domains, while Person is a class name as specified by the component of Figure 5-2. Based on the domain specifications for Customer and using the skeleton of Figure 5-3, we can now equate the names used in our example to the names used by the skeleton:

S = Customer
P = Person
R = Organization
T = Institution
A = Name
B = Address
C = Telephone

With these equations we can generate from the skeleton of Figure 5-3 a specific component for customers by substitution of the names (see Figure 5-4).

component Customers;
type Customer = category (Person, Organization, Institution);
      << accessor operations: >>
     Name (Customer) 	  -> Name;
     Address (Customer)   -> Address;
     Telephone (Customer) -> Telephone; 
      << mutator operations: >>
     change_Name (Customer, Name) 	    -> optional (boolean);
     change_Address (Customer, Address)     -> optional (boolean);
     change_Telephone (Customer, Telephone) -> optional (boolean);
begin
      << accessor operations >>
     Name (Person: X) = Name(X);
     Name ( Z) = ( Name ( Organization: Z), Name (Institution: Z) );
     Address (Person: X) = Address (X); 
     Address ( Z) = ( Address ( Organization: Z), Address (Institution: Z) );
     Telephone (Person: X) = Telephone (X);
     Telephone ( Z) = ( Telephone ( Organization: Z), Telephone (Institution: Z) );
      << mutator operations >>
     change_Name (Person: X, Name)= [ change_Name (X, Name); true ];
     change_Name (Z, Name ) = ( change_Name (Organization: Z, Name),
                         	change_Name (Institution: Z, Name) );
     change_Address (Person: X, Address) = [ change_Address (X, Address); true ];
     change_Address (Z, Address ) = ( change_Address (Organization: Z, Address),
                                      change_Address (Institution: Z, Address) );
     change_Telephone (Person: X, Telephone) = [ change_Telephone (X, Telephone); true ];
     change_Telephone (Z, Telephone ) = ( change_Telephone (Organization: Z, Telephone), 
                                          change_Telephone (Institution: Z, Telephone) );
end component Customers; 

Figure 5-4: Example of a specific component for a selection domain

In this component we assumed that the same operations defined for Customer are also defined for Person, Organization, and Institution. For the Person domain (see Figure 5-2) we know already that this is the case for the Name, Address, change_Name, and change_Address operations, but not for the corresponding Telephone operations. This means that Customers component, as a user of the Persons component requires additional, usage related operations. This does not only affect the domain operations for Person, but also the domain definition for Person, because an additional domain element will be necessary for the telephone number, and, as a result of this feedback, the component for Persons has to be adapted accordingly. So, this is an example of how usage related requirements will demand an iterative modeling process.

Exercise:

5.2 Derive from the skeleton of Figure 5-3 a specific component for the domain:

Organization = Government | Industry | University

with the same operations as defined for Customer.

 

5.4 Implementation of Enumeration Domains

Enumeration domains are represented in Elisa as enumeration types.

If an enumeration domain definition has the following structure:

E = 'V1' | 'V2' | 'V3' | ...

where E is the enumeration domain and V1, V2, V3, ... are the possible domain values, then the domain definition can be converted into a corresponding enumeration type:

type E = (V1, V2, V3, ...);

This is a skeleton of a symbolic enumerated type declaration. Note that the single quotes have been omitted from the domain values.

As an example, we translate the domain:

direction = 'North' | 'East' | 'South' | 'West'

into the corresponding enumeration type:

type direction = (North, East, South, West);

An enumeration type declaration is included in the interface section of the component where it is required.

Exercise:

5.4 Derive a specific component for the domain:

Person = Name + Address + Birthdate + Sex

where the sex domain is defined as:

Sex = 'Male' | 'Female'

 

5.5 Implementation of Optional Domains

Optional domains may be elements of composite domain definitions as illustrated by:

P = A + [B] + C + ...

where P is the name of a new domain and A, [B], C, ... are domain elements. [B] represents an optional domain. B is a simple domain name.

From the domain definition a number of operations for optional domains can be derived, as has been discussed in the previous chapter:

<< constructor operations: >>
P (A, B, C, ...) -> P;
P (A, C, ...) -> P;

<< accessor operations: >>
B (P) -> optional (B);
exists_B (P) -> boolean;

<< mutator operations: >>
change_B (P, B) -> nothing;
remove_B (P) -> nothing;

Based on these domain specifications a skeleton component for operations on optional domains can be specified (see Figure 5-5).

component Ps; 
 type P;
      P (A, B, C, ...) -> P;      << constructor operations >>
      P (A, C, ...) -> P;
      B (P) -> optional (B);      << accessor operations >>
      exists_B(P) -> boolean;
      change_B (P, B) -> nothing; << mutator operations >>
      remove_B (P) -> nothing;
begin
       << constructor operations: >>
      P (A, B, C, ...) = P:[A; B; C; ...]; << descriptor of P >>
      P (A, C, ...)    = P (A, null(B), C, ...); 
       << accessor operations: >>
      B (P) = P.B when P.B <> null(B);
      exists_B (P) = P.B <> null(B);
       << mutator operations: >>
      change_B (P, B) = [ P.B:= B ];
      remove_B (P) = [ P.B:= null(B) ];
end component Ps; 

Figure 5-5: Skeleton of a component for Optional Domains

The following rules have been applied:

1. The skeleton component for composite domains has been used as a basis (see Figure 5-1). Only the differences related to the optional B domain are shown in Figure 5-5.

2. Two constructor operations are specified. The first one can be used if the optional domain is available; the second one if that is not the case. Both operations are using the same descriptor layout. The second constructor operation assigns a null value to the optional domain.

3. The accessor operations are implemented as access operations which tests if the optional domain exists.

4. The mutator operations are implemented as simple assignment operations.

To illustrate the use of the skeleton for optional domains, we will apply it to the example of the domain of names which we will define here as:

Name = FirstName + [MiddleName] + FamilyName

If we compare this with the general form of a composite domain with an optional element:

P = A + [B] + C + ...

then we can equate the names used in the general form to the example names in the following way:

P = Name
A = FirstName
B = MiddleName
C = FamilyName

and apply this to the combination of the skeletons of Figure 5-1 and Figure 5-5. By substituting for the skeleton names the equated names, a specific component is generated. In our example, the result is a specific component for the domain of Name (see Figure 5-6).

component Names; 
 type Name;
      Name (FirstName, MiddleName, FamilyName) -> Name; 
      Name (FirstName, FamilyName)             -> Name;
      FirstName (Name) 	       -> FirstName;
      MiddleName (Name)        -> optional (MiddleName);
      exists_MiddleName (Name) -> boolean;
      FamilyName (Name)        -> FamilyName;
      change_FirstName (Name, FirstName)   -> nothing;
      change_MiddleName (Name, MiddleName) -> nothing;
      remove_MiddleName (Name)             -> nothing;
      change_FamilyName (Name, FamilyName) -> nothing;
begin
       << constructor operations: >>
      Name (FirstName, MiddleName, FamilyName) = Name:[FirstName; MiddleName; FamilyName]; 
      Name (FirstName, FamilyName) = Name (FirstName, null(MiddleName), FamilyName); 
       << accessor operations: >>
      FirstName (Name) = Name.FirstName;
      MiddleName (Name) = Name.MiddleName when Name.MiddleName <> null(MiddleName);
      exists_MiddleName (Name) = Name.MiddleName <> null (MiddleName);
      FamilyName (Name) = Name.FamilyName;
       << mutator operations: >>
      change_FirstName (Name, FirstName) = [ Name.FirstName:= FirstName];
      change_MiddleName (Name, MiddleName) = [ Name.MiddleName:= MiddleName];
      remove_MiddleName (Name) = [ Name.MiddleName:= null(MiddleName)];
      change_FamilyName (Name, FamilyName) = [ Name.FamilyName:= FamilyName];
end component Names; 

Figure 5-6: Example of a component for a composite domain with an optional element.

Notice, that there are no additional behavior related and usage related operations defined for the Name domain.

Exercise:

5.5 Derive a specific component for the domain:

Citizen = Person + [Passport]

 

5.6 Implementation of Repetitive Domains

Repetitive domains are often elements of composite domain definitions as illustrated by:

P = A + B + {C} + ...

where P is the name of a new domain and A, B, {C}, ... are domain elements. {C} represents a repetitive domain. C is a simple domain name.

Sometimes individual repetitive domains are used, which have a form like:

P = { C }

where P is the name of the repetitive domain.

Implementations for both forms will be examined. We will start with the first version.

5.6.1 Repetitive Domains as part of Composite Domains

From the first domain definition a number of operations for repetitive domains can be specified, as has been discussed in the previous chapter. In the corresponding skeleton we will support:

<< constructor operation: >>
P (A, B, ...) -> P;

<< accessor operations: >>
C's (P) -> multi (C);
C(P, C_ID) -> optional (C);

<< mutator operations: >>
add_C (P, C) -> nothing;
remove_C (P, C_ID) -> optional (C);

A repetitive domain represents a collection of items. In Elisa it may be implemented as an array, a list, or any other kind of a collection, such as a sequence, or a tree, which can support the required operations. In the following, we will assume that repetitive domains are implemented as lists.

Based on the domain specifications a skeleton component for operations on repetitive domains as part of composite domains can be given (see Figure 5-7).

component Ps;
 type P;
      P (A, B, ...) -> P; 	         << constructor operation >>
      C's (P)    -> multi (C); 	    << accessor operations >>
      C(P, C_ID) -> optional (C);
      add_C (P, C)	 -> nothing; 	<< mutator operations >>
      remove_C (P, C_ID) -> optional (C);
begin
      P (A, B, ...) = P:[A; B; CC = alist (C); ...]; << descriptor of P >>
       << accessor operations: >>
      C's (P) = items (P.CC);
      C (P, C_ID) = [ [ CI = items (P.CC); if C_ID (CI) == C_ID then return (CI) ];
                      message ("Unknown C_ID: ", C_ID);
                      no(C)];
       << mutator operations: >>
      add_C (P, C) = [ C_ID = C_ID(C);
                            [ CI = items(P.CC); 
                              if C_ID(CI) == C_ID then [ message ("Existing C_ID: ", C_ID); return] ];
                       add (P.CC, C) ];
      remove_C (P, C_ID) = [ [ CE = nodes (P.CC);
                               if C_ID (item (CE)) == C_ID then return (remove (CE)) ]; 
                             message("Unknown C_ID: ", C_ID);
                             no(C)];
end component Ps; 

Figure 5-7: Skeleton of a component for repetitive domains as part of composite domains

For this skeleton the same conventions have been used as for the skeleton of composite domains (Figure 5-1). In addition, the following assumptions have been made:

1. The descriptor for P starts with a empty list for C. However, sometimes a collection should start with an initial element. In that case, the constructor operation should be replaced by:

P (A, B, C, ...) = P:[ A; B; CC = {C}; ...];

2. The C's operation will return all the items of the {C} collection.

3. For the C operation it is assumed that each C is identified by a C_ID. The C definition uses the C_ID to select the C which should be returned. If the requested C is not available then the user of the component will be informed by means of the message operation and no result will be returned. The message operation is defined outside the component. A possible implementation is:

message (text, entity1) -> nothing;
message (T, E) = logln (Message (T, E) );

Message (text, entity1) => term;

The last line specifies a functor term with two arguments.

4. The add operation determines first the C_ID of the new element and then checks if an element with the same C_ID is already included in the list. If that is the case the user will be informed; no element will be added to the list. Otherwise the new element will be added to the list.

5. The order of elements in the list depends on the definition of the add operation and is reflected by the results of the C's operation. The add operation in the skeleton adds a new element to the end of the list. If the add operation should add a new element to the front of the list, the last statement should be replaced by:

add (C, P.CC);

Other variations are also possible such as inserting in the middle of the list depending on certain criteria.

6. The remove operation assumes also that each C is identified by a C_ID. The remove definition uses the C_ID to select the C which should be removed. It will remove the specified C if it exists. The removed C will be returned. If it does not exist the user will be informed and no result will be returned.

To illustrate the use of the skeleton for repetitive domains, we will apply it to the example of the Family domain as already defined in the previous chapters. We used as a domain definition for Family:

Family = Father + Mother + {Child}

We specified in the previous chapter the following operations for the repetitive part of the Family domain:

<< constructor operation: >>
Family (Father, Mother) -> Family;

<< accessor operations: >>
Children (Family) -> multi (Child);
Child (Family, Name) -> optional (Child);

<< mutator operations: >>
add_Child (Family, Child) -> nothing;
remove_Child (Family, Name) -> optional (Child);

Based on the domain specifications for Family and using the combination of the skeletons of Figure 5-1 and Figure 5-7, we can now equate the names used in our example to the names used by the skeleton:

P = Family
A = Father
B = Mother
C = Child
C_ID = Name
CC = Children

With these equations a specific component is derived for the domain of Family (see Figure 5-8).

component Families; 
 type Family;
      Family (Father, Mother) -> Family;          << constructor operation >>
      Father(Family)       -> Father;             << accessor operations >>
      Mother (Family)      -> Mother;
      Children (Family)    -> multi (Child); 
      Child (Family, Name) -> optional (Child);
      change_Father (Family, Father) -> nothing;  <<mutator operations>>
      change_Mother (Family, Mother) -> nothing;
      add_Child (Family, Child)      -> nothing;
      remove_Child (Family, Name)    -> optional (Child);
begin
       << constructor operation: >>
      Family (Father, Mother) = Family:[Father; Mother; Children = alist (Child)]; 
       << accessor operations: >>
      Father (Family) 	   = Family.Father;
      Mother (Family)      = Family.Mother;
      Children (Family)    = items (Family.Children);
      Child (Family, Name) = [ [ CI = items (Family.Children);
                                 if Name (CI) == Name then return(CI)];
                               message ("Unknown Name: ", Name);
                               no (Child) ];
       << mutator operations: >>
      change_Father (Family,Father) = [ Family.Father:=Father];
      change_Mother (Family,Mother) = [ Family.Mother:=Mother];
      add_Child (Family, Child) = 
          [ Name = Name (Child);
              [ CI = items (Family.Children);
                if Name (CI) == Name then [ message ("Existing Name: ", Name); return ] ];
            add(Family.Children, Child)];
      remove_Child (Family, Name) =
          [ [ CE = nodes (Family.Children);
              if Name (item (CE)) == Name then return (remove (CE)) ]; 
            message ("Unknown Name: ", Name);
            no(Child)];
end component Families; 

Figure 5-8: Example of a component for a composite domain with a repetitive element.

Exercises:

5.6 Derive a specific component for the domain:

Department = DepartmentName + [Manager] + {Employee}

5.7 Derive a specific component for the domain:

chessboard = white_pieces + black_pieces
white_pieces = { piece }
black_pieces = { piece }

 

5.6.2 Individual Repetitive Domains

Individual repetitive domains are characterized by the form:

P = {C}

where P is the name of the repetitive domain and C is a simple domain name.

The implementation of an individual repetitive domain is somewhat simpler than the implementation of a repetitive domain which is part of a composite domain, although the specifications of the operations of both kinds are the same, except for the constructor operation which is missing for individual repetitive domains.

Based on the skeleton component of Figure 5-7 a skeleton component for individual repetitive domains can be given (see Figure 5-9).

component Ps;
 type P = list(C);
      C's (P)    -> multi (C); 	         << accessor operations >>
      C(P, C_ID) -> optional (C);
      add_C (P, C)       -> nothing; 	    << mutator operations >>
      remove_C (P, C_ID) -> optional (C);
begin
       << accessor operations: >>
      C's (P) = items (P);
      C (P, C_ID) = [ [ CI = items (P); 
                        if C_ID (CI) == C_ID then return (CI) ];
                      message ("Unknown C_ID: ", C_ID);
                      no(C)];
       << mutator operations: >>
      add_C (P, C) = [ C_ID = C_ID(C);
                       [ CI = items(P); 
                         if C_ID(CI) == C_ID then [ message ("Existing C_ID: ", C_ID); return] ];
                       add (P, C) ];
      remove_C (P, C_ID) = [ [ CE = nodes (P);
                               if C_ID (item (CE)) == C_ID then return (remove (CE)) ]; 
                             message("Unknown C_ID: ", C_ID);
                             no (C)];
end component Ps; 

Figure 5-9: Skeleton of a component for individual repetitive domains

For this skeleton the same conventions have been used as for the skeleton of repetitive domains being part of composite domains (Figure 5-7). In addition, the following adaptations have been made:

1. The type of P is equivalent to the list (C) type.

2. Because the domain of P is represented by a list, there is no need for a separate constructor operation.

3. The specifications of all other operations are identical to the operations for repetitive domains of composite domains.

4. The implementations of those operations are equivalent to the operations for repetitive domains of composite domains. The only difference is that the references to the descriptor element P.CC have been replaced by references to the list parameter P.

In one of the following sections we will use an example based on the skeleton for individual repetitive domains.

Exercises:

5.8 Derive a specific component for the domain:

Articles = {Article}

5.9 Transform the skeleton of Figure 5-9 into a generic component.
5.10 Another approach is to transform the skeleton of Figure 5-9 into a set of polymorphic definitions. Compare this with the generic component of exercise 5.9.

 

5.7 Implementation of Sub-domains

Sub-domains are represented in Elisa as sub-types. If a sub-domain definition has the following form:

S = subdomain (P)

where S is the sub-domain and P is a simple domain name, then the domain definition can be converted into a corresponding sub-type definition:

type S = subtype (P);

If behavior related operations are defined on a sub-domain, then the sub-domain has to be implemented by an Elisa component; otherwise a single sub-type declaration will suffice.

To illustrate the implementation of behavior related operations defined on sub-domains we will use the examples described in the preceding chapter:

meter = subdomain (length)
yard = subdomain (length)

length = subdomain (real)

with the following operations:

meter + meter -> meter;
meter - meter -> meter;
yard + yard -> yard;
yard - yard -> yard;
meter (yard) -> meter;   << conversion from yard to meter>>
yard (meter) -> yard;      << conversion from meter to yard >>

We can now define the following component (see Figure 5-10).

component lengths;
 type meter  = subtype (length);
 type yard   = subtype (length);
 type length = subtype (real);
      meter + meter -> meter;
      meter - meter -> meter;
      yard + yard -> yard;
      yard - yard -> yard;
      meter (yard) -> meter;  << conversion from yard to meter >>
      yard (meter) -> yard;   << conversion from meter to yard >>
begin
      meter  + meter -> meter;
      meter1 + meter2 = meter: (real: meter1 + real: meter2);
      meter  - meter -> meter;
      meter1 - meter2 = meter: (real: meter1 - real: meter2);
      yard  + yard -> yard;
      yard1 + yard2 = yard: (real: yard1 + real: yard2);
      yard  - yard -> yard;
      yard1 - yard2 = yard: (real: yard1 - real: yard2);
      meter (yard) = meter: (0.9144 * real: yard); 
      yard (meter) = yard: (real: meter / 0.9144); 
end component lengths;

Figure 5-10: Example of a specific component for sub-domains

 

5.8 Implementation of Equivalent Domains

Equivalent domains are represented in Elisa as equivalent types. If an equivalent domain definition has the following form:

P = A

where P is the equivalent domain and A is a simple domain name, then the domain definition can be converted into a corresponding type definition:

type P = A;

All operations defined for A are also defined for P.

 

5.9 Implementation of Behavior and Structure-related Operations

In the previous chapter we described for a bank account, as an example, how to combine behavior and structure related operations. In this section we will apply the same example to examine a corresponding implementation. We will use the set of operations as specified in Figure 4-2 of the previous chapter. For the structure related operations we will use the skeleton of Figure 5-1. For the behavior related operations we will use the most simple forms (see Figure 5-11).

component Bank_Accounts;
 type BankAccount;
      BankAccount (AccountNumber, Owner) -> BankAccount; << constructor operation >>
      AccountNumber (BankAccount) -> AccountNumber;      << accessor operations >>
      Owner (BankAccount)         -> Owner;
     change_Owner (BankAccount, Owner) -> nothing;      << mutator operation >>
      Deposit (BankAccount, Amount)  -> nothing;         << behavior related operations >>
      Withdraw (BankAccount, Amount) -> Amount;
      Balance (BankAccount)          -> Amount;
      Close (BankAccount)            -> Amount;
begin
       << constructor operation: >>
      BankAccount (AccountNumber, Owner) = BankAccount:[ AccountNumber; Owner; Amount:=0.0 ]; 
       << accessor operations: >>
      AccountNumber (BankAccount) = BankAccount.AccountNumber;
      Owner (BankAccount)         = BankAccount.Owner;
       << mutator operation: >>
     change_Owner (BankAccount, Owner) = [ BankAccount.Owner:= Owner ];
       << behavior related operations: >>
     Deposit (BankAccount, Amount)  = [ BankAccount.Amount:=BankAccount.Amount + Amount];
     Withdraw (BankAccount, Amount) = [ BankAccount.Amount:=BankAccount.Amount - Amount; Amount ];
     Balance (BankAccount) = BankAccount.Amount;
     Close (BankAccount) = [ Amount:=BankAccount.Amount; BankAccount.Amount:=0.0; Amount];
end component Bank_Accounts;

Figure 5-11: Example of a component for a domain with structure and behavior related operations.

The behavior related operations are defined in the following way:

The Deposit definition increases the Account.Amount with the offered Amount.

The Withdraw definition decreases the Account.Amount with the requested Amount and returns the Amount of money.

The Balance operation returns the value of the actual Account .Amount from the bank account.

The Close operation closes the bank account and returns the remaining amount of money.

Although this is a very simple version of model of a real bank account ( for example, we neglected interest calculations and other aspects as financial limits), this example shows the basic operations of a bank account.

 

5.10 Operations on Elements of Collections

Using operations on the elements of a collection have a number of aspects which are interrelated:

Identification of collection elements.
Operations on the elements
Operations on the collection

In many problem domains, the elements of a collection are distinguished by unique identifiers. For example, books are identified by an ISBN number, people are distinguished by a social security number, cars are identified by a registration number, bank accounts are distinguished by an account number. For those applications, it often suffice to use the identification number to identify the corresponding element unambiguously. In those cases all the operations of the skeletons of Figure 5-7 and Figure 5-9 can be used.

However, there are also collections where the elements are not distinguished by unique identifiers. For those collections the C (P, C_ID) and remove_C (P, C_ID) operations in the skeletons of Figure 5-7 and Figure 5-9 are not defined. For such collections, elements can be selected by specifying the required characteristics of one or more elements. This can be done by reading all the elements of a collection by means of the C's (P) operation and testing for the required attributes.

Another aspect is the relation between the operations on the elements and the operations on the collection. Let use assume the following domain definitions:

Q = {P}

P = A + B + C

where Q is a collection of P elements. The skeleton of Figure 5-9 may be applied to the first domain definition while the skeleton of Figure 5-1 can be applied to the second definition. The resulting components have to co-orporate in order to demonstrate the required behavior.

To illustrate the interaction between operations on collections and the operations on its elements we will again use the example of bank accounts. We assume that a bank account is uniquely identified by an account number and that all bank accounts are part of a bank accounts collection. In terms of the preceding domain definitions we may say:

BankAccounts = {BankAccount}

BankAccount = AccountNumber + Owner + Amount

For the BankAccount domain we will use the component of BankAccounts as defined in the preceding section (see Figure 5-10).

For the collection of BankAccounts we will derive a component based on the skeleton of Figure 5-9. We equate the following names:

Ps = BankAccountsCollections

P = BankAccounts

C = BankAccount

C_ID = AccountNumber

With these equations a specific component can be generated for collections of bank accounts (see Figure 5-12).

component BankAccountsCollections;
 type BankAccounts= list (BankAccount);
       << accessor operations: >>
      BankAccounts (BankAccounts) -> multi (BankAccount);
      BankAccount (BankAccounts, AccountNumber) -> optional (BankAccount);
       << mutator operations: >>
      add_BankAccount (BankAccounts, BankAccount) -> nothing;
      remove_BankAccount (BankAccounts, AccountNumber) -> optional (BankAccount);
begin
       << accessor operations: >>
      BankAccounts (BankAccounts) = items (BankAccounts);
      BankAccount (BankAccounts, AccountNumber) = 
            [ [ CI = items (BankAccounts);
                if AccountNumber (CI) == AccountNumber then return (CI)];
              message("Unknown AccountNumber: ", AccountNumber);
              no (BankAccount) ];
       << mutator operations:>>
      add_BankAccount (BankAccounts, BankAccount) = 
            [ AccountNumber = AccountNumber (BankAccount);
              [ CI = items (BankAccounts);
                if AccountNumber (CI) == AccountNumber then 
                    [ message ("Existing AccountNumber: ", AccountNumber); return] ];
              add (BankAccounts, BankAccount) ];
      remove_BankAccount (BankAccounts, AccountNumber) = 
            [ [ CE = nodes (BankAccounts);
                if AccountNumber (item (CE)) == AccountNumber then return (remove (CE)) ]; 
              message ("Unknown AccountNumber: ", AccountNumber);
              no (BankAccount) ];
end component BankAccountsCollections;

Figure 5-12: Example of a collection component.

The component for BankAccounts as defined in Figure 5-10 and the component for BankAccountsCollections as defined in Figure 5-12 can now be used in the following session:

use Bank_Accounts;
use BankAccountsCollections;
message (text, entity1) -> nothing;
message (T, E) = logln (Message (T, E) ); 
Message (text, entity1) => term;
type Owner = text;
type AccountNumber = subtype (text);
type Amount = real;
BankAccountsCollection = alist (BankAccount);  << empty list >>
BAC = BankAccountsCollection;                  << abbreviation >> 
A1 = AccountNumber:"1234.5678.9012";           <<two account numbers>>
A2 = AccountNumber:"9876.5432.1098";
add_BankAccount (BAC, BankAccount (A1, "John Smith"));
add_BankAccount (BAC, BankAccount (A2, "Peter Jones"));
BAC?

After some preparatory work, two clients, John Smith and Peter Jones, decide to open new bank accounts. Both are using operations of the BankAccount component as well as of the BankAccountsCollections component. The result is an updated version of the BankAccountsCollection:

{ BankAccount:[ AccountNumber = "1234.5678.9012";
                Owner = "John Smith";
                Amount = 0.0], 
  BankAccount:[ AccountNumber = "9876.5432.1098";
                Owner = "Peter Jones";
                Amount = 0.0]}

Then, both clients decide to deposit some money on their bank accounts:

Deposit (BankAccount (BAC, A1), 2100.00);
Deposit (BankAccount (BAC, A2), 7900.00);
BAC?

The results are reflected by the corresponding accounts in the BankAccountsCollection:

{ BankAccount:[ AccountNumber = "1234.5678.9012";
                Owner = "John Smith";
                Amount = 2100.0], 
  BankAccount:[ AccountNumber = "9876.5432.1098";
                Owner = "Peter Jones";
                Amount = 7900.0]}

John Smith decides to withdraw an amount of money:

Withdraw (BankAccount (BAC, A1), 2500.00)?

the amount returned is:

2500.0

Peter Jones decides to close his bank account:

Close (remove_BankAccount (BAC, A2))?

He receives all the money of his account:

7900.0

Then, Peter Jones tries to withdraw some additional money from the closed bank account:

Withdraw (BankAccount (BAC, A2), 12500.00)?

Instead of getting the money he receives the message:

Message ("Unknown AccountNumber: ", "9876.5432.1098")

This matches with the contents of the accounts in the BankAccountsCollection:

{ BankAccount:[ AccountNumber = "1234.5678.9012";
                Owner = "John Smith";
                Amount = -400.0 ]}

This is the end of our exercise. We saw that for each operation on a bank account an operation on the BankAccount and on the BankAccountsCollection were needed.

 

5.11 Guidelines

The fourth step of the domain modeling process is to implement the domain specifications in Elisa. The domain specifications are the domain definitions, as defined in the second step together with the domain operations, as specified by the third step.

Translate every domain specification into corresponding Elisa constructs according to the rules as defined in this chapter. Go back to previous modeling steps when the translation does not succeed.

Use the Elisa system to test each component in its environment, as has been demonstrated in the previous section.

 

5.12 Summary

The fourth step of the conceptual modeling process is to implement the domain specifications in Elisa.
The domain specifications are the domain definitions, together with the domain operations.
Domain definitions are describing the static information structure of the system to build. The domain operations define the dynamic behavior of the system.
For each sort of domain a translation scheme has been given.
Some of these translation schemes are based on skeletons of Elisa components.
There are skeletons for composite domains, selection domains, optional domains, and repetitive domains.
Special attention has been given to the relationships between operations on collections and operations on their elements.
The resulting implementations should be tested in proper environments.

 

Top of Page   Part 4: Domain Modeling   Chapter 5: Domain Implementations

 
            
Introduction

Home | Highlights of Elisa | Integrating Different Paradigms | Getting Started with Elisa | Demo's  | What is Domain Orientation | Bibliography | Copyright | News | Contact | Contents

Language Description:

Lexical Elements | Basic Data Types and Expressions | Definitions | Streams | Backtracking | Statements and Special Expressions | Arrays | Lists | Descriptors | Components | Collections | Generic Components | Terms | Categories | Types | Built-in Definitions | Higher-order Definitions | External Interfaces | Index 

Data Structures: Sequences | Examples involving Lists | Trees | Graphs | Searching State Spaces | Language Processing | Knowledge Representations
Domain Modeling:

Domain Modeling | Concepts | Domain Definitions | Domain Operations | Domain Implementations | Systems | Case study: an Order processing system | Case study: an Airport Support system | Domain Orientation versus Object Orientation

Design Patterns:

Introduction | Abstract Factory | Builder | Factory Method | Prototype | Singleton | Adapter | Bridge | Composite | Decorator | Facade | Flyweight | Proxy | Chain of Responsibility | Command | Interpreter | Iterator | Mediator | Memento | Observer | State | Strategy | Template Method | Visitor 

 

click tracking

 

Send me your comments

 

This page was last modified on 27-09-2012 16:49:26   

free hit counter