Home > Articles > Programming > Windows Programming

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

Designing Objects Using Encapsulation

So far, the information provided has been used in simple examples. By now, you should understand encapsulation principles but probably don't have a firm grasp on how it's used in object design and the creation of real-world object classes.

Think about the real-world instance of an application that deals with a database. This example uses a book database that stores a list of books and authors. The application you are creating deals with the list of books in several different areas of the application, and some developers working on the application don't have much knowledge about databases.

The goal is to design a class that encapsulates a list of books and all the functionality required to query the list from the database while providing a simple interface to retrieve the data. With this class, any area of the application and any developer could retrieve the list of books without any knowledge of the underlying database or how it works. Also, the database code could change, and it would require a single class to change instead of several modules in a large application.

The design of class Books is shown in the UML diagram in Figure 3.1. The BookData class is defined within the Books class as Private and isn't accessible for use outside the Books class.

Figure 3.1 UML class diagram of Books class.

Without getting into how things are implemented in the Books class, concentrate on the public interface of the class. Listing 3.4 has all the public interfaces defined for the Books class.

Listing 3.4 Book.vb: Books Class Definition of Public Interface

Public Class Books
    Implements IDisposable

 'Private members for internal usage
 Private SqlCommand As SqlClient.SqlDataAdapter
 Private QueryData As BookData
 Private CurrentRow As Int32

 'Public New() interface for constructing the object
 Public Sub New()
  strTitle = Nothing
  strAuthor = Nothing
  dPrice = 0
  CurrentRow = -1

  SqlCommand = New SqlClient.SqlDataAdapter()
  SqlCommand.SelectCommand = New SqlClient.SqlCommand()
  SqlCommand.TableMappings.Add("Table", BookData.BOOK_TABLE)
 End Sub

 'Public dispose interface
 Public Overridable Sub Dispose() Implements IDisposable.Dispose
  Dispose(True)
  GC.SuppressFinalize(True)
 End Sub

 'Public Query method provides the user of the object a method to
 'start the query of the books in the database
 Public Sub Query()
  If Not QueryData Is Nothing Then
   QueryData.Dispose()
  End If

  CurrentRow = 0
  QueryData = New BookData()

  With SqlCommand
   Try
    With .SelectCommand
       .CommandType = CommandType.Text
       .CommandText = "SELECT title, au_lname, price FROM " & _
           BookData.BOOK_TABLE
       .Connection = New System.Data.SqlClient.SqlConnection( _
           "data source=(local)\NetSDK;initial catalog=pubs;" & _
           "persist security info=False;user id=sa;")
    End With
    .Fill(QueryData)
   Finally
    If Not .SelectCommand Is Nothing Then
     If Not .SelectCommand.Connection Is Nothing Then
      .SelectCommand.Connection.Dispose()
     End If
     .SelectCommand.Dispose()
    End If
    .Dispose()
   End Try
  End With
 End Sub

 'The public Fetch method provides the object user a method
 'of fetching each record that was returned from the query
 'The results are loaded into members for access via public properties
 Public Function Fetch() As Boolean
  Fetch = False

  If CurrentRow < 0 Then
   Exit Function
  End If

  With QueryData.Tables(BookData.BOOK_TABLE).Rows

   'If there are no more records, exit
   If CurrentRow >= .Count() Then
    CurrentRow = -1
    Exit Function
   End If

   Try
    strTitle = CType(.Item(CurrentRow).Item(BookData.TITLE_COLUMN), _
      String)
    strAuthor = CType(.Item(CurrentRow).Item(BookData.AUTHOR_COLUMN), _
      String)
    dPrice = CType(.Item(CurrentRow).Item(BookData.PRICE_COLUMN), _ 
      Double)
   Catch
    'Do nothing...
   Finally
    Fetch = True
    CurrentRow += 1
   End Try

  End With
 End Function

#Region " Property Definitions "

 Private strTitle As String
 Private strAuthor As String
 Private dPrice As Double

 'The public properties provide read only access to the
 'data returned from the query after each call to Fetch()
 Public ReadOnly Property Title() As String
 Get
  Title = strTitle
 End Get
 End Property

 Public ReadOnly Property Author() As String
 Get
  Author = strAuthor
 End Get
 End Property

 Public ReadOnly Property Price() As String
 Get
  Price = dPrice.ToString("c")
 End Get
 End Property

#End Region

End Class

You can compare the class definition to the UML class diagram in Figure 3.1 and get an idea of the interface. Adding the additional protected and private code to the class as shown in Listing 3.5 completes the Books class definition.

Listing 3.5 Book.vb: Books Class Definition of Private and Protected Interfaces

Public Class Books
    Implements IDisposable

  'Private members for internal usage
  Private SqlCommand As SqlClient.SqlDataAdapter
  Private QueryData As BookData
  Private CurrentRow As Int32

  'Public New() interface for constructing the object
  Public Sub New()
    ...
  End Sub

  'Public dispose interface
  Public Overridable Sub Dispose() Implements IDisposable.Dispose
    ...
  End Sub

  'Protected dispose for internal class usage
  Protected Overridable Sub Dispose(ByVal disposing As Boolean)
   If Not disposing Then
     Exit Sub ' we're being collected, so let the GC take care of
         ' this object
   End If

   If Not SqlCommand Is Nothing Then
      If Not SqlCommand.SelectCommand Is Nothing Then
        If Not SqlCommand.SelectCommand.Connection Is Nothing Then
         SqlCommand.SelectCommand.Connection.Dispose()
        End If
        SqlCommand.SelectCommand.Dispose()
      End If
      SqlCommand.Dispose()
      SqlCommand = Nothing
   End If
  End Sub

  'Public Query method provides the user of the object a method to 
  'start the query of the books in the database
  Public Sub Query()
   ...
  End Sub

  'The public Fetch method provides the object user a method
  'of fetching each record that was returned from the query
  'The results are loaded into members for access via public properties
  Public Function Fetch() As Boolean
   ...
  End Function

#Region " Property Definitions "

  Private strTitle As String
  Private strAuthor As String
  Private dPrice As Double

  Public ReadOnly Property Title() As String
  Get
   Title = strTitle
  End Get
  End Property

  Public ReadOnly Property Author() As String
  Get
   Author = strAuthor
  End Get
  End Property

  Public ReadOnly Property Price() As String
  Get
   Price = dPrice.ToString("c")
  End Get
  End Property

#End Region

  'The BookData class provides a DataSet that represents the data 
  'returned from the query
  <SerializableAttribute()> Private Class BookData
      Inherits DataSet

   Public Const BOOK_TABLE As String = "TitleView"
   Public Const TITLE_COLUMN As String = "title"
   Public Const AUTHOR_COLUMN As String = "au_lname"
   Public Const PRICE_COLUMN As String = "price"

   Public Sub New(ByVal info As SerializationInfo, _
       ByVal context As StreamingContext)
     MyBase.New(info, context)
   End Sub

   Public Sub New()
     MyBase.New()
     BuildDataTables()
   End Sub

   Private Sub BuildDataTables()
     '
     ' Create the Books table
     '
     Dim table As DataTable = New DataTable(BOOK_TABLE)

     With table.Columns
        .Add(TITLE_COLUMN, GetType(String))
        .Add(AUTHOR_COLUMN, GetType(String))
        .Add(PRICE_COLUMN, GetType(Double))
     End With

     Me.Tables.Add(table)
   End Sub
  End Class
End Class

The Books class obviously has a significant amount of code to provide the implementation logic needed by design. The good part is that the Books class user has very little code to write to retrieve a list of books. The use of the Books class is shown in the following code segment:

Public Sub MySub()
  Dim BookQuery As Books = New Books()

  BookQuery.Query()
  While BookQuery.Fetch()
   'Do something with data by accessing the properties within 
   'the Books object
  End While

End Sub

By building classes, such as Books, and encapsulating as much functionality as possible within the class, you can create complex applications with little code.

When a new instance of the Books class is created and assigned to the BookQuery object, all the initialization in the Books.New() constructor is executed. You can see how much code you would have to write repeatedly to duplicate the functionality without the Books class.

When the Books.Query() method is called, a SQL connection is made to a SQLServer, and a query is executed to bring back a list of books with author names and prices. The query results are saved in a DataSet for future processing. Users of the Books object execute only one statement while the class implementation takes care of all the dirty work in dealing with the database.

Finally, the users process the results of the query by repeatedly calling the Books.Fetch() method until it returns false. After each call to Fetch(), the Books object properties are set to the current record, and users can retrieve those values with the property methods.

  • + Share This
  • 🔖 Save To Your Account