Acme Travel Agency Case Study: Design

for RuBoard

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 two-dimensional 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)  {     int unitid = res.UnitId;     DateTime dt = res.Date;     int numDays = res.NumberDays;     ReservationResult result = new ReservationResult();  ... 

Next we check that all the dates requested for the reservation are within the supported range (which for simplicity we are taking as a single year). We make use of the DateTime structure from the System namespace. We return an error if a date lies out of range.

 // 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;  }  ... 

Now we check that space is available for each date, using the numCust array that tracks how many customers currently have reservations for each day and comparing against the capacity. The first dimension of this two-dimensional array indexes on days, and the second dimension indexes on the unit id. (Note that for simplicity we have given our fields and methods names suitable for our initial application, a HotelBroker .)

 // 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;     }  }  ... 

Next we actually reserve the unit for the requested days, which is implemented by incrementing the customer count in numCust for each day.

 // Reserve a room for requested dates  for (int i = day; i < day + numDays; i++)     numCust[i, unitid] += 1;  ... 

Finally, we add the reservation to the list of reservations and return the result.

 // 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;  } 
Lists of Reservations and Reservables

The Broker class also maintains lists of reservations and reservables. For our simple array implementation we only implement Add methods. In a later version we will provide logic to remove elements from lists.

 private void AddReservation(Reservation res)  {     reservations[nextReservation++] = res;  }  protected void AddUnit(Reservable unit)  {     units[nextUnit++] = unit;  } 

Designing the Encapsulation

In our current implementation of Broker all lists are represented by arrays. Since this implementation may not (and in fact will not) be preserved in later versions, we do not want to expose the arrays themselves or the subscripts that are used for manipulating the arrays. We provide public properties NumberUnits and NumberReservations to provide read-only access to the private variables nextUnit and nextReservation .

 public int NumberUnits  {     get     {        return nextUnit;     }  }  public int NumberReservations  {     get     {        return nextReservation;     }  } 

In our Reservation class the simple fields ReservationId , UnitId , Date , and NumberDays are not likely to undergo a change in representation, so we do not encapsulate them. Later, if necessary, we could change some of these to properties, without breaking client code. For now, and likely forever, we simply use public fields.

 public abstract class Reservation  {     public int ReservationId;     public int UnitId;     public DateTime Date;     public int NumberDays;     ... 
for RuBoard


Application Development Using C# and .NET
Application Development Using C# and .NET
ISBN: 013093383X
EAN: 2147483647
Year: 2001
Pages: 158

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