A how-to code example from ASPFree.
One of the most important principles of objects is that they enclose functionality and data while providing an interface with which to interact with other objects. An object's users don't care how the object does what it does; they just want it done well and to have a simple interface. This "black box" characteristic of objects is known as encapsulation. An object encapsulates data and functionality, keeping the implementation details out of users' hands. The object provides a simple interface as a combination of methods, and in some cases member data, for users to control the object's actions at a high level without dealing with the implementation details.
Today you will learn about encapsulation in more detail and how to design classes with encapsulation techniques. You will learn how to design a class with a simple interface that relieves users from dealing with complex implementation details. By the end of the day, you should know how to do the following: Create an object with a simple public interface. Provide a protected interface for derived objects. Keep implementation details private within an object. Design an object with good encapsulation techniques.
Designing a Public Class Interface
When designing an object class, you first decide on the object's function and then determine what type of interface other objects and users will have available for use. The interface you provide in an object class for other users and objects is the public interface. The public interface is fairly self-describing. It's publicly available to all users of an object.
The art of object design is to make only what is necessary publicly available and encapsulate as much of the object functionality and attributes as possible. Using encapsulation techniques in object class design will make your objects easier to use and less likely to result in errors caused from misuse. Most users of an object don't care and don't need to know how a class implements internal functionality. There's no point to provide more interfaces to an object than what's really needed. For example, an object that draws a circle at a given location and draws a supplied string in the middle of the circle would need a public interface consisting of the Draw() method, Pos() property, and Text() property. Listing 3.1 shows the class definition of the CircleButton class.
Listing 3.1 CircleButton.vb: CircleButton Class Showing Encapsulation
Public Class CircleButton 'Public properties to set/get private member data Public Property Pos() As Drawing.Point ... End Property Public Property Text() As String ... End Property 'Public subroutine to draw the circle button Public Sub Draw(ByRef DrawOn As Windows.Forms.Form) ... End Sub End Class
Don't worry too much about the implementation with the CircleButton class definition. Recognizing the amount of functionality encapsulated with this simple object class is more important. The CircleButton class has a public interface with two properties, Pos() and Text(), which get and set the position of the circle and content of the text. It also has the public subroutine, Draw(), which receives a reference to the Form object on which the circle is drawn. There isn't too much to this interface, and it's simple to use, as highlighted in Listing 3.2. [bold] [bold]Listing 3.2 [/bold]Form1.vb: Form1 Class with CircleButton Object[/bold]
Public Class Form1 Inherits System.Windows.Forms.Form Private MyCircleBtn As CircleButton #Region " Windows Form Designer generated code " Public Sub New() MyBase.New() 'This call is required by the Windows Form Designer. InitializeComponent() MyCircleBtn = New CircleButton() MyCircleBtn.Pos = New Drawing.Point(10, 10) MyCircleBtn.Text = "My Circle" End Sub ... #End Region Public Sub DoPaint(ByVal sender As Object, ByVal e As PaintEventArgs) _ Handles MyBase.Paint MyCircleBtn.Draw(Me) End Sub End Class
After you set up the object state in the New() constructor, only a single line of code is required to actually display the circle button in the DoPaint() subroutine. The simplicity of the interface makes it easy and clean to use, and you don't have to deal with complex implementation details. [bold]Protecting Class Members[/bold] When a class definition is used to build another class through inheritance, an interface is available to the new class. Like a back door, the new class can call any methods and use any member data declared protected and still remain hidden to any class users. The protected class interface allows the class definition to provide a specific interface to any new class built to enhance the functionality of an existing class. Member data is commonly defined as protected if the members' usage isn't governed by code. Member data for a class property is a good example. If the property is a simple value and doesn't require special code, it's more efficient for the new class definition to be able to use and set the data value directly. The following protected members are added to the class definition in Listing 3.1. These members are available through the public properties defined in the class to users and directly available to any class that inherits from it:
Public Class CircleButton 'Protected member data Protected ptPos As Drawing.Point Protected strText As String 'Public properties to set/get protected member data Public Property Pos() As Drawing.Point Get Pos = ptPos End Get Set(ByVal Value As Drawing.Point) ptPos = Value End Set End Property Public Property Text() As String Get Text = strText End Get Set(ByVal Value As String) strText = Value End Set End Property ...
You can also define methods as protected to provide a semi-private interface to any class that inherits your class. The methods aren't available to class users, so they are still contained within the "black box." Derived classes can override protected methods to change the functionality of the object or even make a method public, which changes the interface available to users of the new class. Overall, protected is used whenever the encapsulated functionality and member data are available for enhancement and access by new class definitions. Because the methods and members aren't visible to the class users, the integrity of the object design is maintained. [bold]Defining Private Class Members[/bold] All functionality and data truly encapsulated by a class and not available to any outside user or derived class are defined as private. When you're designing "black box" classes, all supporting methods, classes, and data aren't needed by any user except the class itself. The class definition in Listing 3.3 shows a class that has private member data and methods defined to implement the public interface. [bold] [bold]Listing 3.3 [/bold]CircleButton.vb: CircleButton Class Modified to Encapsulate Functionality[/bold]
Class CircleButton 'Public draw functions Public Sub DrawRedButton(ByRef DrawOn As Windows.Forms.Form) Draw(DrawOn, Drawing.Color.Red) End Sub Public Sub DrawBlackButton(ByRef DrawOn As Windows.Forms.Form) Draw(DrawOn, Drawing.Color.Black) End Sub 'Private subroutine to draw the circle button in the specified color Private Sub Draw(ByRef DrawOn As Windows.Forms.Form, _ ByVal Color As Drawing.Color) ... End Sub End Class
Listing 3.3 shows a simple instance in which a class has a public interface that calls a private method. In this case, the Color parameter is added to the Draw() method, and two new public methods, DrawRedButton() and DrawBlackButton(), are defined to draw red and black buttons. The object's user doesn't have to know or care what it takes to draw the red or black button—and doesn't need to. The details are hidden from the public interface, providing object users a more intuitive interface. In more complex classes, providing simple public interfaces that encapsulate more complex interfaces is one feature that makes object-oriented programming quicker and self-documenting. Sometimes it's necessary for a class to define other classes used only within the class implementation. By encapsulating other class definitions, a class defines its own private objects for implementation of the overall functionality. Without encapsulation, these class definitions are available for other classes to use, which sometimes isn't desirable. The following code shows how a class defines its own classes:
Public Class MyTestClass ' Can be used as MyTestClass.PublicInnerClass by anyone Public Class PublicInnerClass '... End Class ' Can only be used by MyTestClass or derivatives Protected Class ProtectedInnerClass '... End Class ' Can only be used by MyTestClass Private Class PrivateInnerClass '... End Class End Class
As you can see in the code segment, a class also can provide other class definitions available for use outside MyTestClass. For example, an object may have a closely related object class that it uses for keeping internal information. However, if that information is made available as part of the public interface, the internal class must also be publicly available. The internal public class, PublicInnerClass, is referenced by using the following statement: MyTestClass.PublicInnerClass Attempts to reference nonpublic classes defined within MyTestClass won't compile because they aren't available for public use. [bold]Designing Objects Using Encapsulation[/bold] 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. [bold]Figure 3.1 UML class diagram of Books class.[/bold] 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. [bold] [bold]Listing 3.4 [/bold]Book.vb: Books Class Definition of Public Interface[/bold]
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. [bold] [bold]Listing 3.5 [/bold]Book.vb: Books Class Definition of Private and Protected Interfaces[/bold]
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. [bold]Summary[/bold] Today you learned how encapsulation in class design can hide a complex process in an object and provide a simple interface. By using object-oriented programming, you learned that encapsulation can speed development and limit the number of errors within an application. As you design object-oriented applications, remember that encapsulating functionality and data in a class is a fundamental goal of object-oriented development. If you use encapsulation effectively and creatively, writing stable, complex applications will become simple. [bold]Q&A[/bold] [bold] Q Isn't there a standard that dictates how to encapsulate?[/bold] [bold]A[/bold] The quick answer is no. Encapsulation is facilitated by object-oriented development. You can write an application with objects and classes that don't really encapsulate much of anything. It's up to the class designer to encapsulate functionality and data into a "black box" that users can easily use without knowing the internal implementation. [bold] Q Can I go too far with encapsulation?[/bold] [bold]A[/bold] Yes. If you take encapsulation too far, a class becomes rigid and useful only for its original purpose. Rigid classes don't provide a flexible enough interface to allow new classes to inherit and enhance its functionality. Also, the interface is too rigid for its intended use because it evolves and is constantly being changed to accommodate new requirements. Encapsulation is a good thing, but too much can be a bad thing. [bold]Workshop[/bold] The Workshop provides quiz questions and an exercise to help solidify your understanding of encapsulation and provide you with experience in using what you've learned. Answers are provided in Appendix A, "Answers to Quizzes." [bold]Quiz[/bold] What's the recommended scope for member data in a well-encapsulated class design? What's the purpose of encapsulation in class design?What interfaces are available to users of an encapsulated class? [bold]Exercise[/bold] Modify the Books class in Listing 3.5 to add a subroutine, FillList(), that queries the book data and loads the results into a supplied ListView object. By defining this subroutine, you extend the functionality of the Books class, which in turn removes the code from the user application. © Copyright Pearson Education. All rights reserved.
| DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware. |
More Code Examples Articles
More By Steve Hughes
developerWorks - FREE Tools! |
Discover how Rational tools and best practices for testing can make your job easier. The new Rational Testing eKits provide you with valuable resources – including demos, webcasts, tutorials, and articles – that help you address your specific testing needs across the software lifecycle. Five new eKits are available covering the topics of Requirements and Test Management, Functional Testing, Performance Testing, Code Quality and Embedded Systems, and SOA and Web Services Testing. FREE! Go There Now!
|
|
|
|
Join us for this web seminar to learn how you can defend your web applications from attack. Learn about the 3 most common web application attacks, including how they occur and what can be done to prevent them. We’ll also discuss manual versus automated approaches for scanning and identifying web application vulnerabilities and how IBM Rational AppScan, an automated vulnerability scanner, can help you automate more of what you are doing manually today. FREE! Go There Now!
|
|
|
|
Join this webcast to discover the key requirements for successful change and release management. Learn how to extend your .NET environment to improve productivity and collaboration, and address core problems afflicting team development. In this webcast, we’ll review typical challenges faced by customers and how to resolve them with the IBM Rational Change and Release Management solution, including Rational ClearCase, Rational ClearQuest and Rational Build Forge. Replay is available for 9 months. FREE! Go There Now!
|
|
|
|
This whitepaper provides areas to consider when evaluating any software configuration management solution. It addresses how the IBM solutions (Rational ClearCase and Rational ClearQuest) meet the needs and requirements of both project leaders and developers to provide successful Software Change and Configuration Management. FREE! Go There Now!
|
|
|
|
Join this webcast to see how IBM Data Studio Developer and pureQuery can take the pain out of Java data access. uApplications developed using both Java and SQL have become a common requirement. Database connectivity using Java Database Connectivity (JDBC) to create an application is a multi-step tedious process, and tooling that covers both SQL and Java has been unavailable, until now. IBM Data Studio introduces the pureQuery platform: a high-performance, Java data access platform focused on simplifying the tasks of developing, managing, and optimizing database applications and services. FREE! Go There Now!
|
|
|
|
Regression testing -- in which code is thoroughly tested to ensure that changes have not produced unexpected results -- is an important part of any development process. But many testing environments neglect the terminal-based applications that still form the backbone of many industries. In this tutorial, you'll learn how the Rational Functional Tester Extension for Terminal-Based Applications works with other Rational Functional Tester to help test terminal-based applications quickly and easily. FREE! Go There Now!
|
|
|
|
Visit IBM developerWorks to download a free trial version of IBM Rational Business Developer V7.1. Rational Business Developer offers rapid and simplified development of business applications and services through Enterprise Generation Language (EGL) tools, generating Java or mainframe solutions while shielding developers from technical complexities. FREE! Go There Now!
|
|
|
|
Visit IBM developerWorks to download IBM DB2 Express-C 9.5, a no-charge version of DB2 Express 9 database server. DB2 Express-C offers the same core data server base features as other DB2 Express editions and provides a solid base to build and deploy applications developed using C/C++, Java, .NET, PHP, and other programming languages. FREE! Go There Now!
|
|
|
|
David Barnes, Lead Evangelist for IBM Emerging Internet Technologies will discuss aspects of Web 2.0 that bring value to corporations, academia, and government. He'll also discuss IBM's vision around Web 2.0, including the importance of remixability and consumability. The discussion will culminate with examples of various IBM Software Group solutions you can use to get ahead of the Web 2.0 adoption curve. FREE! Go There Now!
|
|
|
|
As organizations integrate software into every aspect of business, they are constantly pressured to deliver faster, better, and cheaper results. Unfortunately, a “dis-integrated” software delivery approach reduces returns while increasing costs. This IBM Rational White Paper shows how Integrated Requirements Management aligns organizations around maximizing value and keeping pace with change. FREE! Go There Now!
|
|
|
|
All FREE IBM® developerWorks Tools! |