C# – Manager/Container class vs static class methods

cdesigndesign-patternsobject-oriented

Suppose I have a Widget class that is part of a framework used independently by many applications. I create Widget instances in many situations and their lifetimes vary. In addition to Widget's instance specified methods, I would like to be able to perform the follow class wide operations:

  • Find a single Widget instance based on a unique id
  • Iterate over the list of all Widgets
  • Remove a widget from the set of all widgets

In order support these operations, I have been considering two approaches:

  1. Container class – Create some container or manager class, WidgetContainer, which holds a list of all Widget instances, support iteration and provides methods for Widget addition, removal and lookup. For example in C#:

    public class WidgetContainer : IEnumerable<Widget>  
    {  
      public void AddWidget(Widget);  
      public Widget GetWidget(WidgetId id);  
      public void RemoveWidget(WidgetId id);  
    }  
    
  2. Static class methods – Add static class methods to Widget. For example:

    public class Widget
    {
      public Widget(WidgetId id);
    
      public static Widget GetWidget(WidgetId id);
      public static void RemoveWidget(WidgetId id);
      public static IEnumerable<Widget> AllWidgets();  
    }
    

Using a container class has the added problem of how to access the container class. Make it a singleton?..yuck! Create some World object that provides access to all such container classes?

I have seen many frameworks that use the container class approach, so what is the general consensus?

Best Answer

The modern consensus is that you should not do this at all.

Having a 'all instances of this class' design has proven to be troublesome in many aspects, the foremost being with regards to concurrency. Do you want all widgets, or all widgets owned by your thread? Do you really want to make that all widget list threadsafe? Remember that unit tests are almost always done in parallel, so "my app is single threaded" might not be good enough.

The other problem you run into is in your design. With the 'one and only' list of anything, be it global, static or singleton you'll quickly run into issues when you need more than one. You'll run into issues with temporary widgets or offscreen widgets if they're automatically added to the list. You'll run into errors where you forgot to add them to the list if they're not automatically handled.

Once you don't have the 'all instances of a class' requirement, then you end up with a plain old collection. Whatever consuming code is working with the widgets (even if that's some framework that is in turn consumed) can orchestrate how the collection is used and how/when the widgets get put in there. The widgets themselves should not care.