1

I am working on an EquipmentManager class that needs to be able to view all ShortTrailers, LongTrailers and Tractors, add a new tractor/trailer based on a unique ID, and remove a tractor/trailer based on its ID.

I'm not really sure where to begin. My current dilemma is trying to make a list for just Trailer in an EquipmentManager class that can combine both classes of ShortTrailer and LongTrailer.

EquipmentManager class:

using System.Collections.Generic;

namespace BusinessLayer
{
    public class EquipmentManager
    {
        private List<Tractor> Tractors {get; set;}
        private List<ShortTrailer, LongTrailer> Trailers {get; set;}
        //          Incorrect but the idea^
        public EquipmentManager() //constructor
        {
            Trailers = new List<Trailer>();
            Tractors = new List<Tractor>();
        }

        public object GetTractorCount()
        {
            int TractorCount = 0;

            return TractorCount;
        }

        public object GetTrailerCount()
        {
            int TrailerCount = 0;

            return TrailerCount
        }

        public string ViewTractors(Tractors)
        {
            new List<Tractors> = " ";

            return Tractors;
        }

        public string ViewTrailers()
        {
            new string List<Trailers>;
            
            return Tractors;
        }

        public bool AddTractor()
        {

        }

        public bool RemoveTractor()
        {

        }

        public bool AddTrailer()
        {

        }

        public bool RemoveTrailer()
        {
            
        }
        
    }
}

Classes for Tractor, ShortTrailer, and LongTrailer in their own .cs files, respectively:

namespace BusinessLayer
{
    public class Tractor
    {
        string ID;
        int[] Terminal = {01, 02, 03}; 
        string[] Status = {"Arrived", "Fueled", "Serviced", "Ready"};
    }

public class ShortTrailer
    {
        string ID;
        int[] Terminal = {01, 02, 03, 04, 05, 06, 07, 08, 09, 10};
        string[] Status = {"Arrived", "Fueled", "Serviced", "Ready"};
        int Length;
        int Width;
        int Height;
    }

public class LongTrailer
    {
        string ID;
        int[] Terminal = {01, 02, 03, 04, 05, 06, 07, 08, 09, 10};
        string[] Status = {"Arrived", "Fueled", "Serviced", "Ready"};
        int Length;
        int Width;
        int Height;
    }
}
seasharp
  • 35
  • 4
  • 3
    This seems to be begging for an `ITrailer` interface – Colm Prunty Mar 08 '21 at 21:28
  • Is this for a class project or something for work? Reason I ask is that the answer will be different depending on what the use case is. In a nutshell, you are mixing your models and logic. Your `Manager` should not have the models in there. It should operate on the model class. If this is for school, it's fairly straightforward to use a `Dictionary` to store things in memory. If not, for the implementation you would need to have classes to save your model state values in a database such as `DynamoDb`, etc. – JebaDaHut Mar 08 '21 at 21:29
  • 1
    What is the difference between a short trailer and a long trailer? Why do they need to have their own classes? – John Wu Mar 08 '21 at 21:31
  • @ColmPrunty or perhaps an `IEntity` to be honest. – Hazel へいぜる Mar 08 '21 at 21:52
  • This question begs for education in interfaces, generics, single responsibility, and more. Is there a single issue you'd like assistance with? As a whole I believe your question is a bit too broad. – Hazel へいぜる Mar 08 '21 at 22:10
  • @ColmPrunty that's what I was thinking, but I am not sure how to implement interfaces appropriately. Aren't they used kind of like placeholders for functions shared by classes? – seasharp Mar 08 '21 at 22:11
  • @JebaDaHut This is a class project. I'll try to implement dictionaries instead because that would seem to work a bit better. So in the context of equipment and their IDs, the ID would be the key and the individual tractor/trailer would be the value, right? – seasharp Mar 08 '21 at 22:14
  • @タコス Sorry, I am new to the site and quite behind in my understanding of the concepts. I am about to try to figure out interfaces, but could you provide insight as to how to correctly implement dictionaries in this context? – seasharp Mar 08 '21 at 22:25
  • 1
    @seasharp updated my answer with a basic example to help clarify some of the items I pointed out. Good luck! – Hazel へいぜる Mar 09 '21 at 00:23

2 Answers2

3

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.

Hazel へいぜる
  • 2,751
  • 1
  • 12
  • 44
1

There's a couple of things you can do here.

You can use an interface:

public interface ITrailer
{
    string ID;
    int[] Terminal;
    string[] Status;
    int Length;
    int Width;
    int Height;
}

public class ShortTrailer : ITrailer
{
    public string ID { get; set; }
    public int[] Terminal { get; set; } = {01, 02, 03, 04, 05, 06, 07, 08, 09, 10};
    public string[] Status { get; set; } = {"Arrived", "Fueled", "Serviced", "Ready"};
    public int Length { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }
}

public class LongTrailer : ITrailer
{
    public string ID { get; set; }
    public int[] Terminal { get; set; } = {01, 02, 03, 04, 05, 06, 07, 08, 09, 10};
    public string[] Status { get; set; } = {"Arrived", "Fueled", "Serviced", "Ready"};
    public int Length { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }
}

Then inside your EquipmentManager class you would have public List<ITrailer> Trailers { get; set; }

Or if you expect to have shared properties in your trailer classes with the option of shared OR unique methods, you can use an abstract class:

public abstract class Trailer
{
    public string ID { get; set; }
    public int[] Terminal { get; set; } = {01, 02, 03, 04, 05, 06, 07, 08, 09, 10};
    public string[] Status { get; set; } = {"Arrived", "Fueled", "Serviced", "Ready"};
    public int Length { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }
    
    public virtual void DoSomething()
    {
        // Do something generic for all trailers, but this method can be overridden by the child classes
    }
}

public class ShortTrailer : Trailer
{
    public override void DoSomething()
    {
        // Do something short trailer version
    }
}

public class LongTrailer : Trailer
{
    public override void DoSomething()
    {
        // Do something long trailer version
    }
}

Then inside your EquipmentManager class you would have public List<Trailer> Trailers { get; set; }

Arman Peiravi
  • 1,079
  • 9
  • 12
  • Wouldn't it be better to use an `enum` for `Terminal` and `Status`? – Julia Mar 08 '21 at 22:56
  • @Julia yes certainly it would and I considered doing that myself, but that's not what OP asked and I didn't want to confuse things for them. – Arman Peiravi Mar 08 '21 at 23:12