方法重写和多形体瘤
本文关键字:重写 方法 | 更新日期: 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
接口、一个名为Room
的IRoom
接口的抽象实现,具有所有房间通用的代码和功能,一个用于更特定类型的具体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
方法中删除。