distractedhamster
distractedhamster

Reputation: 767

How to keep a list in sync with references?

I have a list of clients and a list of rooms. A Room can have many clients, a Client can only be in one Room.

I created a list in my Room class to contain references to its clients and also a reference in my Client class to keep a reference to the Room it's currently in.

The problem is that someone could add a Client to a Room without setting the Room reference in the Client to the same Room (or to any room at all for that matter).

The best I have come up with is to wrap the client list in a custom class with a custom Add method that sets the clients room reference to the room it's being added to. That way someone could still set the clients room reference without adding it to that room so then I would have to customize the clients room reference setter so that it Adds itself to the referenced Rooms list of clients whenever it is set. I would have to add some checks to avoid infinite recursion. Feels a little clunky.

  1. Is there a name for this problem?
  2. Are there some established patterns for keeping relationships like these in sync?

Thanks

Upvotes: 1

Views: 138

Answers (1)

Miraziz
Miraziz

Reputation: 509

If you do not want to solve the problem through databases, then one solution might be that you make your list of clients private and make a public method for adding a new Client in your Room class and control references integrity in your Client class.

Room.cs

class Room
{
    public int RoomId { get; set; }
    public decimal Price { get; set; }
    public string RoomNumber { get; set; }

    private List<Client> Clients { get; set; }

    public void AddClient(Client client)
    {
        if (client != null && !Clients.Contains(client))
        {
            Clients.Add(client);

            // after adding client to the current room
            // (re)assign his room to the current room
            client.Room = this;
        }
    }

    public void RemoveClient(Client client)
    {
        if (client != null && Clients.Contains(client))
            Clients.Remove(client);
        /*else
            throw new System.Exception($"There is no clien assigned to the room: {this.RoomNumber}");*/
    }

    public Room(int id, decimal price, string roomNumber)
    {
        RoomId = id;
        Price = price;
        RoomNumber = roomNumber;
        Clients = new List<Client>();
    }
}

Client.cs

class Client
{
    public int ClientId { get; set; }
    public string FullName { get; set; }

    private Room _room;
    public Room Room
    {
        get => _room;
        set
        {
            // Check if the Client is assigned to any room
            if (value != null && _room == null)
            {
                _room = value;

                // add client to the new room
                value.AddClient(this);
            }
            // If the Client is currently assigned to any room
            // and we want to just reassign it from it
            else if(value == null && _room != value)
            {
                _room.RemoveClient(this);
                _room = null;
            }
            // If the Client is currently assigned to any room
            // and we want to assign him to another room
            else if(value != null && _room != value && _room != null)
            {
                _room.RemoveClient(this);
                
                value.AddClient(this);
                _room = value;
            }
        }

    }

    public Client(int id, string fullName, Room room = null)
    {
        ClientId = id;
        FullName = fullName;
        Room = room;
    }
}

Some usage:

Program.cs

class Program
{
    static void Main(string[] args)
    {
        Room room = new Room(1, 250, "Room 1");
        Room room1 = new Room(2, 200, "Room 2");

        Client client = new Client(1, "Client1");
        Client client2 = new Client(2, "Client2");
        Client client3 = new Client(3, "Client3");

        // Add clients to room through Room's object method
        room.AddClient(client);
        room.AddClient(client2);

        room.Clients.ForEach(c => WriteLine(c.FullName));
        WriteLine();
        /* Output:
         * Client1
         * Client2
         */

        // Assign client3 to room1 through Client's 'room object'
        client3.Room = room1;
        
        room1.Clients.ForEach(c => WriteLine(c.FullName));
        WriteLine();
        /* Output:
         * Client3
         */

        // Reassign client to another room
        client.Room = room1;

        room1.Clients.ForEach(c => WriteLine(c.FullName));
        WriteLine();
        /* Output:
         * Client3
         * Client1
         */

        room.Clients.ForEach(c => WriteLine(c.FullName));
        WriteLine();
        /* Output:
         * Client2
         */

        // Try to assign client to his current Room --> it will not add
        client.Room = room1;

        room1.Clients.ForEach(c => WriteLine(c.FullName));
        WriteLine();
        /* Output:
         * Client3
         * Client1
         */

        // Remove client through Room's object method
        room1.RemoveClient(client);

        room1.Clients.ForEach(c => WriteLine(c.FullName));
        WriteLine();
        /* Output:
         * Client3
         */

        // Try to remove not existing client from Room --> it will not remove any
        room1.RemoveClient(client);

        // Reassign client from room --> client3's reference from room1.Clients will be removed
        client3.Room = null;

        room1.Clients.ForEach(c => WriteLine(c.FullName));
        /* Output:
         * 
         */
    }
}

Basically, we need to control the references through objects all the time. And be careful during the implementation, because sometimes it might confuse you, and incorrect implementation might lead to StackOverflow (continuously adding, removing references continuously), not adding or removing references in different situations, so you might want to test for different cases before using.

Upvotes: 2

Related Questions