Home > Articles > Programming > Windows Programming

  • Print
  • + Share This
This chapter is from the book

Rule 2-8: Model in Terms of Sessions Instead of Entities

When it comes to designing the object model for your system, you should consider whether to design your classes around sessions or entities. Entities represent a more traditional object-oriented approach, in which classes are based on real-world entities in your system—customers, orders, products, and so forth. In contrast, sessions represent the set of expected interactions between clients and your objects. Although session-based class designs may deviate from pure OODs, the motivation is performance over elegance. Session-based systems strive to streamline client interactions, which is particularly important when objects are out-of-process (e.g., in distributed applications).23

Obviously, the issue of design is no small matter. For example, consider a multi-tier system with business and data layers. How should the data access layer behave?24 Should it model each table as a class? If so, where do queries over multiple tables fit in? Perhaps there should be just one class per database. And what about the business layer? Are lots of smaller classes better than a few larger ones? How should they be grouped to take advantage of polymorphism in the client? Each of these questions may have different answers, based on system goals.

At a high level, most business systems are the same: They gather information from their users and submit this information for processing. The system is thus divided into at least two parts, the front-end user interface and the back-end processor. Each communication from the front-end to the back-end represents a unit of work and constitutes a round-trip. Session-based designs model user scenarios in an attempt to minimize round-trips. Traditional OODs often don't take into account the cost of a round-trip, yielding less than optimal performance.

For example, consider the traditional entity-based design of a CCustomer class. The class models the state and behavior of a customer in the system; in particular, allowing easy access to customer information:

'** class module: CCustomer (traditional OOD)
Option Explicit

Public Name As String
Public StreetAddr As String
Public City As String
Public State As String
Public Zip As String

Public Property Get CreditLimit() As Currency
	'** return customer's credit limit for purchases
End Property

Public Sub PlaceOrder(ByVal lProductNum As Long, _
           ByVal lQuantity As Long)
	'** code to place an order for this customer
End Sub

Although straightforward to understand, consider what the client must do to change a customer's address:

Dim rCust As CCustomer
Set rCust = ...

With rCust
	.StreetAddr = <new street address>
	.City = <new city>
	.State = <new state>
	.Zip = <new zipcode>
End With

The cost is four round-trips, one per datum. Likewise, placing an order for N different products requires N + 1 trips, one to check the customer's credit limit and another N to order each product.

A better approach, at least from the perspective of performance, is to redesign the CCustomer class based on the expected user scenarios: getting the customer's information, updating this information, and ordering products. This leads to the following session-based result:

'** class module: CCustomer (revised session-based design)
Option Explicit

Private Name As String     '** no public access to data
Private StreetAddr As String
Private City As String
Private State As String
Private Zip As String

Public Sub GetInfo(Optional ByRef sName As String, _
      Optional ByRef sStreetAddr As String, _
      Optional ByRef sCity As String, _
      Optional ByRef sState As String, _
      Optional ByRef sZip As String)
	sName = Name
	sStreetAddr = StreetAddr
	sCity = City
	sState = State
	sZip = Zip
End Sub

Public Sub Update(Optional ByVal sName As String = "?", _
      Optional ByVal sStreetAddr As String = "?", _
      Optional ByVal sCity As String = "?", _
      Optional ByVal sState As String = "?", _
      Optional ByVal sZip As String = "?")
	If sName <> "?" Then Name = sName
	If sStreetAddr <> "?" Then StreetAddr = sStreetAddr
	If sCity <> "?" Then City = sCity
	If sState <> "?" Then State = sState
	If sZip <> "?" Then Zip = sZip
End Sub

Public Sub PlaceOrder(laProducts() As Long)
	'** confirm that client passed a 2D array (products times quantities)
	Debug.Assert UBound(laProducts, 1) = _
         UBound(laProducts, 2)

	'** code to check that credit limit is sufficient
	'** code to place entire order for this customer
End Sub

First of all, notice there is no public access to customer data. All reads and writes must be done via methods. As a result, an address change now takes only one round-trip call to Update. Likewise, PlaceOrder is redesigned to accept an array of product numbers and quantities, allowing an entire order to be placed via one round-trip call. In short, the class contains one entry for each task that the user may need to perform. Although the class's interface is arguably more cumbersome for clients to use, the potential increase in performance is significant, especially across a network.

Session-based designs are usable at every level of a system. For example, in a standard multi-tier application, your business objects would model client sessions, whereas your data access objects model business object sessions. For the latter, your data access design may be as simple, and as efficient, as two methods: one to read and one to write:

'** class module: CDataAccess (minimal session-based design)
Option Explicit

Public Function ReadDB(sConnectionInfo As String, _
            sSQL As String) As ADODB.Recordset
	'** code to open DB, build recordset, disconnect, and return it...
End Function

Public Function UpdateDB(sConnectionInfo As String, _
             sSQL As String)
	'** code to open DB and update via SQL...
End Function

Obviously, good design is the proper balance of usability, maintainability, extensibility, and performance.

  • + Share This
  • 🔖 Save To Your Account