The
SimpleTest classes provide functionality through public methods. If
you're familiar with class-based programming ,
you'll remember that classes can also store information in private
member variables and provide property procedures that allow the calling
code to modify this information. For example, a Person class might have a
FirstName property.
When you use properties and store information in member variables, you're using stateful design.
In stateful design, the class has the responsibility of maintaining
certain pieces of information. In stateless design, no information is
retained between method calls. Compare the earlier SimpleTest class,
which uses stateless design, to the stateful SimpleTest class shown
here:
Public Class SimpleTest
Private _data As String
Public Property Data() As String
Get
Return _data
End Get
Set(ByVal value As String)
_data = value
End Set
End Property
Public Function GetInfo() As String
Return "You invoked SimpleTest.GetInfo()," & _
"and data is '" & Data & "'"
End Function
End Class
Programmers who design
large-scale applications (such as web applications) sometimes debate
whether stateful or stateless programming is best. Stateful programming
is the most natural, object-oriented approach, but it also has a few
disadvantages. To accomplish a common task, you might need to set
several properties before calling a method. Each of these steps adds a
little bit of unneeded overhead. A stateless design, on the other hand,
often performs all its work in a single method call. However, because no
information is retained in state, you may need to specify several
parameters, which can make for tedious programming. A good example of
stateful versus stateless objects is shown by the FileInfo and File
classes.
There is no short answer
about whether stateful or stateless design is best, and it often depends
on the task at hand. Components that are high-performance, components
that use transactions, components that use limited resources such as a
database connection, or components that need to be invoked remotely
(such as web services) usually use stateless design, which is the
simplest and most reliable approach. Because no information is retained
in memory, fewer server resources are used, and no danger exists of
losing valuable data if a software or hardware failure occurs. The next
example illustrates the difference with two ways to design an Account
class.
1. A Stateful Account Class
Consider a stateful
account class that represents a single customer account. Information is
read from the database when it's first created in the constructor
method, and this information can be updated using the Update() method.
Public Class CustomerAccount
Private _accountNumber As Integer
Private _balance As Decimal
Public Property Balance() As Decimal
Get
Return _balance
End Get
Set(ByVal value As Decimal)
_balance = value
End Set
End Property
Public Sub New(ByVal accountNumber As Integer)
' (Code to read account record from database goes here.)
End Sub
Public Sub Update()
' (Code to update database record goes here.)
End Sub
End Class
If you have two
CustomerAccount objects that expose a Balance property, you need to
perform two separate steps to transfer money from one account to
another. Conceptually, the process works like this:
' Create an account object for each account,
' using the account number.
Dim accountOne As New CustomerAccount(122415)
Dim accountTwo As New CustomerAccount(123447)
Dim amount As Decimal = 1000
' Withdraw money from one account.
accountOne.Balance -= amount
' Deposit money in the other account.
accountTwo.Balance += amount
' Update the underlying database records using the Update() method.
accountOne.Update()
accountTwo.Update()
The problem here is that if
this task is interrupted halfway through by an error, you'll end up with
at least one unhappy customer.
2. A Stateless AccountUtility Class
A stateless object might expose only a shared method named FundTransfer(), which performs all its work in one method:
Public Class AccountUtility
Public Shared Sub FundTransfer(ByVal accountOne As Integer, _
ByVal accountTwo As Integer, ByVal amount As Decimal)
' (The code here retrieves the two database records,
' changes them, and updates them.)
End Sub
End Class
The calling code can't use
the same elegant CustomerAccount objects, but it can be assured that
account transfers are protected from error. Because all the database
operations are performed at once, they can use a database stored
procedure for greater performance and can use a transaction to ensure
that the withdrawal and deposit either succeed or fail as a whole.
' Set the account and transfer details.
Dim amount As Decimal = 1000
Dim accountIDOne As Integer = 122415
Dim accountIDTwo As Integer = 123447
AccountUtility.FundTransfer(accountIDOne, accountIDTwo, _
amount)
In a mission-critical system,
transactions are often required. For that reason, classes that retain
little state information are often the best design approach, even though
they aren't quite as satisfying from an object-oriented perspective.
There is one
potential compromise. You can create stateful classes to represent
common items such as accounts, customers, and so on, without adding any
functionality. Then, you can use these classes as data packages to send
information to and from a stateless utility class.
|
|