方法重写和多形体瘤

本文关键字:重写 方法 | 更新日期: 2023-09-27 18:24:10

我正在尝试创建一个程序,允许用户入住酒店房间。程序应检查房间是否空闲,然后分配其中一个空闲房间(如果有的话)。我有多个房间类型,如单间、双人间、双床间等等,它们都需要从基类Room继承。

这是我当前的代码。

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
{
}

我在房间类中定义的功能需要可供所有不同的房间类型使用,但每个房间都有自己的单独数组,说明它们是否可用。我该怎么做?我如何访问每个类的这些函数,但在它们自己的单独数组上,而不是像目前那样只在"av"数组上执行它们。

方法重写和多形体瘤

正如您所说的您是C#的新手,我建议您重新思考结构。您处于面向对象的范例中。您所想的是简单的、老式的面向C函数的编程。

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);
      }  
}

我在房间类中定义的功能需要由所有不同的房间类型,但每个房间都有自己的独立阵列说明它们是否可用。我该怎么做?

对变量使用static关键字时,表示该变量属于类型本身,而不是对象实例。引用MSDN文档:

使用static修饰符声明静态成员,该成员属于类型本身,而不是特定对象。

换句话说,数组由类Room"拥有",而不是由使用new创建的Room类型的单个对象"拥有"。如果希望每个对象实例都拥有自己的私有成员变量,则需要删除static关键字。即

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

应该是:

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

请注意,这同样适用于方法名称,即,如果在方法上使用static关键字,则该方法由类/类型本身"拥有",而不是由单个对象实例"拥有"。这意味着,您的roomStatus方法必须用作Room.roomStatus(),并且尝试new Room().roomStatus()是不可能的。

实际上,我建议您删除数组并将其转换为属性,这样您就可以简单地执行以下操作:

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

您还应该重构代码,以遵循方法、变量名的.NET命名约定,并更好地使用面向对象的方法。我认为Niraj Doshi的职位是朝着这个方向迈出的一大步。

既然你是C#的新手,我建议你看看B.Wagner的《有效的C#》一书。

更新-重构代码

这是我对代码重构的看法,它有一个RoomManager、一个IRoom接口、一个名为RoomIRoom接口的抽象实现,具有所有房间通用的代码和功能,一个用于更特定类型的具体SingleRoom,以及一个用于管理如何向用户呈现/显示数据(即基于文本的输出)的TextView类。

请注意,这遵循模型-视图-控制器(MVC)设计模式,Room类是模型(即数据),TextView负责显示数据(即表示),主程序本身是控制器(即协调其他两个)。

Program

该程序只需添加一些房间,然后根据经理的能力显示每个房间的信息。

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接口

界面定义了一种类型,是所有房间的基础。接口用于定义与客户端的契约,并且不依赖于实现细节,这使其成为一种良好的面向对象实践。

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

摘要Room

该文件室只包含所有文件室通用的代码,而不考虑它们各自的详细信息。

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 + ")";
        }
    }
}

混凝土SingleRoom

此时,此房间只需要报告其实际类型。除了现有的通用功能外,它不需要做任何特殊的事情。

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

RoomManager

管理员只需帮助跟踪所有房间,并提供一个简化的界面来与集合交互。

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

TextView

视图的唯一责任是决定如何将模型中的数据呈现给用户。这使数据本身与数据的显示方式解耦,使系统更易于维护和扩展。您也可以使用多个视图,而不必在其中一个视图或另一个视图之间进行选择。

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 ());
        }
    }
}

输出

程序的快速运行将产生以下输出:

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

这很简单,不用字段,而是使用一个属性:

public class Room 
{
    public virtual bool[] av { get; set; } = { false, true, false };
    //All of your functions remain unchanged except you need to remove static
}

然后在派生类中:

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

继承的房间将设置将在基本功能中使用的阵列的可用性,因此您只需编写一次即可。

这是属性相对于字段的另一个优点,您可以在其中设置属性,使其可以继承。原始代码唯一真正"错误"的地方是数组和一些方法被声明为static,这意味着它在类的所有实例中都是相同的。可用性应该是实例级别的字段/属性,而不是类型级别的字段。

如果删除static并使派生类如下所示,则原始代码可以工作:

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

您将数组设置为static,这意味着对数组的所有访问都会到达同一个对象。

去掉它,每个人都会有自己的。

根据注释-static标识符也应从roomStatus方法中删除。