Handle Generic Property between 2 Maps and avoid repeat code

Viewed 24

I wanted to ask about a scenario that happened to me. I am working with different models that implement a common generic property, which has different implementations, however, this generic property is common.

In this case, I am doing a Mapping where they have a generic property T in common with other models, in the example I created it is Comparable and NonComparable, both implement a Unit T.

Initially, I have a Mapper for comparable and another for IComparable, as you can see:

Code Link: https://github.com/xeof-landmark/generic-tests

Branch: main

Repeating Code Here:

Program

class Program
{
    static Fixture fixture = new Fixture();
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");

        var map = new Map();

        var unitsComparables = InitializeComparables();
        var unitsNonComparables = InitializeNonComparables();


        var unitsMappedCompared = map.MapList(unitsComparables);
        var unitsMappedNonCompared = map.MapList(unitsNonComparables);

        var units = new List<IUnit>();
        units.AddRange(unitsMappedCompared);
        units.AddRange(unitsMappedNonCompared);

        foreach (var unit in units)
        {
            Console.WriteLine(unit.Name);
            Console.WriteLine(Environment.NewLine);
        }
    }

    static List<Comparable<IUnit>> InitializeComparables()
    {
        return new List<Comparable<IUnit>>()
        {
            new Comparable<IUnit> { Unit = fixture.Create<LegacyUnit>() },
            new Comparable<IUnit> { Unit = fixture.Create<LegacyUnit>() },
            new Comparable<IUnit> { Unit = fixture.Create<LegacyUnit>() },
            new Comparable<IUnit> { Unit = fixture.Create<SaleUnit>() },
            new Comparable<IUnit> { Unit = fixture.Create<SaleUnit>() },
        };
    }

    static List<NonComparable<IUnit>> InitializeNonComparables()
    {
        return new List<NonComparable<IUnit>>()
        {
            new NonComparable<IUnit> { Unit = fixture.Create<LegacyUnit>() },
            new NonComparable<IUnit> { Unit = fixture.Create<LegacyUnit>() },
            new NonComparable<IUnit> { Unit = fixture.Create<LegacyUnit>() },
            new NonComparable<IUnit> { Unit = fixture.Create<SaleUnit>() },
            new NonComparable<IUnit> { Unit = fixture.Create<SaleUnit>() },
        };
    }
}

Classes:

public class SaleUnit : IUnit
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

public class LegacyUnit : IUnit
{
    public int Id { get; set; }
    
    private string name;

    public string Name
    {
        get { return $"{name}.Legacy"; }
        set { name = value; }
    }

    public string Description { get; set; }
}

public interface IUnit
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

public class Comparable<T>
    where T : IUnit
{
    public T Unit { get; set; }
    public int ExisingUnit { get; set; }
}

public class NonComparable<T>
    where T : IUnit
{
    public T Unit { get; set; }
    public bool IsDifferent(int id) => true;
    public void AddNewPrices(decimal prices) => throw new System.NotImplementedException();

}

Mapper

public class Map
{
    static Fixture fixture = new Fixture();

    public List<IUnit> MapList(List<Comparable<IUnit>> compareOnes)
    {
        return new List<IUnit>()
        {
            fixture.Create<LegacyUnit>(),
            fixture.Create<LegacyUnit>(),
            fixture.Create<LegacyUnit>(),
            fixture.Create<SaleUnit>(),
            fixture.Create<SaleUnit>(),
        };
    }

    public List<IUnit> MapList(List<NonComparable<IUnit>> compareOnes)
    {
        return new List<IUnit>()
        {
            fixture.Create<LegacyUnit>(),
            fixture.Create<LegacyUnit>(),
            fixture.Create<LegacyUnit>(),
            fixture.Create<SaleUnit>(),
            fixture.Create<SaleUnit>(),
        };
    }
}

I realized that at first, I was repeating a lot of code, in turn, I was reading a bit about covariance (I tried to do it but I couldn't).

I ended up solving it by casting them as an IComparable interface and then using a common Mapper for the IComparables.

Resolution Link: https://github.com/xeof-landmark/generic-tests/pull/1

So I ended up doing this

First I've created a new Interface to abstract the generic Unit.

public interface IComparable<T>
    where T : IUnit
{
    public T Unit { get; set; }
}

I have inherited the Comparable and NonComparable from IComparable

public class Comparable<T> : IComparable<T>
    where T : IUnit
{
    public T Unit { get; set; }
    public int ExisingUnit { get; set; }
}

public class NonComparable<T> : IComparable<T>
    where T : IUnit
{
    public T Unit { get; set; }
    public bool IsDifferent(int id) => true;
    public void AddNewPrices(decimal prices) => throw new System.NotImplementedException();
}

Then I added a new method overload to my Map Class to handle both Comparable and NonComparable MapList(List<IComparable> unitsComparables) to be able to handle both maps.

    public List<IUnit> MapList(List<IComparable<IUnit>> unitsComparables)
    {
        var mapUnitComparables = unitsComparables.Where(x => x is Comparable<IUnit>).Select(s => (Comparable<IUnit>)s).ToList();
        var mapUnitNonComparables = unitsComparables.Where(x => x is NonComparable<IUnit>).Select(s => (NonComparable<IUnit>)s).ToList();
        var result = new List<IUnit>();

        result.AddRange(MapList(mapUnitComparables));
        result.AddRange(MapList(mapUnitNonComparables));

        return result;
    }

And Finally my Program:

class Program
{
    static Fixture fixture = new Fixture();
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");

        var map = new Map();

        var unitsComparables = InitializeComparables();

        var units = map.MapList(unitsComparables);

        foreach (var unit in units)
        {
            Console.WriteLine(unit.Name);
            Console.WriteLine(Environment.NewLine);
        }
    }

    static List<IComparable<IUnit>> InitializeComparables()
    {
        return new List<IComparable<IUnit>>()
        {
            new Comparable<IUnit> { Unit = fixture.Create<LegacyUnit>() },
            new Comparable<IUnit> { Unit = fixture.Create<LegacyUnit>() },
            new Comparable<IUnit> { Unit = fixture.Create<LegacyUnit>() },
            new Comparable<IUnit> { Unit = fixture.Create<SaleUnit>() },
            new Comparable<IUnit> { Unit = fixture.Create<SaleUnit>() },
            new NonComparable<IUnit> { Unit = fixture.Create<LegacyUnit>() },
            new NonComparable<IUnit> { Unit = fixture.Create<LegacyUnit>() },
            new NonComparable<IUnit> { Unit = fixture.Create<LegacyUnit>() },
            new NonComparable<IUnit> { Unit = fixture.Create<SaleUnit>() },
            new NonComparable<IUnit> { Unit = fixture.Create<SaleUnit>() },
        };
    }
}

Now my question is, this solves it, but ..... Would it be the best way to solve it? or can it be done better?

Could I use covariance on this occasion? is it well planned?

0 Answers
Related