0

I am looking for a way to make the examples I provided more generic if possible. Could anyone provide some guidance on the best way to do this?

I have over 10 classes that look identical except for the data type and the static manager calls that are used. Some of the static method calls to the managers may have different implementations. In the example I used Item1 and Item2 as examples.

// Item1
public static class Item1Extensions
{   
    public static void Save(this Item1 item)
    {
        try
        {
            if (!item.IsDirty) return;

            Item1Manager.SaveItem(item);
        }
        catch (Exception ex)
        {
            throw new ItemSaveException();
        }
    }

    public static Item1 Get(long id, long callerId)
    {
        Item1 item;
        try
        {
            item = Item1Manager.GetItem(id, callerId);
        }
        catch (Exception ex)
        {
            throw new ItemRetrieveException();
        }

        return item;
    }

    public static List<Item1> List(long callerId)
    {       
        return Item1Manager.GetItemsByCallerId(callerId).Where(x => x.IsActive).ToList();
    }

    public static bool Delete(long id, long callerId)
    {
        try
        {
            Item1Manager.DeactivateItem(id, callerId);
            return true;
        }
        catch (Exception ex)
        {
            return false;
        }
    }
}

Item 2 is almost identical except maybe the manager calls

// Item2
public static class Item2Extensions
{   
    public static void Save(this Item2 item)
    {
        try
        {
            if (!item.IsDirty) return;

            Item2Manager.SaveItem(item);
        }
        catch (Exception ex)
        {
            throw new ItemSaveException();
        }
    }

    public static Item2 Get(long id, long callerId)
    {
        Item2 item;
        try
        {
            item = Item2Manager.GetItem(id, callerId);
        }
        catch (Exception ex)
        {
            throw new ItemRetrieveException();
        }

        return item;
    }

    public static List<Item2> List(long callerId)
    {       
        return Item2Manager.GetItemsByCallerId(callerId).Where(x => x.IsNotActive).ToList();
    }

    public static bool Delete(long id, long callerId)
    {
        try
        {
            Item2Manager.DeactivateItem(id, callerId);
            return true;
        }
        catch (Exception ex)
        {
            return false;
        }
    }
}

I thought using some generics or a factory based pattern would be helpful but I could use some guidance. Since the structure of the logic layer classes look identical except for the manager calls it seems that the duplication of this logic can be simplified like

public static class Item2ManagerCalls
{
    public static void Save(Item2 item)
    {
        Item2Manager.SaveItem(item);
    }

    public static Item2 Get(long id, long callerId)
    {
        return Item2Manager.GetItem(id, callerId);
    }

    public static List<Item2> List(long callerId)
    {
        return Item2Manager.GetItemsByCallerId(callerId).Where(x => x.IsActive).ToList();
    }

    public static bool Delete(long id, long callerId)
    {
        return Item2Manager.DeactivateItem(id, callerId);
    }
}
Jerode
  • 490
  • 4
  • 15
  • Do ItemManager, Item2Manager etc. classes have common base class? Can you create an extension for that base class? – nick Jul 28 '15 at 00:34
  • Why do you need to have `static` extensions? Seems to me that some of those can be instance methods. – Jaime Jul 28 '15 at 00:34
  • No they are calls to an added assembly and are the superclasses. – Jerode Jul 28 '15 at 00:35
  • @Jamie, they could be instance methods. Currently they are called at a model level with something like `Item2 myItem = new Item2 { Prop1 = "something" }; Item2Extensions.Save(myItem);` – Jerode Jul 28 '15 at 00:39
  • @nick, I can create an extension for the base class if needed. – Jerode Jul 28 '15 at 01:03

1 Answers1

1

Assuming that all item classes inherit from some base class names something like BaseItem that contains .IsDirty and .IsActive properties, then something like this should work:

// This is a fill for a class I assume that you already have
public class BaseItem
{
    public bool IsDirty;
    public bool IsActive;
}

// This is a fill for a class I assume that you already have
public class Item1 : BaseItem {}

// This is a fill for a class I assume that you already have
public class Item2 : BaseItem {}

// This is a fill for a class I assume that you already have
public class ItemSaveException : ApplicationException {}

// This is a fill for a class I assume that you already have
public class ItemRetrieveException : ApplicationException {}

// This is a fill for a class I assume that you already have
public static class Item1Manager
{
    internal static void SaveItem(Item1 item) {}

    internal static Item1 GetItem(long id, long callerId) { return new Item1(); }

    internal static List<Item1> GetItemsByCallerId(long callerId) { return new List<Item1>(); }

    internal static void DeactivateItem(long id, long callerId) {}
}

// This is a fill for a class I assume that you already have
public static class Item2Manager
{
    internal static void SaveItem(Item2 item) {}

    internal static Item2 GetItem(long id, long callerId) { return new Item2(); }

    internal static List<Item2> GetItemsByCallerId(long callerId) { return new List<Item2>(); }

    internal static void DeactivateItem(long id, long callerId) {}
}

public abstract class ItemManagerAdapter<TItemManagerAdapter, TItem>
    where TItemManagerAdapter : ItemManagerAdapter<TItemManagerAdapter, TItem>, new() 
    where TItem : BaseItem
{

    private static TItemManagerAdapter instance = new TItemManagerAdapter();

    protected abstract void SaveItem(TItem item);
    protected abstract TItem GetItem(long id, long callerId);
    protected abstract List<TItem> GetItemsByCallerId(long callerId);
    protected abstract void DeactivateItem(long id, long callerId);

    public static void Save(TItem item)
    {
        try
        {
            if (!item.IsDirty) return;

            instance.SaveItem(item);
        }
        catch (Exception ex)
        {
            throw new ItemSaveException();
        }
    }

    public static TItem Get(long id, long callerId)
    {
        TItem item;
        try
        {
            item = instance.GetItem(id, callerId);
        }
        catch (Exception ex)
        {
            throw new ItemRetrieveException();
        }

        return item;
    }

    public static List<TItem> List(long callerId)
    {       
        return instance.GetItemsByCallerId(callerId).Where(x => x.IsActive).ToList();
    }

    public static bool Delete(long id, long callerId)
    {
        try
        {
            instance.DeactivateItem(id, callerId);
            return true;
        }
        catch (Exception ex)
        {
            return false;
        }
    }

}

public class Item1ManagerAdapter : ItemManagerAdapter<Item1ManagerAdapter, Item1>
{
    protected override void SaveItem(Item1 item) { Item1Manager.SaveItem(item); }
    protected override Item1 GetItem(long id, long callerId) { return Item1Manager.GetItem(id, callerId); }
    protected override List<Item1> GetItemsByCallerId(long callerId) { return Item1Manager.GetItemsByCallerId(callerId); }
    protected override void DeactivateItem(long id, long callerId) { Item1Manager.DeactivateItem(id, callerId); }
}

public class Item2ManagerAdapter : ItemManagerAdapter<Item2ManagerAdapter, Item2>
{
    protected override void SaveItem(Item2 item) { Item2Manager.SaveItem(item); }
    protected override Item2 GetItem(long id, long callerId) { return Item2Manager.GetItem(id, callerId); }
    protected override List<Item2> GetItemsByCallerId(long callerId) { return Item2Manager.GetItemsByCallerId(callerId); }
    protected override void DeactivateItem(long id, long callerId) { Item2Manager.DeactivateItem(id, callerId); }
}

Use it like so:

public class Program
{

    void Main()
    {
        var item1 = Item1ManagerAdapter.Get(1, 1);
        Item1ManagerAdapter.Save(item1);
        var item1Deleted = Item1ManagerAdapter.Delete(1, 1);
        var item1s = Item1ManagerAdapter.List(1);

        var item2 = Item2ManagerAdapter.Get(2, 2);
        Item2ManagerAdapter.Save(item2);
        var item2Deleted = Item2ManagerAdapter.Delete(2, 2);
        var item2s = Item2ManagerAdapter.List(2);
    }

}
Tyree Jackson
  • 2,588
  • 16
  • 22
  • Looks incredibly promising. Is this a repository pattern that you used? – Jerode Jul 28 '15 at 06:37
  • 1
    This is just an adapter pattern. The ItemManagerAdapter is a base adapter class with abstract methods that the subclass adapter overrides to forward the calls to the static classes that you already have that you mentioned do not share a common base class. The adapter is used directly. If you want to see an implementation using the Repository pattern check out my answer to this other question: http://stackoverflow.com/questions/31646525/net-managing-layers-relationships/31659634#31659634 – Tyree Jackson Jul 28 '15 at 08:43
  • Can you double check a few items and update accordingly? The class declaration `public class ItemManagerAdapter where TItemManagerAdapter : ItemManagerAdapter, new() where TItem : BaseItem` I think this puts the call in an unlimited loop the way it is defined. Also, `public class Item1ManagerAdapter : ItemManagerAdapter` does not implement the abstract class that is defined. I don't think an abstract method can be in a non abstract class `protected abstract void SaveItem(TItem item);` How can this be accomplished? – Jerode Jul 28 '15 at 19:20
  • 1
    I've updated the PoC code with fills and corrections to ensure that it compiles. There should be no infinite loops as long as you are not changing calls from Item1ManagerAdapter to Item1Manager to call itself. Item1Manager and Item1ManagerAdapter are two different classes. – Tyree Jackson Jul 28 '15 at 19:46