Search code examples
c#asp.net-mvcdesign-patternsfactory-patternabstract-factory

Understanding the code in an Abstract factory pattern


I am trying to learn design patterns in C# and my friend has written me some code for an Abstract factory pattern (I think).

from what I am seeing the code creates a factory(Fa), this factory(Fa) then creates another factory(Fb) based on an Enum and then that factory(Fb) creates a concrete class that can be used to call an API etc.

I can create a factory(Fb) and it creates the class but when I call methods from the class that were created by the factory(fb), I do not see my methods and cant call them but can only call the class that it inherits.

What I am trying to do in a nutshell, is create a factory that creates Jane dolls (like it does) and this inherits everything from the doll class, it also has all its own properties, great, but why cant I access its own properties when I make a factory to create the Jane Factory, it only lets me use the inherited Doll methods this way, but if I created another factory to create Santa dolls it would have different methods I need to use.

**Web.Controllers **

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;


namespace Qqq.Dolls.Web.Controllers
{

    public class InventoryController : Controller
    {
        private readonly IDollFactory _dollFactory;
        private readonly IJaneDollFactory _janeDollFactory;
        private readonly IMapper _mapper;

        public InventoryController(IJaneDollFactory dollFactory, IMapper mapper, IDollFactory dollFactory1)
        {
            _janeDollFactory = dollFactory;
            _mapper = mapper;
            _dollFactory = dollFactory1;
        }

        public async Task<IActionResult> List()
        {
            var token = HttpContext.Session.GetObject<OAuthResponse>(SessionConstants.JaneToken);
            var doll = _JaneDollFactory.Create(token, JaneScopeConstants.GetAllScopes());


            var a = _DollFactory.Create(Doll.Jane, HttpContext);

            var ab = await a.LGetProductAsync("TestProduct");


            var inventory = await doll.GetInventory();

            var ret = inventory.InventoryItems.Select(
                inventoryItem => _mapper.Map<InventoryViewModel>(inventoryItem));

            return View(ret);

        }
    }

}

DollFactory

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Qqq.Dolls.Web.Infrastructure;

public class DollFactory : IDollFactory
{
    private readonly IJaneDollFactory _JaneDollFactory;

    public DollFactory(IJaneDollFactory JaneDollFactory)
    {
        _JaneDollFactory = JaneDollFactory;
    }

    public IDoll Create(Doll Doll, HttpContext httpContext)
    {
        switch (Doll)
        {
            case Doll.Jane:
                var token = httpContext.Session.GetObject<OAuthResponse>(SessionConstants.JaneToken);

                return _JaneDollFactory.Create(token, JaneScopeConstants.GetAllScopes());
            default:
                throw new NotImplementedException();
        }

    }
}

IJaneDollFactory Interface

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Qqq.Dolls.Jane;

public interface IJaneDollFactory
{
    IJaneDoll Create(OAuthResponse oAuthResponse, List<string> scopes, HttpMessageHandler httpMessageHandler = null);
}

**IJaneDoll interface **

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Qqq.Dolls.Jane;

public interface IJaneDoll : IDoll
{
    //Inventory
    Task<Inventory> GetInventory();
    Task ListInventoryItem(InventoryItem product);
    Task DeleteInventoryItem(string sku);

}

IDoll interface

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

public interface IDoll
{
    Task ListProductAsync(Product product);

    Task<Product> GetProductAsync(string productId);
}

interface IDollFactory

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Qqq.Dolls.Web.Infrastructure;

public interface IDollFactory
{
    IDoll Create(Doll doll, HttpContext httpContext);
}

JaneDollFactory

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;


namespace Qqq.Dolls.Jane;

public class JaneDollFactory : IJaneDollFactory
{
    private readonly IMapper _mapper;
    private readonly JaneApiConfiguration _JaneApiConfiguration;

    public JaneDollFactory(IOptions<JaneApiConfiguration> JaneApiConfiguration, IMapper mapper)
    {
        _mapper = mapper;
        _JaneApiConfiguration = JaneApiConfiguration.Value;
    }
    public IJaneDoll Create(OAuthResponse oAuthResponse, List<string> scopes, HttpMessageHandler httpMessageHandler = null)
    {
        return new JaneDoll(_mapper, _JaneApiConfiguration, oAuthResponse, scopes, httpMessageHandler);
    }

}

Solution

  • This is happening is because in you factory create method you have the return type as

    IDoll

    So when you create a doll, no matter what doll you instantiate, it gets implicitly casted into an IDoll (this is the mistake in DollFactory class)

    Since your caller knows what doll it wants the abstract factory to create, when you retrieve the object you can explicitly cast it to an IJaneDoll

    var a = (IJaneDoll) _DollFactory.Create(Doll.Jane, HttpContext);
    

    This should allow you to access members from both IDoll and IJane doll.