Enclosing Features and Data in .NET Objects
- Designing a Public Class Interface
- Protecting Class Members
- Defining Private Class Members
- Designing Objects Using Encapsulation
- Summary
- Q&A
- Workshop
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.
Listing 3.2 Form1.vb: Form1 Class with CircleButton Object
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.