Adam
Adam

Reputation: 61

Method overrides and polymorphysm

I am trying to create a program that allows the user to check into a hotel room. The program should check if the room is free and then allocate one of the free rooms if any are available. I have multiple rooms types such as a single room, a double room, a twin room and so on which all need to inherit from the base class Room.

Here is my code currently.

public class Room 
{
    public static bool[] av = { false, true, false };

    public bool availability()
    {
        bool a = false;
        foreach (var roomAv in av)
        {
            a = a || roomAv;
        }
        return a;
    }

    public bool availability(int room)
    {
        return av[room];    
    }

    public int allocate()
    {
        if (availability())
        {
            int room = 0;
            while (!av[room])
            {
                room++;
            }
            av[room] = false;
            return room;
        }
        else
        {
            return -1;
        }
    }

    public static void roomStatus()
    {
        for (int i = 0; i < av.Length - 1; i++)
        {
            Console.WriteLine(i + av[i].ToString());
        }

    }
}

class SingleRoom
{

}

The functions I have defined in the room class need to be usable by all the different room types but each hold their own separate array stating whether they are available or not. How can I do this? How can I access those functions for each class but on there own separate array instead of performing them just on the 'av' array like I have currently.

Upvotes: 6

Views: 180

Answers (4)

code_dredd
code_dredd

Reputation: 6085

The functions i have defined in the room class need to be usable by all the different room types but each hold their own separate array stating whether they are available or not. how can i do this?

When you use the static keyword for a variable, you're saying the variable belongs to the type itself instead of the object instance. Quoting from MSDN Docs:

Use the static modifier to declare a static member, which belongs to the type itself rather than to a specific object.

In other words, your array is "owned" by the class Room, not by individual objects of type Room created with new. If you want each object instance to own its own private member variables, then the static keyword needs to be removed. I.e.

public static bool[] av = { false, true, false };

should be:

public bool[] av = { false, true, false };

Note that the same applies to method names, i.e., if you use the static keyword on a method, the method is "owned" by the class/type itself, and not the individual object instances. This means, your roomStatus method must be used as Room.roomStatus() and trying new Room().roomStatus() would not be possible.

I'd actually recommend that you remove the array and turn this into a property, so that you can simply do something like:

Room r = new SingleRoom();
if(r.IsAvailable)
{
    // ...
}

You should also refactor your code to follow .NET naming conventions for your methods, variable names, and make better use of object-orientation. I think Niraj Doshi's post is a good step in that direction.

Since you're new to C#, I'd recommend you get a hold of the book Effective C# by B. Wagner.

Update - Refactored Code

This is my take on refactoring the code, having a RoomManager, a IRoom interface, an abstract implementation of the IRoom interface called Room with code and functionality common to all rooms, a concrete SingleRoom for a more specific type, and a TextView class to manage how the data will be presented/displayed to the user (i.e. text-based output).

Notice that this following the Model-View-Controller (MVC) design pattern, with the Room classes being the models (i.e. data), the TextView being responsible for displaying the data (i.e. presentation), and the Main program itself being the controller (i.e. coordinating the other two).

Main Program

The program simply adds some rooms and then displays information for each of them, based on the manager's capacity.

using System;
using System.Collections.Generic;

namespace HotelRoomManager
{
    class MainClass
    {
        public static void Main (string[] args)
        {
            RoomManager mgr = new RoomManager (5);
            for (uint i = 0; i < mgr.Capacity; ++i)
                mgr.AddRoom (new SingleRoom (1, i + 1) );

            List<IRoom> rooms = mgr.GetAllRooms ();
            TextView view = new TextView ();
            view.RenderHeader ();
            view.RenderModels (rooms);

            mgr.RemoveAllRooms ();
        }
    }
}

IRoom Interface

The interface defines a type and is the basis for all the rooms. Interfaces are used to define contracts with clients, and does not rely on implementation details, which makes it a good object-oriented practice.

using System;

namespace HotelRoomManager
{
    public enum BedType
    {
        Single,
        Double,
        Twin,
        Queen,
        King
    }

    public interface IRoom
    {
        BedType BedType { get; }
        uint Floor { get; }
        uint Number { get; }
        bool IsOccupied { get; set; }
    }
}

Abstract Room

The room simply contains code that is common to all rooms, regardless of their own individual details.

using System;

namespace HotelRoomManager
{
    public abstract class Room : IRoom
    {
        private uint floor;
        private uint number;
        private bool occupied;

        public Room (uint floor, uint number)
        {
            this.floor = floor;
            this.number = number;
            occupied = false;
        }

        public uint Floor {
            get { return floor; }
        }

        public uint Number {
            get { return number; }
        }

        public abstract BedType BedType { get; }

        public bool IsOccupied {
            get { return occupied; }
            set { occupied = value; }
        }

        override public string ToString() {
            return "Room(floor=" + floor + ", number=" + number + ")";
        }
    }
}

Concrete SingleRoom

By this point, this room only needs to report its actual type. It doesn't need to do anything special in addition to the common functionality already available.

using System;

namespace HotelRoomManager
{
    public sealed class SingleRoom : Room
    {
        public SingleRoom (uint floor, uint number) : base(floor, number)
        {}

        override public BedType BedType {
            get { return BedType.Single; }
        }
    }
}

The RoomManager

The manager simply helps to keep track of all the rooms and provides a simplified interface to interact with the collection.

using System;
using System.Collections.Generic;

namespace HotelRoomManager
{
    public class RoomManager
    {
        private List<IRoom> rooms;

        public RoomManager (uint capacity) {
            rooms = new List<IRoom> ();
            rooms.Capacity = (int) capacity;
        }

        public void AddRoom(IRoom room) {
            rooms.Add (room);
        }

        public void RemoveRoom(IRoom room) {
            rooms.Remove (room);
        }

        public List<IRoom> GetAllRooms() {
            return rooms;
        }

        public void RemoveAllRooms() {
            rooms.Clear ();
        }

        public uint Capacity {
            get { return (uint) rooms.Capacity; }
        }

    }
}

The TextView

The sole responsibility of the view is to decide how the data from the models will be presented to the user. This decouples the data itself from how the data is displayed, making your system easier to maintain and expand. You can also have multiple views available instead of having to choose between one or the other.

using System;
using System.Collections.Generic;
using System.Text;

namespace HotelRoomManager
{
    public class TextView
    {
        public TextView () {}

        public void RenderHeader() {
            Console.WriteLine ("Hotel Management System");
            Console.WriteLine ("-----------------------");
        }

        public void RenderModels(List<IRoom> rooms) {
            StringBuilder sb = new StringBuilder ();
            foreach (IRoom r in rooms) {
                sb.Append ("Floor   : " + r.Floor + "\n");
                sb.Append ("Number  : " + r.Number + "\n");
                sb.Append ("Bed     : " + r.BedType + "\n");
                sb.Append ("Occupied: " + (r.IsOccupied ? "Yes" : "No") + "\n\n");
            }
            Console.WriteLine (sb.ToString ());
        }
    }
}

Output

A quick run of the program will produce the following output:

Hotel Management System
-----------------------
Floor   : 1
Number  : 1
Bed     : Single
Occupied: No

Floor   : 1
Number  : 2
Bed     : Single
Occupied: No

Floor   : 1
Number  : 3
Bed     : Single
Occupied: No

Floor   : 1
Number  : 4
Bed     : Single
Occupied: No

Floor   : 1
Number  : 5
Bed     : Single
Occupied: No

Upvotes: 2

Marshal
Marshal

Reputation: 6651

As you said you are new to C#, I would suggest to rethink on structure. You are in object-oriented paradigm. What you are thinking is plain old C-function oriented programming.

public class Room
{
    public bool IsAvailable {get; set;}
    public RoomType RoomType {get; set;}
    public int RoomNo {get; set;}
    public int Floor {get; set;}
    public string RoomName {get; set;}
}

public enum RoomType
{
     Single,
     Double,
     Twin,
     King,
     HoneymoonSuite
}

public class RoomManager
{
   public List<Room> AllRooms {get; set;}

   public RoomManager()
   {
       AllRooms = new List<Room>();

       AllRooms.Add(new Room(){ RoomType=RoomType.Single, 
                                RoomNo=1, 
                                Floor=1, 
                                RoomName="A101", 
                                IsAvailable=true});

       AllRooms.Add(new Room(){ RoomType=RoomType.Double, 
                                RoomNo=2, 
                                Floor=1, 
                                RoomName="A102", 
                                IsAvailable=false});

       AllRooms.Add(new Room(){ RoomType=RoomType.HoneyMoonSuite, 
                                RoomNo=1, 
                                Floor=2, 
                                RoomName="A201", 
                                IsAvailable=true});

      }

      public bool IsAvailable(int roomNo)
      {
          //You need to check if roomNo is a valid RoomNo
          return AllRooms.Any(r=>r.RoomNo==roomNo && r.IsAvailable);
      } 

      public bool IsAvailable(string roomName)
      {
          //You need to check if roomName is valid RoomName
          return AllRooms.Any(r=>r.RoomName==roomName && r.IsAvailable);
      }  

}

Upvotes: 2

JNF
JNF

Reputation: 3730

You put the array as static, meaning that all access to the array reaches the same object.

Remove that and each will have their own.

As per comments - the static identifier should be removed from the roomStatus method as well.

Upvotes: 0

Ron Beyer
Ron Beyer

Reputation: 11273

This is pretty simple, instead of a field, use a property:

public class Room 
{
    public virtual bool[] av { get; set; } = { false, true, false };

    //All of your functions remain unchanged except you need to remove static
}

Then in your derived classes:

public class SingleRoom : Room
{
    public override bool[] av { get; set; } = { true, true, false };
}

The inherited rooms will set the array which will be used in the base functions for availability so you only have to write it once.

This is another plus of properties over fields, where you can set a property such that it can be inherited. The only thing that was really "wrong" with your original code was that the array and some methods were declared static meaning that it was the same across all instances of the class. Availability should be an instance-level field/property, not a type-level one.

Your original code could work if you remove the static and made your derived classes like this:

public class SingleRoom
{
    public SingleRoom
        : base()
    {
        //Redefine the values of the array.
        av = { true, true, false };
    }
}

Upvotes: 1

Related Questions