C
C#3mo ago
TheUnquiet

Advice on making sure an object cannot be added to another list

I've got a program with boats, companies and fleets, if a ship is added to one fleet it cannot be added to another fleet, could someone give me some guideness as to how to achive this?
52 Replies
Pobiega
Pobiega3mo ago
Easiest way is to make the boats keep track of what fleet they belong to. ie, if a boatA.Fleet is null then yeah, we can add it to a fleet
TheUnquiet
TheUnquiet3mo ago
should i add an id field to each fleet ?
Pobiega
Pobiega3mo ago
this is also how a relational database would keep track of it, by the way. well either that, or keep a direct refernce to the fleet object If we assume that companies, fleets and boats are all classes
TheUnquiet
TheUnquiet3mo ago
yes
Pobiega
Pobiega3mo ago
then just have a property of type Fleet in your boat class
TheUnquiet
TheUnquiet3mo ago
all fleets have a dictionary of boats where the key is the boat name and the value is the boat object itself
Pobiega
Pobiega3mo ago
thats fine I suggest you have both thou fleets keep track of boats, but boats also keep track of fleets
TheUnquiet
TheUnquiet3mo ago
this is an assignment, i have to follow a specific document tho
Pobiega
Pobiega3mo ago
this lets you navigate the tree two ways can you show the requirements?
TheUnquiet
TheUnquiet3mo ago
sure but they are in dutch ill translate and send them your way
Pobiega
Pobiega3mo ago
translate them. most browsers have built in autotranslate
TheUnquiet
TheUnquiet3mo ago
Ships are managed by shipping companies, with each shipping company having different fleets, each fleet consisting of a number of ships. There are different types of ships, we distinguish: • Container ship: length, width, tonnage, name, number of containers, cargo value • Ro-Ro ship: length, width, tonnage, name, number of cars, number of trucks, cargo value • Cruise ship: length, width, tonnage , name, number of passengers, route • Ferry: length, width, tonnage, name, number of passengers, route • Oil tanker: length, width, tonnage, name, cargo value, volume (liters), cargo (oil, benzene, diesel or naphtha) •Gas tanker: length, width, tonnage, name, cargo value, volume (liters), cargo (LPG, LNG or ammonia) •Tugboat: length, width, tonnage, nameCargo value: is the value of the cargo expressed in euros. Tonnage: is the volume of the ship Route: for a ferry this is a fixed route between 2 ports, for a cruise ship this is a variable list of ports that are visited. The order of the ports is important for the route of a cruise ship. Ports can easily be represented by their name. A fleet has a name (e.g. North Sea Fleet) and has a number of ships. It should be possible to add ships to a fleet as well as to remove ships. A ship may of course only appear once in a fleet. It must also be possible to look up a ship very quickly based on the name of the ship. And it must also be possible to provide an overview of the ships included in a fleet. The shipping company has various fleets (e.g. North Atlantic fleet, North Sea fleet, Caribbean fleet, etc.). It should be possible to add or remove a fleet. Of course, the same fleet only appears once. A fleet must also be quickly searchable based on its name. A shipping company operates in several ports, so it should be possible to add ports to a shipping company (and also to remove ports). It should also be possible to provide an overview. The shipping company has several fleets (e.g. North Atlantic fleet, North Sea fleet, Caribbean fleet ...). It should be possible to add or remove a fleet. Of course, the same fleet only appears once. A fleet must also be quickly searchable based on its name. A shipping company operates in several ports, so it should be possible to add ports to a shipping company (and also to remove ports). It should also be possible to provide an overview the ports belonging to a shipping company in alphabetical order. Ports are simply represented by just a name (e.g. Antwerp, Rotterdam, ...). It should also be possible to place a ship in another fleet (e.g. place ship “De Plank” in fleet “Pacific Ocean”). Verder moet het mogelijk zijn om een overzicht te geven van : •De totale cargowaarde van deschepen die tot een rederij behoren.
•Het totaal aantal passagiers. •De tonnage per vloot op te lijsten (van groot naar klein). •Het totaal aantal volume die de tankers kunnen vervoeren. •De beschikbare sleepboten.Een rederij moet ook in staat zijn om info over een bepaald schip weer te geven (op basis van de naam van het schip).Opmerking : zorg ervoor dat alles efficiënt verloopt (denk eraan dat de lijsten in realiteit groot kunnen zijn en de opvragingen veelvuldig).
Pobiega
Pobiega3mo ago
So far I don't see anything that prevents double bookkeeping
TheUnquiet
TheUnquiet3mo ago
I think adding that extra field is okay I should add a field 'Fleet' to the Boat class, how will the rest work?
Pobiega
Pobiega3mo ago
public class Fleet
{
private List<Boat> _boats = new();

public void AddBoat(Boat boat)
{
if (boat.Fleet != null)
{
throw new InvalidOperationException("Boat is already assigned to a fleet");
}

boat.Fleet = this;
_boats.Add(boat);
}
}

public class Boat
{
public Fleet Fleet { get; internal set; }
}
public class Fleet
{
private List<Boat> _boats = new();

public void AddBoat(Boat boat)
{
if (boat.Fleet != null)
{
throw new InvalidOperationException("Boat is already assigned to a fleet");
}

boat.Fleet = this;
_boats.Add(boat);
}
}

public class Boat
{
public Fleet Fleet { get; internal set; }
}
something like this you said you used a dictionary, so just change that around as needed
TheUnquiet
TheUnquiet3mo ago
interesting the internal set is there because we don't want anyone outside the class to change the fleet that is assigned? am I correct?
Pobiega
Pobiega3mo ago
well yesn't what you described would be private set but we cant use that, as we want Fleet to be able to set it
TheUnquiet
TheUnquiet3mo ago
oh, what's the internal set do?
Pobiega
Pobiega3mo ago
so public is everyone private is "just me" protected is "me and my children" internal is "everyone in my assembly" assembly being the current project
TheUnquiet
TheUnquiet3mo ago
oh, what would just set be?
Pobiega
Pobiega3mo ago
then it uses whatever access modifier the property itself has ie public in this case
TheUnquiet
TheUnquiet3mo ago
c#
public void VoegSchip(Schip schip)
{
try
{
if (!Schepen.ContainsKey(schip.Naam))
{
Schepen.Add(schip.Naam, schip);
} else
{
throw new DomeinException("Vloot bevat al schip");
}
} catch(Exception e) { throw new DomeinException("Vloot : VoegSchip", e); }
}
c#
public void VoegSchip(Schip schip)
{
try
{
if (!Schepen.ContainsKey(schip.Naam))
{
Schepen.Add(schip.Naam, schip);
} else
{
throw new DomeinException("Vloot bevat al schip");
}
} catch(Exception e) { throw new DomeinException("Vloot : VoegSchip", e); }
}
this is my current function that adds a ship
Pobiega
Pobiega3mo ago
Man coding in any language other than english is so cursed 😄
TheUnquiet
TheUnquiet3mo ago
should I just change the whole thing? I'll rewrite it hold please
Pobiega
Pobiega3mo ago
no I see whats going on. kinda what is VoegSchip thou? but this doesnt respect the rules you have regarding a ship only existing in one fleet, obviously you just check that THIS fleet doesnt already contain it
TheUnquiet
TheUnquiet3mo ago
c#
public void AddBoat(Boat boat)
{
try
{
if (!Boats.ContainsKey(ship.Name))
{
Boats.Add(ship.Name, ship);
} else
{
throw new DomeinException("This ship belongs to this fleet");
}
} catch(Exception e) { throw new DomeinException("Fleet : AddBoat", e); }
}
c#
public void AddBoat(Boat boat)
{
try
{
if (!Boats.ContainsKey(ship.Name))
{
Boats.Add(ship.Name, ship);
} else
{
throw new DomeinException("This ship belongs to this fleet");
}
} catch(Exception e) { throw new DomeinException("Fleet : AddBoat", e); }
}
does this help a little?
Pobiega
Pobiega3mo ago
Not a big fan of the throw inside the try
TheUnquiet
TheUnquiet3mo ago
alright, I'll change that should I just use this instead of what I've had before?
Pobiega
Pobiega3mo ago
well, currently you can't really do the requirement of not having the same ship in multiple fleets
TheUnquiet
TheUnquiet3mo ago
I'll just use your function then, it makes more sense to me
Pobiega
Pobiega3mo ago
it makes sense to me too the alternative is accessing all other fleets and checking their lists, which isnt very nice make sure you hide access to the dictionary/list thou (also, why is it a dictionary?)
TheUnquiet
TheUnquiet3mo ago
because the program needs to be able to find ships easily
Pobiega
Pobiega3mo ago
renaming a ship is now much harder. Will you very often do name -> ship lookups on a fleet basis?
TheUnquiet
TheUnquiet3mo ago
no need for that, we don't need to rename any ships we only need to be able to delete, add and find ships in a fleet also change the fleet of a ship (I'll need to change this function then)
Pobiega
Pobiega3mo ago
remember, since we are doing double book-keeping, we need to be clever about how you let the code edit these things for example, boat.Fleet = myNewFleet; can be very dangerous here, unless we override the setter and write logic in there to update the old AND new fleets too
TheUnquiet
TheUnquiet3mo ago
the current function i have for ChangeFleet is the following:
c#
public void ChangeFleet(string SourceFleetName, string DestFleetName, string ShipName)
{
try
{
FLeet src = FindFleet(vlootNaamSrc);
Fleet dest = FindFleet(vlootNaamDest);
Ship ship = src.FindFleet(schipNaam);

if (src != null && dest != null && ship != null)
{
src.DeleteShip(schipNaam);
dest.AddShip(schip);
}
} catch (Exception e) { throw new DomeinException("Company : ChangeFleet", e); }
}
c#
public void ChangeFleet(string SourceFleetName, string DestFleetName, string ShipName)
{
try
{
FLeet src = FindFleet(vlootNaamSrc);
Fleet dest = FindFleet(vlootNaamDest);
Ship ship = src.FindFleet(schipNaam);

if (src != null && dest != null && ship != null)
{
src.DeleteShip(schipNaam);
dest.AddShip(schip);
}
} catch (Exception e) { throw new DomeinException("Company : ChangeFleet", e); }
}
can't I just add something like ship.Fleet = dest?
Pobiega
Pobiega3mo ago
yeah, you can and you dont even need to do that because dest.AddShip does that
TheUnquiet
TheUnquiet3mo ago
really?
Pobiega
Pobiega3mo ago
src.DeleteShip should however be modified to set it to null and also rename to RemoveShip the ship still exists
TheUnquiet
TheUnquiet3mo ago
what do you mean?
Pobiega
Pobiega3mo ago
try it and see.
TheUnquiet
TheUnquiet3mo ago
alright, thank you! It says that a ship with that key has already been added it's not removing the ship (i think) is that right?
Pobiega
Pobiega3mo ago
remember, double book-keeping you need to remove it from both places ie, both links
TheUnquiet
TheUnquiet3mo ago
so from the source Fleet and the Destanation Fleet?
Pobiega
Pobiega3mo ago
no from the source fleet and the boat and that should be done by fleet.RemoveShip
TheUnquiet
TheUnquiet3mo ago
set the Fleet field to null and the to the new Fleet?
Pobiega
Pobiega3mo ago
ie, when you remove the boat, break both links yes removing a ship means to remove from the list AND to set the fleet prop to null
TheUnquiet
TheUnquiet3mo ago
This is RemoveShip :
c#
public void `RemoveShip(string ShipName)
{
try
{
if (Ships.ContainsKey(ShipName))
{
Ships.Remove(ShipName);
Ship[ShipName].Fleet = null;
}
} catch (Exception e) { throw new DomeinException("Fleet : RemoveShip", e); }
}
c#
public void `RemoveShip(string ShipName)
{
try
{
if (Ships.ContainsKey(ShipName))
{
Ships.Remove(ShipName);
Ship[ShipName].Fleet = null;
}
} catch (Exception e) { throw new DomeinException("Fleet : RemoveShip", e); }
}
Pobiega
Pobiega3mo ago
that aint gonna work look at your order of operations
TheUnquiet
TheUnquiet3mo ago
I see it the ship isn't in the dict anymore
Pobiega
Pobiega3mo ago
yeah so turn them around
TheUnquiet
TheUnquiet3mo ago
thank you so much! you're amazing