1

所以我想设计这样的团队/球员关系:每个球员都属于一个球队,但因为我想用界面练习,所以我制作了 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) 

在 BasketballPlayer 而不是这个

public void AddPlayer(IAthlete player) 

我认为它应该可以工作,因为BasketballPlayer 是一名运动员。如果我将其更改为 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 中。它应该是这样的,我应该小心不要那样做吗?我究竟做错了什么?如何使用类图显示这一点?这会导致松耦合吗?

4

5 回答 5

3

您试图违反Liskov 替换原则

任何可以用超类型完成的事情——比如添加一个HockeyPlayer——也可以用一个子类型完成——包括BasketballTeam.

相反,您应该使用泛型:

class Team<TPlayer> where TPlayer : IAthlete {
    public ReadOnlyCollection<TPlayer> Players { get; }
    public string Name { get; }
    public void AddPlayer(TPlayer player);
}
于 2012-04-09T11:39:58.983 回答
2

这是对您的代码的一些想法。首先,在 C# 中,您可以使用属性,而不是 Get 和 Set 方法。

public interface IAthlete
{
    string Name { get; }
    string Sport { get; }
}

使用自动属性,您可以要求编译器为属性生成后台存储。还可以考虑创建基类 Player,它将包含 Name 和 Sport 属性的实现。

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

现在关于团队。如果您不想在您的 BasketballTeam 中包含 FootballPlayers,那么您应该创建参数化团队。还可以考虑使用 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);
        }
    }
}
于 2012-04-09T12:17:54.763 回答
1

逻辑上,

这些应该是您的基类: Team , Player

这些应该是您的派生类:BasketballTeam、BasketballPalyer

这些应该是 Player 上的接口: IPlay() , IRun , IGetName 等。无论哪个适用

等等...

指导原则:动词更适合接口,名词更适合类。要求中的名词最适合代码中的类。

于 2012-04-09T13:27:24.443 回答
0

如果您的界面旨在供各种游戏使用,那么您似乎在这里错过了 Game并且可能需要使用Generics

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

}

这将防止曲棍球运动员加入篮球队。

于 2012-04-09T11:46:05.453 回答
0

斯拉克斯是正确的。您可以向您添加一个通用约束,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; 
    // }

    // […]
}
于 2012-04-09T11:49:24.287 回答