两个接口和两个类之间的关系
本文关键字:两个 关系 之间 接口 | 更新日期: 2023-09-27 18:30:24
所以我想设计这样的团队/球员关系:每个球员都属于一个团队,但由于我想用界面练习,所以我制作了 ITeam 和 IAthlete,然后制作了 BasketballTeam 和 BasketballPlayer。然后我写了这段代码:
public interface IAthlete
{
string GetName();
string GetSport();
}
public interface ITeam
{
void AddPlayer(IAthlete player);
IAthlete[] GetAthletes();
string GetName();
int GetNumberOfPlayers();
}
public class BasketballPlayer:IAthlete
{
private string name;
public string GetName()
{
return this.name;
}
public string GetSport()
{
return "Basketball";
}
public BasketballPlayer(string name)
{
this.name = name;
}
public void Run(int distance)
{
Console.WriteLine(this.name + " just ran " + distance.ToString() + " meters.");
}
public bool Shoot()
{
Console.WriteLine("Successful shot for " + this.name);
return true;
}
}
public class BasketballTeam: ITeam
{
BasketballPlayer[] players;
int numberOfPlayers;
private string name;
public void AddPlayer(BasketballPlayer player)
{
this.players[this.numberOfPlayers] = player;
this.numberOfPlayers++;
}
public IAthlete[] GetAthletes()
{
return this.players;
}
public string GetName()
{
return this.name;
}
public int GetNumberOfPlayers()
{
return this.numberOfPlayers;
}
public BasketballTeam(string name)
{
this.numberOfPlayers = 0;
this.name = name;
this.players = new BasketballPlayer[10];
}
}
class Program
{
static void Main(string[] args)
{
BasketballTeam bt = new BasketballTeam("MyTeam");
BasketballPlayer bp = new BasketballPlayer("Bob");
bt.AddPlayer(bp);
foreach (BasketballPlayer player in bt.GetAthletes())
{
Console.WriteLine(player.GetName());
}
foreach (IAthlete a in bt.GetAthletes())
{
Console.WriteLine(a.GetName());
}
}
}
但它不会编译,因为我正在使用这个:
public void AddPlayer(BasketballPlayer player)
在篮球运动员中而不是这个
public void AddPlayer(IAthlete player)
我认为它应该有效,因为篮球运动员是一个 IAthlete。如果我将其更改为 IAthlete,那么我可以创建另一个这样的类:
public class HockeyPlayer : IAthlete
{
private string name;
public string GetName()
{
return this.name;
}
public string GetSport()
{
return "Hockey";
}
public HockeyPlayer(string name)
{
this.name = name;
}
public void Run(int distance)
{
Console.WriteLine(this.name + " just ran " + distance.ToString() + " meters.");
}
}
然后在我的主目录中执行此操作:
HockeyPlayer hp = new HockeyPlayer("Henry");
bt.AddPlayer(hp);
这在逻辑上是错误的,因为我正在将曲棍球运动员添加到篮球队。应该是这样的,我应该小心不要那样做吗?我做错了什么?如何使用类图显示这一点?这会导致耦合松动吗?
你试图违反利斯科夫替换原则。
可以使用超类型执行的任何操作(例如添加HockeyPlayer
)也可以使用子类型(包括BasketballTeam
)完成。
相反,您应该使用泛型:
class Team<TPlayer> where TPlayer : IAthlete {
public ReadOnlyCollection<TPlayer> Players { get; }
public string Name { get; }
public void AddPlayer(TPlayer player);
}
以下是对代码的一些想法。首先,在 C# 中,可以使用属性,而不是 Get 和 Set 方法。
public interface IAthlete
{
string Name { get; }
string Sport { get; }
}
使用自动属性,您可以要求编译器为属性生成后台存储。此外,请考虑创建基类 Player,它将保存名称和运动属性的实现。
public class Player : IAthlete
{
public Player(string name, string sport)
{
Name = name;
Sport = sport;
}
public string Name { get; private set; }
public string Sport { get; private set; }
}
现在,在实现某些播放器时,只需将值传递给基类构造函数即可。您的自定义播放器将仅保留特定于它们的功能(无代码重复)。此外,建议使用字符串格式,而不是连接字符串:
public class BasketballPlayer : Player
{
public BasketballPlayer(string name)
: base(name, "Basketball")
{
}
public void Run(int distance)
{
Console.WriteLine("{0} just ran {1} meters.", Name, distance);
}
public bool Shoot()
{
Console.WriteLine("Successful shot for " + Name);
return true;
}
}
现在关于团队。如果你不想在你的篮球队中有足球运动员,那么你应该创建参数化的球队。还可以考虑使用 IEnumerable:
public interface ITeam<TPlayer>
where TPlayer : IAthlete
{
void AddPlayer(TPlayer player);
IEnumerable<TPlayer> Players { get; }
string Name { get; }
int NumberOfPlayers { get; }
}
同样,对于常见功能,您可以创建基类。 请记住,在添加新玩家之前,您应该检查团队中当前有多少玩家。
public class Team<TPlayer> : ITeam<TPlayer>
where TPlayer : IAthlete
{
private readonly List<TPlayer> _players = new List<TPlayer>();
public Team(string name, int teamSize)
{
Name = name;
TeamSize = teamSize;
}
public void AddPlayer(TPlayer player)
{
if (_players.Count == TeamSize)
throw new Exception("Players number exceeded");
_players.Add(player);
}
public string Name { get; private set; }
public int TeamSize { get; private set; }
public IEnumerable<TPlayer> Players
{
get { return _players; }
}
public int NumberOfPlayers
{
get { return _players.Count; }
}
}
自定义团队实施变得非常容易。你只需告诉它将拥有哪种类型的球员,然后传递给基地团队实施团队名称和团队规模。
public class BasketballTeam : Team<BasketballPlayer>
{
public BasketballTeam(string name)
: base(name, 10)
{
}
}
现在,您的程序就像一个魅力:
class Program
{
static void Main(string[] args)
{
BasketballTeam bt = new BasketballTeam("MyTeam");
BasketballPlayer bp = new BasketballPlayer("Bob");
bt.AddPlayer(bp);
foreach (BasketballPlayer player in bt.Players)
{
Console.WriteLine(player.Name);
}
foreach (IAthlete a in bt.Players)
{
Console.WriteLine(a.Name);
}
}
}
逻辑上,
这些应该是你的基类:团队,球员
这些应该是你的派生类:篮球队,篮球帕莱尔
这些应该是播放器上的接口:IPlay(),IRun,IGetName等,以适用者为准。
等等...
指南:动词更适合接口,名词适合类。要求中的名词最适合代码中的类。
SLaks 是正确的。您可以向ITeam
添加一个通用约束,以不接受所有玩家,而只接受一种类型的玩家:
public interface ITeam<T> where T : IAthlete
{
void AddPlayer(T player);
IAthlete[] GetAthletes();
// or: T[] GetAthletes();
string GetName();
int GetNumberOfPlayers();
}
BasketballTeam
实现可能如下所示:
public class BasketballTeam : ITeam<BasketballPlayer>
{
BasketballPlayer[] players;
// […]
public void AddPlayer(BasketballPlayer player)
{
this.players[this.numberOfPlayers] = player;
this.numberOfPlayers++;
}
public IAthlete[] GetAthletes()
{
return this.players;
}
// or:
// public BasketballPlayer[] GetAthletes()
// {
// return this.players;
// }
// […]
}
如果你的界面是供各种游戏使用的,那么这里似乎缺少游戏,也许需要使用泛型:
public interface IGame
{
string Name {get;}
...
}
public class Bastketball : IGame
{
...
}
public interface ITeam<TGame> where TGame: class, IGame
{
void AddPlayer(IPlayr<TGame> player);
...
}
public interface IPlayer<TGame> where TGame: class, IGame
{
...
}
这将防止曲棍球运动员被添加到篮球队。