1

したがって、チームとプレーヤーの関係を次のように設計したいと思います。すべてのプレーヤーは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なので、うまくいくはずだと思いました。そして、それを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

リスコフの置換原則に違反しようとしています。

スーパータイプで実行できること(aの追加など)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; }
}

自動プロパティを使用すると、プロパティのバックストアを生成するようにコンパイラに要求できます。NameプロパティとSportプロパティの実装を保持する基本クラス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;
    }
}

今チームについて。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

論理的には、

これらはあなたの基本クラスでなければなりません:チーム、プレーヤー

これらは派生クラスである必要があります:BasketballTeam、BasketballPalyer

これらはPlayerのインターフェースである必要があります:IPlay()、IRun、IGetNameなど。

等々...

ガイドライン:動詞はインターフェースに適し、名詞はクラスに適しています。要件の名詞は、コードのクラスに最適です。

于 2012-04-09T13:27:24.443 に答える
0

インターフェイスがさまざまなゲームで使用されることを意図している場合は、ここでゲームが欠落しているようで、おそらく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

SLaksは正しいです。ITeamすべてのプレーヤーを受け入れるのではなく、1つのタイプのプレーヤーだけを受け入れるように、一般的な制約を追加できます。

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 に答える