You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#抽象工厂模式问题:无法访问具体类自有方法

问题描述

我正在学习C#设计模式,朋友帮我写了一套抽象工厂模式的代码。逻辑是先创建工厂Fa,Fa根据枚举创建工厂Fb,再由Fb生成可调用API的具体类。但我发现通过Fb创建的对象只能调用父接口IDoll的方法,访问不了自身(比如IJaneDoll)的自有方法。我需要创建Jane Doll这类继承自Doll且有自有属性的对象,后续如果加Santa Doll也要能访问它的专属方法,该怎么解决?


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);
}

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);
    }
}

解决方案

针对这个问题,有几种实用的处理方式,可根据后续扩展需求选择:

方式一:泛型工厂接口改造(推荐)

把顶层IDollFactory改成泛型接口,让工厂直接返回具体子类型,保证类型安全且支持扩展:

1. 修改IDollFactory为泛型

namespace Qqq.Dolls.Web.Infrastructure;

public interface IDollFactory<TDoll> where TDoll : IDoll
{
    TDoll Create(Doll dollType, HttpContext httpContext);
}

2. 实现泛型DollFactory

public class DollFactory<TDoll> : IDollFactory<TDoll> where TDoll : IDoll
{
    private readonly IJaneDollFactory _janeDollFactory;
    // 后续添加SantaDollFactory依赖

    public DollFactory(IJaneDollFactory janeDollFactory)
    {
        _janeDollFactory = janeDollFactory;
    }

    public TDoll Create(Doll dollType, HttpContext httpContext)
    {
        switch (dollType)
        {
            case Doll.Jane:
                var token = httpContext.Session.GetObject<OAuthResponse>(SessionConstants.JaneToken);
                return (TDoll)(object)_janeDollFactory.Create(token, JaneScopeConstants.GetAllScopes());
            case Doll.Santa:
                // 调用SantaDollFactory创建对象并转换类型
                break;
            default:
                throw new NotImplementedException();
        }
    }
}

3. 控制器中使用泛型工厂

public class InventoryController : Controller
{
    private readonly IDollFactory<IJaneDoll> _janeDollFactory;
    private readonly IMapper _mapper;

    public InventoryController(IDollFactory<IJaneDoll> janeDollFactory, IMapper mapper)
    {
        _janeDollFactory = janeDollFactory;
        _mapper = mapper;
    }

    public async Task<IActionResult> List()
    {
        var janeDoll = _janeDollFactory.Create(Doll.Jane, HttpContext);
        // 直接调用IJaneDoll专属方法
        var inventory = await janeDoll.GetInventory();
        
        var ret = inventory.InventoryItems.Select(
            inventoryItem => _mapper.Map<InventoryViewModel>(inventoryItem));

        return View(ret);
    }
}

方式二:类型转换(快速临时解决)

如果不想改动工厂结构,可在获取对象后做类型判断和转换,缺点是需要提前知晓具体类型:

var doll = _dollFactory.Create(Doll.Jane, HttpContext);
if (doll is IJaneDoll janeDoll)
{
    var inventory = await janeDoll.GetInventory();
    // 处理业务逻辑
}

方式三:访问者模式(复杂场景适配)

如果后续Doll类型多、专属操作差异大,可用访问者模式统一处理不同类型的专属逻辑:

1. 定义访问者接口

public interface IDollVisitor
{
    void Visit(IJaneDoll janeDoll);
    void Visit(ISantaDoll santaDoll);
}

2. 扩展IDoll接口添加Accept方法

public interface IDoll
{
    Task ListProductAsync(Product product);
    Task<Product> GetProductAsync(string productId);
    void Accept(IDollVisitor visitor);
}

3. 实现具体Doll的Accept方法

public class JaneDoll : IJaneDoll
{
    // 原有方法实现...
    public void Accept(IDollVisitor visitor)
    {
        visitor.Visit(this);
    }
}

4. 创建具体访问者处理逻辑

public class InventoryVisitor : IDollVisitor
{
    public async Task<Inventory> Inventory { get; private set; }

    public void Visit(IJaneDoll janeDoll)
    {
        Inventory = await janeDoll.GetInventory();
    }

    public void Visit(ISantaDoll santaDoll)
    {
        // 处理SantaDoll专属逻辑
    }
}

5. 控制器中使用

var visitor = new InventoryVisitor();
var doll = _dollFactory.Create(Doll.Jane, HttpContext);
doll.Accept(visitor);
var inventory = visitor.Inventory;

内容的提问来源于stack exchange,提问作者Clas Antics

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 16:20:33