Tải bản đầy đủ - 0 (trang)
Chapter 4. Object-Oriented Programming in C #

Chapter 4. Object-Oriented Programming in C #

Tải bản đầy đủ - 0trang

Review of Object-Oriented Concepts

In this preliminary section we review the fundamentals of object-oriented

programming. If you are an experienced C++ or Java programmer, you may skim

through this section as a refresher and begin your careful reading with the next

section, where we introduce the case study.



Objects

Objects have both a real-world and a software meaning. The object model

describes a relationship between them.

Objects in the Real World

The term object has an intuitive real-world meaning. There are concrete, tangible

objects, such as a ball, an automobile, and an airplane. There are also more

abstract objects that have a definite intellectual meaning, such as a committee, a

patent, or an insurance contract.

Objects have both attributes (or characteristics) and operations that can be

performed upon them. A ball has a size, a weight, a color, and so on. Operations

may be performed on the ball, such as throw, catch, and drop.

There can be various types of relationships among classes of objects. One, for

example, is a specialization relationship, such as an automobile is a special kind

of vehicle. Another is a whole/part relationship, such as an automobile consists of

an engine, a chassis, wheels, and other parts.

Object Models

Objects can also be used in programs. Objects are useful in programming because

you can set up a software model of a real-world system. Software objects abstract

the parts of objects in the real world that are relevant to the problem being solved.

The model can then be implemented as software using a programming language.

A software system implemented in this way tends to be more faithful to the real

system, and it can be changed more readily when the real system is changed.

There are formal languages for describing object models. The most popular

language is UML (Unified Modeling Language), which is a synthesis of several

earlier modeling languages. Formal modeling languages are beyond the scope of

this book, but we will find that informal models are useful.

Reusable Software Components



Another advantage of objects in software is that they can facilitate reusable

software components. Hardware has long enjoyed significant benefits from

reusable hardware components. For example, computers can be created from

power supplies, printed circuit boards, and other components. Printed circuit

boards in turn can be created from chips. The same chip can be reused in many

different computers, and new hardware designs do not have to be done from

scratch.

With appropriate software technology, similar reuse is feasible in software

systems. Objects provide the foundation for software reuse.

Objects in Software

An object is a software entity containing data (state) and related functions

(behavior) as a self-contained module. For example, a HotelBroker may contain

a list of hotels (the state) and provide operations to add a hotel and make a

reservation (behavior).

Abstraction

An abstraction captures the essential features of a real-world object, suppressing

unnecessary details. All instances of an abstraction share these common features.

Abstraction helps us deal with complexity. For example, consider the problem of

booking a reservation. There are many different kinds of things you might want to

reserve, such as a hotel, an airplane flight, or a conference room. Such

"reservables" have many differences, but they have certain essentials in common.,

such as a capacity.

Encapsulation

The implementation of an abstraction should be hidden from the rest of the

system, or encapsulated. For example, the list of hotels may be contained in

several different kinds of data structures, such as an array, a collection, or a

database. The rest of the system should not need to know the details of the

representation.



Classes

A class groups all objects with common behavior and common structure. A class

allows creation of new objects of the same type. An object is an instance of some

class. We refer to the process of creating an individual object as instantiation.

Classes can be related in various ways, such as by inheritance and by



containment.

Inheritance

Inheritance is a key feature of the object-oriented programming paradigm. You

abstract out common features of your classes and put them in a highlevel base

class. You can add or change features in more specialized derived classes, which

"inherit" the standard behavior from the base class. Inheritance facilitates code

reuse and extensibility.

Consider Reservable as a base class, with derived classes Hotel and Flight. All

reservables share some characteristics, such as a capacity. Different kinds of

reservables differ in other respects. For example, a hotel has a city and a name,

while a flight has an origin and a destination. Figure 4-1 illustrates the

relationship among these different kinds of reservables.

Figure 4-1. Inheritance relationship among different reservable

classes.



Abstract Classes

Sometimes a class is not meant to be instantiated, but only to provide a template

for derived classes. The Reservable class is an example—it is too abstract to

actually instantiate. Only specific kinds of reservable classes, such as Hotel and

Flight, may actually be instantiated. We call a class such as Reservable that

cannot be instantiated an abstract class. A class that can be instantiated is called a

concrete class.

Relationships Among Classes



Classes may be related to each other in various ways.













The inheritance (IS-A) relationship specifies how one class is a special

case of another class. A Hotel (subclass or derived class) is a special kind

of Reservable (superclass or base class).

The composition (HAS-A) relationship specifies how one class (the

whole) is made up of other classes (the parts). A HotelBroker (whole) has

a list of Hotel objects.

A weaker kind of relationship (USES-A) can be identified when one class

merely makes use of some other class when carrying out its

responsibilities.



Polymorphism

Consider the problem of generating a payroll for various categories of employees.

Different kinds of employees may have pay calculated in a different manner. A

salaried employee receives a fixed salary. A wage employee is paid according to

the number of hours worked. A sales employee is paid according to the

commissions earned on sales that were made.

A traditional approach is to maintain a type field in an employee structure and to

perform processing in a switch statement, with cases for each type. Such use of

switch statements is error prone and requires much maintenance when adding a

new employee type.

An alternative is to localize the intelligence to calculate pay in each employee

class, which will support its own GetPay method. Generic payroll code can then

be written that will handle different types of employees and will not have to be

modified to support an additional employee type. Provide a GetPay method in

the base class and an override of this method in each derived class. Call GetPay

through an object reference to a general Employee object. Depending on the

actual employee class referred to, the appropriate GetPay method will be called.

The ability for the same method call to result in different behavior depending on

the object through which the method is invoked is referred to as polymorphism.

Polymorphism can greatly simplify complex systems and is an important part of

the object-oriented paradigm.

You should not try to coerce your design so that you can take advantage of

polymorphism. We will see in our Acme Travel Agency case study that we have

three different abstract base classes, but we do not need polymorphism to achieve

quite general behavior. On the other hand, the .NET Framework classes use

polymorphism heavily, as we shall see beginning in Chapter 5. Later in this

chapter we will provide a small example of polymorphism using an employee



class hierarchy, as outlined above.



Acme Travel Agency Case Study: Design

The Acme Travel Agency provides various services, including the booking of hotel, plane,

and car rental reservations. We will use this simple theme of booking reservations to

illustrate various features of .NET throughout the book. In this chapter we design the

architecture of a general system for booking different kinds of reservations. We illustrate

the reservation system with an implementation of a hotel broker system that supports the

following basic features:















Add a hotel to the list of hotels

Show all the hotels

Show all the hotels in a particular city

Reserve a hotel room for a range of dates

Show all the reservations

Show all the reservations for a particular customer



The system also maintains a list of customers. Customers may register by giving their

name and email address, and they will be assigned a customer ID. The following features

are supported in the basic customer management subsystem:









Register as a customer

Change the email address of a customer

Show a single customer or all the customers



In this chapter various lists, such as hotels, reservations, and customers, will be maintained

as arrays. In the next chapter we will use .NET collections in place of arrays, and we will

implement more features, such as the ability to delete a hotel, cancel a reservation, and the

like. In later chapters we will extend the case study in various ways, such as providing a

graphical user interface, storing all data in a database, deploying as a Web application, and

so on.

The code for our case study is in the CaseStudy folder for this chapter.



Designing the Abstractions

Bearing in mind that eventually we want to implement not only a hotel reservation system,

but also a system for other kinds of reservations, including plane and car rental, it behooves

us at the beginning to look for appropriate abstractions. The more functionality we are able

to put in base classes, the less work we will have to do in order to implement a particular

kind of reservation system. On the other hand, having more functionality in the base

classes can reduce the range of problems to which they are applicable. Good design is a

balancing act.

Another attribute of good abstractions is that they will survive major changes in

implementation. As we shall see later in this book, our C# abstractions of the hotel

reservation system remain intact as we implement the system on an SQL Server database.



These abstractions will be represented in C# by abstract classes, defined in the file

Broker.cs in the CaseStudy folder for this chapter.

Reservable

Our first abstraction is the thing we are looking to reserve. We will denote this abstraction

as simply Reservable. The basic issue in reservations is resource usage. There are a

limited number of reservable resources. Hence the key attribute of a Reservable is

capacity. For example, a hotel may have 100 rooms. A flight may have 250 seats. We will

also want a unique identifier for a Reservable, which we will denote by unitid. (The

shorter name unitid is used in preference to the longer, more awkward name reservableid.

Later we will see other uses of the terminology "unit." For example, the method to add a

reservable is called AddUnit.)

For our applications we are going to introduce an additional attribute, cost. There is a room

rate for a hotel, a ticket cost for a flight, and so on. Note that this attribute may not be

applicable to all things that are being reserved. For example, a conference room within a

company may not have a cost assigned to it. However, our applications are for commercial

customers, so we choose to include cost in our model.



Simplifications

Because our case study is designed to illustrate concepts in C# and .NET, we

will choose many simplifications in our design, so that we do not become

bogged down in too detailed coding. For example, in real life a hotel has several

different kinds of rooms, each having a different rate. Similarly, an airplane

flight will have different classes of seats. Here the situation in real life is even

more complicated, because the price of a seat may vary wildly depending on

when the reservation was made, travel restrictions, and so on. To make life

simple for us, we are assuming that each instance of a particular reservable will

have the same cost.

In C# we will represent a Reservable by an abstract class.



public abstract class Reservable

{

static private int nextid = 0;

protected int unitid;

internal protected int capacity;

internal protected decimal cost;

public Reservable(int capacity, decimal cost)

{

this.capacity = capacity;

this.cost = cost;



unitid = nextid++;

}

}

A constructor allows us to specify the capacity and cost when the object is created. The

unitid is autogenerated by a static variable. This id starts out at 0, because it is also going

to be used in our implementation as an index in a two-dimensional array to track the

number of customers having a reservation at a given reservable on a given date.

We will discuss the role of the private, internal, and protected access control specifiers

later.

Reservation

When a customer books a reservation of a reservable, a record of the reservation will be

made. The Reservation class holds the information that will be stored.



public abstract class Reservation

{

public int ReservationId;

public int UnitId;

public DateTime Date;

public int NumberDays;

static private int nextReservationId = 1;

public Reservation()

{

ReservationId = nextReservationId++;

}

}

The ReservationId is autogenerated. The UnitId identifies the reservable that was booked.

Date is the starting date of the reservation, and NumberDays specifies the number of days

for which the reservation was made.

Broker

Our third abstraction, Broker, models a broker of any kind of reservable, and is also

represented by an abstract class. It maintains a list of reservables, represented by the array

units, and a list of reservations, represented by the array reservations. The twodimensional array numCust keeps track of the number of customers having a reservation

at a given reservable on a given day.



public abstract class Broker

{

private int MaxDay;



private const int MAXRESERVATION = 10;

private static int nextReservation = 0;

private static int nextUnit = 0;

private int[,] numCust;

protected Reservation[] reservations;

protected Reservable[] units;

public Broker(int MaxDay, int MaxUnit)

{

this.MaxDay = MaxDay;

numCust = new int[MaxDay, MaxUnit];

units = new Reservable[MaxUnit];

reservations = new Reservation[MAXRESERVATION];

}

...

ReservationResult

A simple structure is used for returning the result from making a reservation.



public struct ReservationResult

{

public int ReservationId;

public decimal ReservationCost;

public decimal Rate;

public string Comment;

}

The Rate is the cost for one day, and ReservationCost is the total cost, which is equal to

the number of days multiplied by the cost for one day. The ReservationId is returned as -1

if there was a problem, and an explanation of the problem is provided in the Comment

field. This structure is created so that result information can be passed in distributed

scenarios, such as Web Services, where you cannot throw exceptions.



Base Class Logic

The base class Broker not only represents the abstraction of a broker of any kind of

reservable. It also contains general logic for booking reservations and maintaining a list of

reservations. Our ability to capture this logic abstractly gives the power to this base class

and will make implementing reservations in a derived class relatively simple.

Reserve

The core method of the Broker class is Reserve.



protected ReservationResult Reserve(Reservation res)



{

int unitid = res.UnitId;

DateTime dt = res.Date;

int numDays = res.NumberDays;

ReservationResult result = new ReservationResult();

// Check if dates are within supported range

int day = dt.DayOfYear - 1;

if (day + numDays > MaxDay)

{

result.ReservationId = -1;

result.Comment = "Dates out of range";

return result;

}

// Check if rooms are available for all dates

for (int i = day; i < day + numDays; i++)

{

if (numCust[i, unitid] >= units[unitid].capacity)

{

result.ReservationId = -1;

result.Comment = "Room not available";

return result;

}

}

// Reserve a room for requested dates

for (int i = day; i < day + numDays; i++)

numCust[i, unitid] += 1;

// Add reservation to reservation list and

// return result

AddReservation(res);

result.ReservationId = res.ReservationId;

result.ReservationCost = units[unitid].cost * numDays;

result.Rate = units[unitid].cost;

result.Comment = "OK";

return result;

}

The Reserve method is designed to implement booking several different kinds of

reservations. Thus the Reservation object, which will be stored in the list of reservations,

is created in a more specialized class derived from Broker and is passed as a parameter to

Reserve. For example, a HotelBroker will book a HotelReservation, and so on. The

UnitId, Date, and NumberDays fields are extracted from the Reservation object, and a

ReservationResult object is created to be returned.



protected ReservationResult Reserve(Reservation res)

{



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Chapter 4. Object-Oriented Programming in C #

Tải bản đầy đủ ngay(0 tr)

×