But imagine a class Author and a class Book. A book does have an author so a book class contains a author object, right?
That's why we sometimes use abstraction. But if you want to keep to SRP, then you will want to keep your classes strictly about the details of their purpose. In your case, they appear to be details about A) the club, and B) the tennis player.
There are many ways you can do what you are doing, including using inheritance, to inherit object values from another class while keeping relative to SRP.
One way you could have wrote it would be as the following code is demonstrated. Create a club object and only use what is required to create a club object, in this example, I will use three properties. Code is commented so you can hover over it in VS and it will tell you what each part is doing :
public class Club
{
public string ClubName { get; set; }
public string ClubAddress { get; set; }
public string ClubPhoneNumber { get; set; }
/// <summary>
/// The Club class is inisialised with a new instance of a club object and its details are provided upon instanciation.
/// </summary>
/// <param name="clubName">The name of the club is provided here</param>
/// <param name="clubAddress">The club address is provided here</param>
/// <param name="clubPhoneNumber">The clubs phone number is provided here</param>
public Club(string clubName, string clubAddress, string clubPhoneNumber)
{
ClubName = clubName ?? throw new ArgumentNullException(nameof(clubName));
ClubAddress = clubAddress ?? throw new ArgumentNullException(nameof(clubAddress));
ClubPhoneNumber = clubPhoneNumber ?? throw new ArgumentNullException(nameof(clubPhoneNumber));
}
}
Next you need the same thing from your Tennis player; a name, address, and maybe a phone number...
public class TennisPlayer
{
public string PlayerName { get; set; }
public string PlayerAddress { get; set; }
public string PlayerPhoneNumber { get; set; }
/// <summary>
/// The TennisPlayer class is inisialised with a new instance of a TennisPlayer object and its details are provided upon instanciation.
/// </summary>
/// <param name="playerName">The name of your tennis player</param>
/// <param name="playerAddress">The address of your tennis player</param>
/// <param name="playerPhoneNumber">The phone number of your tennis player</param>
public TennisPlayer(string playerName, string playerAddress, string playerPhoneNumber)
{
PlayerName = playerName ?? throw new ArgumentNullException(nameof(playerName));
PlayerAddress = playerAddress ?? throw new ArgumentNullException(nameof(playerAddress));
PlayerPhoneNumber = playerPhoneNumber ?? throw new ArgumentNullException(nameof(playerPhoneNumber));
}
}
Next, you want some type of controller class. Something where you can add and delete tennis players from clubs. What I would also recommend is adding a model class for all your data. This model would be then bind to your UI. But this is not implemented in this example. The main purpose here was to demonstrate keeping your class objects relative to what they represent :
/// <summary>
/// Here you can use an additional class to work with the objects of your other classes; such as Club, and TenniPlayer.
/// This class can add your tennis players to a list of tuple TennisPlayer, Club. When this class is first instanciated,
/// keep a copy of the instanciated object to pass around your application. New instances of this class will create a new instance of _PlayerClubPair.
/// </summary>
public class WithMembersOfClubs
{
/// <summary>
/// This list keeps track of your tenis players and the clubs they are in.
/// </summary>
private List<Tuple<TennisPlayer, Club>> _PlayerClubPair = new List<Tuple<TennisPlayer, Club>>();
/// <summary>
/// This method will add players to your _PlayerClubPair list and store the values of your tennis player and club in a tuple<Tuple<TennisPlayer, Club>>.
/// </summary>
/// <param name="tennisPlayer">This is where you pass in the tennis player object</param>
/// <param name="clubName">This is where you pass in the club object</param>
public void AddTennisPlayerAsMember(TennisPlayer tennisPlayer, Club clubName)
{
_PlayerClubPair.Add(Tuple.Create(tennisPlayer, clubName));
}
/// <summary>
/// This method will remove all references of a TennisPlayer object where the tuple item variable matches the tennisPlayer.Player name variable.
/// </summary>
/// <param name="tennisPlayer">This is where you pass in the tennis player you wish to remove</param>
public void RemoveTennisPlayerAsMember(TennisPlayer tennisPlayer)
{
_PlayerClubPair.RemoveAll(t => t.Item1.PlayerName == tennisPlayer.PlayerName);
}
/// <summary>
/// This method allows you to check the number of members in a given club object.
/// </summary>
/// <param name="clubName">The name of the club you want to check the number of players in</param>
/// <param name="number">The number variable will be used to count how many members are in a given club</param>
/// <returns></returns>
public int NumberOfMembers_In(string clubName, int number)
{
number += (_PlayerClubPair.Where(club => club.Item2.ClubName == clubName)).Count();
return number;
}
To use this code, you only need to call the constructors of your club and tennisplayer objects, and pass them to another class where the data is "managed" like a controller would manage data in a web app, and from there you would generally update a model, and as I said, that model would be then bind to your UI, where your UI would automatically be updated upon new data entered into it. A sample of running the code would be as follows :
Club club = new Club("Bondi Club", "33 Bondibeach Park", "0123456789");
TennisPlayer tennisPlayer1 = new TennisPlayer("Sheepings", "Sheep ville", "0234567890");
WithMembersOfClubs membersOfClubs = new WithMembersOfClubs();
membersOfClubs.AddTennisPlayerAsMember(tennisPlayer1, club);
TennisPlayer tennisPlayer2 = new TennisPlayer("Bob", "Bob ville", "3214567890");
membersOfClubs.AddTennisPlayerAsMember(tennisPlayer2, club);
Debug.WriteLine(string.Join(" ", $"{club.ClubName} has", membersOfClubs.NumberOfMembers_In(club.ClubName, 0), "Members"));
membersOfClubs.RemoveTennisPlayerAsMember(tennisPlayer1);
Debug.WriteLine(string.Join(" ", $"{club.ClubName} has", membersOfClubs.NumberOfMembers_In(club.ClubName, 0), "Members"));
Further references you should read up on are :
In this blog we will lean about Abstraction and Encapsulation. Difference between Abstraction and Encapsulation.
www.c-sharpcorner.com
.
and
abstract - C# Reference
docs.microsoft.com
Note; while I didn't go into abstraction in this example, I did however keep it relatively close to how you already constructed your code. But, you could have used a abstract data layer and still achieve the same goal. That approach is quite different to the example above, yet still worth educating yourself on.