4
\$\begingroup\$

Would like to know if the factory pattern I've implemented for my stat library is the correct way to go. I have an interface IPlayerStatsManager where the type parameter is an interface as well (IPlayerStatsModel). The IPlayerStatsManager is an interface that lists the common operations that can be performed on any class that implements IPlayerStatsModel. I've only completed the implementation for basketball and it seems to work correctly but would like to know if the way I currently have it is the most efficient way. Here's the code (some of the implementation removed for brevity)

Context

public interface IPlayerStatsModel
{
    string type {get; set;}
}

// concrete implementation
public class  BasketballPlayerStats : IPlayerStatsModel
{
    // various class members with getters and setters
}

public interface IPlayerStatsManager<T> where T : IPlayerStatsModel
{
    Task<T> GetPlayerGameStats(....);

    Task<List<T>> GetPlayersGameStats(...);

    // insert and update methods
}

//concrete implementation
public class BasketballPlayerStatsManager : IPlayerStatsManager<BasketballPlayerStats>
{
    public BasketballPlayerStatsManager(DbContext context)
    {
    }

    public async Task<BasketballPlayerStats> GetPlayerGameStats(...)
    {
    }
}

Factory

//Factory class
public class PlayerStatsFactory<IPlayerStatsManager>
{
    public static IPlayerStatsManager Create(DbContext context, int sportType)
    {
        if (sportType == (int)Utils.SportType.Basketball)
        {
             return (IPlayerStatsManager)Activator.CreateInstance(typeof(BasketballPlayerStatsManager));
        }
        return default(IPlayerStatsManager);
     }
}

I feel like there might be a better way but I'm not sure what that would look like. Any pointers would be greatly appreciated.

\$\endgroup\$
4
  • 2
    \$\begingroup\$ In general, looks good to me. You may want to replace if with switch/case or Dictionary<SportType, Type> (I'd go with the later). Also have generic Manager (looks like a Repository) for any T. \$\endgroup\$ Mar 13, 2015 at 18:49
  • 2
    \$\begingroup\$ Also, since you actually know the type you are creating, you could just use "new" instead of the Activator. \$\endgroup\$
    – Pablo Romeo
    Mar 13, 2015 at 18:52
  • \$\begingroup\$ Thanks for the suggestions. I do like the approach of using a dictionary so I'll switch to that and yes the intent was to create a generic repository related to retrieving and saving basic stats so that my team class can have a single method retrieving and a single method for saving stats based on the sport type it represented. I'll switch to using "new" as well. Good to know I'm on the right track. Thanks again. \$\endgroup\$
    – Edwin Boykin
    Mar 13, 2015 at 19:16
  • \$\begingroup\$ Activator.CreateInstance<T>() where T : new() inside just calls new T() so not a big difference. No difference at all, actually :) \$\endgroup\$ Mar 13, 2015 at 20:42

1 Answer 1

3
\$\begingroup\$

You have:

public static IPlayerStatsManager Create(DbContext context, int sportType)

And then:

if (sportType == (int)Utils.SportType.Basketball)

Why not just take a SportType parameter and remove that cast?


An abstract factory is a great tool, especially with Dependency Injection. The problem is that yours is a static method, which makes it available just about anywhere in the code, effectively hiding dependencies and defeating the purpose of IoC and DI: that factory becomes some sort of ambient context that's there waiting to be used by anyone, and your constructors don't need to document the fact that you're depending on an abstract factory: that smells IMO.

The factory implementation has one purpose: instantiate types. It has to know what it's creating. Your usage of reflection is nothing but a very convoluted way of saying...

return new BasketballPlayerStatsManager();

The cast to interface is also superfluous - if BasketballPlayerStatsManager does implement IPlayerStatsManager, then that's all the client code will see anyway (of course it implements that interface).


"Manager" sounds like a very general and not-so-precise way of referring to what other programmers would expect to be called a Repository; I'd rename it as such.


return default(IPlayerStatsManager);

You're returning a reference type - the default value can only be null. Your intent would be much clearer like this:

return null;
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thanks. For some reason I was getting a compiler error when trying to return BasketballPlayerStatsManager which is why I switched to casting it but you are right it's unnecessary and it did look wrong to me as well which led me to posting this question to get some feedback. I'll fix it. Thanks again for your suggestions. \$\endgroup\$ Mar 18, 2015 at 15:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.