Having the extension layer raises the first question: Should I add the new logic directly to the extension layer or should I add the logic by inheriting from the extension layer? The answer is both. The difference between the two methods lies in the circular relationship between the PFC and PFE layers. The logic added to the extension layer affects the whole library. The logic added by inheritance will only affect a new descendant object. Lets identify and define the different techniques.
| Extension by Insertion - Extending by adding code directly to the objects in the PFE layer or any layer between the PFC and PFE layers. These extensions are usually generic in nature. |
Extension by Insertion is very powerful. Every PFC object has a corresponding PFE descendant. Every object in the class library can be extended.
To use this method the insertion object must already exist in the class library. Another limitation of this method lies in a fact that any change made by insertion is global for the scope of PFE layer. Lets take a look at the transaction object n_tr. It contains several precoded and placeholder functions. To make the transaction object functional for sybase we need to override of_setuser, and if autocommit is set to true, of_begin and of_end as well. This is illustrated in Figure 3 below.

Figure 3 - Transaction extended by insertion
The above works great for a single database application, but what happens if you need to connect to different databases? Adding the DBMS specific functions to n_tr has made every transaction in the application specific to a single DBMS. There are ways around this problem, but as a rule extension by insertion has to be generic enough for the scope of the PFE layer. .
| Extension by Inheritance - Extending by inheriting an object from the PFE layer and adding code to further specialize the object. This form of extension is usually more specific in nature. |
Extension by inheritance can get more specific. Figure 4 below demonstrates how different windows can be extended by inheritance. In this case an application frame and multiple sheet windows are generated by inheriting from the windows in the PFE layer.

Figure 4 - Extension by Inheritance
Extension by Inheritance works great for the lowest descendant objects. There is a problem using extension by inheritance to add logic to the ancestor object. Because the new object is not a part of the inheritance chain, the functionality added to w_app_master will not make it to the w_app_frame. Figure 5 shows this limitation.

Figure 5 - Extension by Inheritance Limitations
Extension by Inheritance is limited to the lowest level descendants and concrete classes, it does not work for the abstract classes.
The limitation of the extension by inheritance could also be considered an advantage. Extension by inheritance makes it possible to generate very specific objects without affecting the rest of the class library. Lets get back to the transaction example. As you can see from figure 6 below we can generate a DBMS specific transaction through inheritance.

Figure 6 - Transaction Extended by Inheritance
The transaction object is easily extended by inheritance because it is declared as a global variable. The developer can select a specific transaction. This method may not be appropriate for pre-referenced service classes.
| Extension by Delegation - Extending by delegating tasks to new service objects. This form of extension can be generic or specific in nature but due to the additional complexity it may be reserved for the code intended for reuse. |
Two previous methods have added new logic through the use of inheritance. An important alternative that should not be overlooked is extension by delegation or by creating new services. Because the services are associated at runtime this methods opens new opportunities for implementation techniques such as operational polymorphism, which allows dynamic object association. This method was used to implement the file and platform PFC services. Figure 7 demonstrates how the transaction object can be extended using this technique.

Figure 7 - Transaction Extended by Delegation
The of_connect function is extended to create the appropriate transaction service.
CHOOSE CASE Upper(Left(this.dbms, 3))
CASE SYB, SYC
this.inv_trsrv = CREATE n_cst_trsrv_sybase
CASE OR7, O71, O72, O73
this.inv_trsrv = CREATE n_cst_trsrv_oracle
CASE ELSE
// unsupported dbms
return -1
END CHOOSE
return super::of_connect()
The of _setuser function is then delegated to the transaction service.
IF IsValid(inv_trsrv) THEN
inv_trsrv.of_setuser(as_userid, as_password)
END IF
Typically, the tasks are delegated by client objects to service providers. Application delegates its tasks to application manager, datawindow control to datawindow services, windows to window services and so on. However, the service delegation is not limited to two levels. The service providers may assume the role of client objects and transfer some responsibilities to other service objects. Therefore it is possible to use the extension by delegation technique to extend the PFCs service objects.
Article based on the material from "PowerBuilder Foundation Class Library Professional
Reference", McGraw Hill, ISBN
0-07-913267-7.
Last revised: February 15, 2004 03:58 AM.