What Is Object-Oriented Programming?


Object-oriented programming is a relatively new approach to creating computer applications that seeks to address many of the problems with so-called traditional programming techniques. The type of programming you have seen so far is known as functional (or procedural) programming, often resulting in so-called monolithic applications, meaning that all functionality is contained in a few modules of code (often just one). With OOP techniques, you often use many more modules of code, each offering specific functionality, and each module may be isolated or even completely independent of others. This modular method of programming gives you much more versatility and provides more opportunity for code reuse.

To illustrate this further, imagine that a high-performance application on your computer is a top-of-the- range racing car. If written with traditional programming techniques this sports car is basically a single unit. If you want to improve this car, you have to replace the whole unit by sending it back to the manufacturer and getting their expert mechanics to upgrade it, or by buying a new one. If OOP techniques are used, then you can simply buy a new engine from the manufacturer and follow their instructions to replace it yourself, rather than taking a hacksaw to the bodywork.

In a more traditional application the flow of execution is often simple and linear. Applications are loaded into memory, start executing at point A, end at point B, and are then unloaded from memory. Along the way various other entities might be used, such as files on storage media, or the capabilities of a video card, but the main body of the processing goes on in one place. The code along the way is generally concerned with manipulating data through various mathematical and logical means. The methods of manipulation are usually quite simple, using basic types such as integers and Boolean values to build up more complex representations of data.

With OOP things are rarely so linear. Although the same results are achieved, the way of getting there is often very different. OOP techniques are firmly rooted in the structure and meaning of data, and the interaction between that data and other data. This usually means putting more effort into the design stages of a project, but it has the benefit of extensibility. Once an agreement is made as to the representation of a specific type of data, that agreement can be worked into later versions of an application, and even entirely new applications. The fact that such an agreement exists can reduce development time dramatically. This explains how the racing car example works. The agreement here is how the code for the "engine" is structured, such that new code (for a new engine) can be substituted with ease, rather than requiring a trip back to the manufacturer.

As well as providing an agreed-on approach to data representation, OOP programming often simplifies things by providing an agreement on the structure and usage of more abstract entities. For example, an agreement can be made not just on the format of data that should be used to send output to a device such as a printer but also on the methods of data exchange with that device. This would include what instructions it understands and so on. Going back to the racing car example, the agreement would include how the engine connects to the fuel tank, how it passes drive power to the wheels of the car, and so on.

As the name of the technology suggests, this is achieved using objects. So, what is an object?

What Is an Object?

An object is a building block of an OOP application. This building block encapsulates part of the application, which may be a process, a chunk of data, or some more abstract entity.

In the simplest sense, an object may be very similar to a struct type such as those you saw earlier in the book, containing members of variable and function types. The variables contained make up the data stored in the object, and the functions contained give access to the functionality of the object. Slightly more complex objects might not maintain any data; instead, they can represent a process by containing only functions. For example, an object representing a printer might be used, which would have functions enabling control over a printer (allowing you to print a document, print a test page, and so on).

Objects in C# are created from types, just like the variables you've seen already. The type of an object is known by a special name in OOP, its class. You can use class definitions to instantiate objects, which means to create a real, named instance of a class. The phrases instance of a class and object mean the same thing here; be sure to note at this point that class and object mean fundamentally different things.

Note

The terms class and object are often confused, and it is important to get the distinction right from the beginning. It may help you to visualize these terms using the earlier racing car example. In this example, a class might be thought of as the template for the car, or perhaps the plans used to build the car. The car itself is an instance of those plans, so it could be referred to as an object.

In this chapter, you picture classes and objects using Universal Modeling Language (UML) syntax. UML is a language designed for modeling applications, from the objects that build them up, to the operations they perform, and to the use cases that are expected. Here, you use only the basics of this language, which will be explained as you go along. I won't be going into the more complex aspects because UML is a specialized subject that has whole books devoted to it.

Note

The diagrams in this chapter have been created using Microsoft Visio, which ships with the Enterprise Architect edition of VS. There is also a class viewer in VS that is a powerful tool in its own right. You look at that in Chapter 9.

Figure 8-1 shows a UML representation of your printer class, called Printer.

image from book
Figure 8-1

The class name is shown in the top section of this box (you learn about the bottom two sections a little later).

Figure 8-2 shows a UML representation of an instance of this Printer class called myPrinter.

image from book
Figure 8-2

Here, the instance name is shown first in the top section, followed by the name of its class. These two names are separated by a colon.

Properties and Fields

Properties and fields provide access to the data contained in an object. This object data is what differentiates separate objects, because it is possible for different objects of the same class to have different values stored in properties and fields.

At this point it is worth introducing another term — the various pieces of data contained in an object together make up the state of that object.

Imagine an object class that represents a cup of coffee, called CupOfCoffee. When you instantiate this class (that is, you create an object of this class), you must provide it with a state for it to be meaningful. Here, you might use properties and fields to enable code using this object to set the type of coffee used, whether the coffee contains milk or sugar, whether the coffee is instant, and so on. A given coffee cup object would then have a given state, such as "Columbian filter coffee with milk and two sugars."

Both fields and properties are typed, so you can store information in them as string variables, as int variables, and so on. However, properties differ from fields in that they don't provide direct access to data. Objects are capable of shielding users from the nitty-gritty details of their data, which needn't be represented on a 1-to-1 basis in the properties that exist. If you used a field for the number of sugars in a CupOfCoffee instance then users could place whatever value they liked in the field, limited only by the limits of the type used to store this information. If, for example, you used an int to store this data then users could use any value between –2147483648 and 2147483647, as you saw in Chapter 3. Obviously, not all values here make sense, particularly the negative ones — and some of the large positive amounts might require an inordinately large cup. However, if you used a property for this information, then you could limit this value to, say, a number between 0 and 2.

In general, it is better to provide properties rather than fields for state access, because you have more control over what goes on. This choice doesn't affect code that uses object instances, because the syntax for using properties and fields is the same.

Read/write access to properties may also be clearly defined by an object. Certain properties may be read-only, allowing you to see what they are but not change them (at least not directly). This is often a useful technique for reading several pieces of state simultaneously. You might have a read-only property of the CupOfCoffee class called Description, returning a string representing the state of an instance of this class (such as the string given earlier) when requested. You might be able to assemble the same data by interrogating several properties, but a property such as this one may save you time and effort. You might also have write-only properties operating in a similar way.

As well as this read/write access for properties, it is also possible to specify a different sort of access permission for both fields and properties, known as accessibility. Accessibility determines what code can access these members, that is, whether they are available to all code (public), are available only to code within the class (private), or use a more complex scheme (this is covered in more detail later on in the chapter, when it becomes pertinent). One very common practice is to make fields private and provide access to them via public properties. This means that code within the class can have direct access to the data stored in the field, while the public property shields external users from this data and prevents them from placing invalid content here. Public members are said to be exposed by the class.

One way of visualizing this is to equate it with variable scope. Private fields and properties, for example, can be thought of as local to the object that possesses them, whereas the scope of public fields and properties also encompasses code external to the object.

In the UML representation of a class, you use the second section to display properties and fields, as shown in Figure 8-3.

image from book
Figure 8-3

This is a representation of the CupOfCoffee class, with five members (properties or fields, because no distinction is made in UML) defined as discussed earlier. Each of the entries contains the following information:

  • Accessibility: A + symbol is used for a public member, a - symbol is used for a private member. In general, though, I won't show private members in the diagrams in this chapter, because this information is internal to the class. No information is provided as to read/write access.

  • The member name.

  • The type of the member.

A colon is used to separate the member names and types.

Methods

Method is the term used to refer to functions exposed by objects. These may be called in the same way as any other function and may use return values and parameters in the same way — you looked at functions in detail in Chapter 6.

Methods are used to give access to the functionality of objects. Like fields and properties, they can be public or private, restricting access to external code as necessary. They will often make use of an objects' state to affect their operation, and have access to private members such as private fields if required. For example, the CupOfCoffee class might define a method called AddSugar(), which would provide a more readable syntax for incrementing the sugar property than setting the corresponding Sugar property.

In UML, class boxes show methods in the third section, as shown in Figure 8-4.

image from book
Figure 8-4

The syntax here is similar to that for fields and properties, except that the type shown at the end is the return type and method parameters are shown. Each parameter is displayed in UML with one of the following identifiers: in, out, or inout. These are used to signify the direction of data flow, where out and inout roughly correspond to the use of the C# keywords out and ref described in Chapter 6. in roughly corresponds to the default C# behavior, where neither the out nor ref keyword is used.

Everything's an Object

At this point it's time for me to come clean — you have been using objects, properties, and methods throughout this book. In fact, everything in C# and the .NET Framework is an object! The the Main() function in a console application is a method of a class. Every variable type you've looked at is a class. Every command you have used has been a property or a method, such as <String>.Length, <String>.ToUpper(), and so on. The period character here separates the object instance's name from the property or method's name.

Objects really are everywhere, and the syntax to use them is often very simple. It has certainly been simple enough for you to concentrate on some of the more fundamental aspects of C# up until now.

From here on in, you start to look at objects in more detail. Bear in mind that the concepts introduced here have far-reaching consequences — applying even to that simple little int variable you've been happily playing around with.

The Lifecycle of an Object

Every object has a clearly defined lifecycle. Apart from the normal state of "being in use," this lifecycle includes two important stages:

  • Construction: When an object is first instantiated it needs to be initialized. This initialization is known as construction and is carried out by a constructor function.

  • Destruction: When an object is destroyed, there will often be some clean up tasks to perform, such as freeing up memory. This is the job of a destructor function.

Constructors

Basic initialization of an object is automatic. For example, you don't have to worry about finding the memory to fit a new object into. However, there are times where you will want to perform additional tasks during an object's initialization stage, such as initializing the data stored by an object. A constructor function is what you use to do this.

All objects have a default constructor, which is a parameterless method with the same name as the class itself. In addition, a class definition might include several constructor methods with parameters, known as nondefault constructors. These enable code that instantiates an object to do so in many ways, perhaps providing initial values for data stored in the object.

In C#, constructors are called using the new keyword. For example, you could instantiate a CupOfCoffee object using its default constructor in the following way:

 CupOfCoffee myCup = new CupOfCoffee(); 

Objects may also be instantiated using nondefault constructors. For example, the CupOfCoffee class might have a nondefault constructor that uses a parameter to set the bean type at instantiation:

 CupOfCoffee myCup = new CupOfCoffee("Blue Mountain"); 

Constructors, like fields, properties, and methods, may be public or private. Code external to a class can't instantiate an object using a private constructor; it must use a public constructor. In this way, you can, for example, force users of your classes to use a nondefault constructor (by making the default constructor private).

Some classes have no public constructors, meaning that it is impossible for external code to instantiate them. However, this doesn't make them completely useless, as you will see shortly.

Destructors

Destructors are used by the .NET Framework to clean up after objects. In general, you don't have to provide code for a destructor method; instead, the default operation works for you. However, you can provide specific instructions if anything important needs to be done before the object instance is deleted.

When a variable goes out of scope, for example, it may not be accessible from your code, but it may still exist somewhere in your computer's memory. It is only when the .NET runtime performs its garbage collection clean up that the instance is completely destroyed.

Note

This means that you shouldn't rely on the destructor to free up resources that are used by an object instance, as this may be a long time after the object is of no further use to you. If the resources in use are critical this can cause problems. However, there is a solution to this — as you see in the "Disposable Objects" section later in this chapter.

Static and Instance Class Members

As well as having members such as properties, methods, and fields that are specific to object instances, it is also possible to have static (also known as shared, particularly to our Visual Basic brethren) members, which may be methods, properties, or fields. Static members are shared between instances of a class, so they can be thought of as global for objects of a given class. Static properties and fields allow you access to data that is independent of any object instances, and static methods allow you to execute commands related to the class type but not specific to object instances. When using static members, in fact, you don't even need to instantiate an object.

For example, the Console.WriteLine() and Convert.ToString() methods you have been using are static. At no point do you need to instantiate the Console or Convert classes (indeed, if you try it you'll find that you can't, as the constructors of these classes aren't publicly accessible, as discussed earlier).

There are many situations such as these where static properties and methods can be used to good effect. For example, you might use a static property to keep track of how many instances of a class have been created.

In UML syntax, static members of classes are shown underlined, as shown in Figure 8-5.

image from book
Figure 8-5

Static Classes

Often, you will want to use classes that only contain static members and cannot be used to instantiate objects (such as Console). A shorthand way of doing this, rather than making the constructors of the class private, is to use a static class. A static class can only contain static members and needs no constructor definition, since by implication it can never be instantiated.




Beginning Visual C# 2005
Beginning Visual C#supAND#174;/sup 2005
ISBN: B000N7ETVG
EAN: N/A
Year: 2005
Pages: 278

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net