There are many things I would change here to make your solution much more scalable and maintainable. The items you'll need to research to have a full understanding are:
Enums
Enums are powerful in that they help us represent values in a way that is consistent, versatile, and most importantly, checked at compile time. From a maintenance aspect they can save your day (and in severe cases week or month) by helping to remove magic numbers and strings from your code. Imagine you set a Tractor
's status to Arived
instead of Arrived
and now your arrival code doesn't work. Enums help you prevent this issue and many more. I highly recommend changing your Status
property to an enum
:
public enum EquipmentStatus { None, Arrived, Fueled, Serviced, Ready }
Interfaces
Interfaces are very, very powerful. They allow us to define what an object looks like without any implementation. For example, you know that your trailers all have three properties in common length
, width
and height
. With this in mind, you can define a trailer using an interface, and handle the ITrailer
instead of having to account for LongTrailer
and ShortTrailer
individually. I won't go into too much detail on this topic though because I don't feel that I can truly do it justice.
With that in mind though, I recommend creating an interface that defines your most common properties across all three objects (Id
, Terminal
and Status
):
public interface IEquipmentObject {
string Id { get; set; }
int[] Terminal { get; set; }
EquipmentStatus Status { get; set; }
}
Generics
Generics are fantastic monsters in that when used properly, can help you accomplish tasks that you never thought were possible. In your case, the section below on single responsibility will hit this in more detail. With generics, you can specify a data type when creating an object, calling a method, etc. and this can be incredibly beneficial. You use generics already with List<T>
. The surface benefit there is that instead of having to box and unbox your equipment objects, you get to work with them directly because you're using a List<Trailer>
not a List<object>
.
Single Responsibility
I personally think you're trying to accomplish way too much with a single object definition. While it makes sense from an operational standpoint, it's much easier to break it down from a development standpoint. Instead of creating one object that handles all three together in one monolithic definition, create a single definition that is responsible for the work in a simplified form:
public sealed class EquipmentCollection<T> : ICollection<T> where T : IEquimentObject {
// See the basic example below.
}
From here, I would finally create the equipment manager object that has various instances of EquipmentCollection
:
public sealed class EquipmentManager {
public EquipmentCollection<Tractor> Tractors { get; set; }
public EquipmentCollection<Trailer> LongTrailers { get; set; }
public EquipmentCollection<Trailer> ShortTrailers { get; set; }
}
When implementing ICollection<T>
on the EquipmentCollection<T>
definition, you'll setup the methods to add, insert, and remove objects (among many other functionalities involved with a collection object). That's where you'll need to reference your Id
to make sure you're meeting the requirements of adding and removing by Id
.
Basic Sample
public enum EquipmentStatus { None, Arrived, Fueled, Serviced, Ready }
public enum TrailerType { None, Short, Long }
public class Terminal {
public int Id { get; set; }
}
public interface IEquipmentObject {
string Id { get; set; }
Terminal Owner { get; set; }
EquipmentStatus Status { get; set; }
}
public class Tractor : IEquipmentObject {
public string Id { get; set; }
public Terminal Owner { get; set; }
public EquipmentStatus Status { get; set; }
}
public class Trailer : IEquipmentObject {
public string Id { get; set; }
public Terminal Owner { get; set; }
public EquipmentStatus Status { get; set; }
public TrailerType Type { get; set; }
public int Length { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
public class EquipmentCollection<T> : ICollection<T> where T : IEquipmentObject {
#region Fields
private List<T> _items = new List<T>();
#endregion
#region ICollection Custom Implementation
public bool Contains(T item) => _items.Any(containedItem => containedItem.Id == item.Id);
public bool Remove(T item) => _items.Remove(_items.Single(removalCandidate => removalCandidate.Id == item.Id));
#endregion
#region ICollection Passthrough
public int Count => _items.Count;
public bool IsReadOnly => false;
public void Add(T item) => _items.Add(item);
public void Clear() => _items.Clear();
public void CopyTo(T[] array, int arrayIndex) => _items.CopyTo(array, arrayIndex);
public IEnumerator<T> GetEnumerator() => _items.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator();
#endregion
}
public class EquipmentManager {
public EquipmentCollection<Tractor> Tractors { get; set; }
public EquipmentCollection<Trailer> ShortTrailers { get; set; }
public EquipmentCollection<Trailer> LongTrailers { get; set; }
}
Good luck!
See also: sealed, generic constraints.