这是为已更改的工作流传递附加数据的更好方法

本文关键字:数据 方法 更好 工作流 | 更新日期: 2023-09-27 18:03:58

设实体A与实体BC的关系均为1:1. *
同时,B只包含C的部分键,但结合A的键,可以将BC联系起来。

翻译成人类语言:
会计中心(A)由分支机构(C)组成,支付流(B)从会计中心进入系统,并包含只在给定会计中心内唯一的BranchId。我们需要检查所提供的BranchId的正确性,并将Payment绑定到Branch)

处理B s的集合:

class BsProcessor
{
    private BProcessor _processor;
    void ProcessBs(IEnumerable bs)
    {
          for (var b in bs)
          {
              _processor.Process(b);
          }
    }
}



某天需求发生变化,现在BProcessor需要根据相应的C做出决策。

从性能的角度来看,最好是提前获得B所指向的A的所有C,然后通过更改Process方法签名将这些数据传递给BProcessor。

class BsProcessor
{
    private BProcessor _processor;
    private CProvider _provider;
    void ProcessBs(IEnumerable bs)
    {
          var aKeys = bs.Select(b => b.aKey);
          var cs = _provider.GetCsForAs(aKeys).ToDictionary( c => c.aKey);
          for (var b in bs)
          {
              var cCandidates = cs[b.aKey];
              _processor.Process(b, cCandidates);
          }
    }
}

BProcessor然后尝试找到匹配的C

这是一个直接快速的代码和简单的工作解决方案…
老实说,我并不完全喜欢它。我认为这违反了SRP。
除了性能之外,BsProcessor没有任何其他原因可以知道Cs

查找C候选和匹配的逻辑既不属于BsProcessor也不属于BProcessor,而是一个专用的服务CMatcher

class CMatcher
{
    private CProvider _provider;
    private IEnumerable<aKey> _aKeys;
    public CMatcher(CProvider provider, IEnumerable<aKey> aKeys)
    {
        ...
    }
    public C Match(aKey akey, cPartialKeyFromB partialKey)
    {
    } 
}

这个服务应该被BProcessor注入和使用。因为这个服务是上下文的,并且需要key集合,所以我们需要切换到工厂模式:

class BsProcessor
{
    private BProcessorFactory _factory;
    void ProcessBs(IEnumerable bs)
    {
          var aKeys = b.Select(b => b.aKey);
          var processor = _factory.Create(aKeys);
          for (var b in bs)
          {
             processor.Process(b);
          }
    }
}

我认为这个解决方案的代码有点复杂,但更容易测试。我唯一的问题是BProcessor包含多个方法,而那些不需要将CB匹配,因此使用构造函数注入是不好的,而方法注入使BsProcessor知道CMatcher与初始解决方案有些相似。

这里开始看起来像BProcessor请求被分成单独的类,我问自己SRP是否值得这么多重构,第二种方法在哪里确实比第一种方法更好?

这是为已更改的工作流传递附加数据的更好方法

我认为,诀窍是采用第一种方法,但是在处理过程中传递 CProvider,而不是始终包含 CProvider

此外,根据处理中涉及的其他内容,您可能根本不需要BProcessor/BsProcessor类;简单地让每个B能够处理自己,给定一个CProvider来找到相应的C

下面是一个快速而肮脏的例子,使用上面的会计类比。公平的警告,我没有验证很多数据,而且我可能对结构的要求做出了错误的假设。
// A
class AccountingCenter
{
    private List<Branch> m_branches = new List<Branch>();
    public string Id { get; private set; }
    public ReadOnlyCollection<Branch> Branches { get { return m_branches.AsReadOnly(); } }
    public AccountingCenter(string id, IEnumerable<string> branchIds = null)
    {
        Id = id;
        if (branchIds != null)
        {
            foreach(var b in branchIds)
            {
                AddBranch(b);
            }
        }
    }
    public void AddBranch(string id)
    {
        m_branches.Add(new Branch(id, this));
    }
}
// C
class Branch
{
    private AccountingCenter m_parentCenter;
    public string BranchId { get { return m_parentCenter.Id + "-" + Id; } } // or whatever the combined implementation would be
    public string Id { get; private set; }
    public Branch(string id, AccountingCenter center)
    {
        Id = id;
        m_parentCenter = center;
    }
}
// CProvider
class AccountingCenterContainer
{
    private Dictionary<string, Branch> m_BranchIdToBranchMap = new Dictionary<string, Branch>();
    public AccountingCenterContainer(IEnumerable<AccountingCenter> centers)
    {
        foreach (var c in centers)
        {
            foreach (var b in c.Branches)
            {
                m_BranchIdToBranchMap.Add(b.BranchId, b);
            }
        }
    }
    public Branch GetBranchFromId(string branchId)
    {
        if (!m_BranchIdToBranchMap.ContainsKey(branchId))
        {
            throw new ArgumentException("ID " + branchId + " does not correspond to any known branch");
        }
        return m_BranchIdToBranchMap[branchId];
    }
}
// B
class Payment
{
    public string BranchId { get; private set; }
    public Payment(string branchId)
    {
        BranchId = branchId;
    }
    public void Process(AccountingCenterContainer container)
    {
        Branch b = container.GetBranchFromId(BranchId);
        // process...
    }
}

SRP只是一个指导,在简洁的决策下可以安全地违反。因此,如果你认为这种分离不会带来直接利益,那么违反它是可以的。

但是假设你想把责任分开。我知道你的解决方案不是最优的。

  • 首先,它仍然需要知道在CMatcher中获得C时aKey的参与。你为什么不通过IEnumerable<B>呢?
  • 第二,您更改BsProcessor的类签名,从最初提供BProcess到现在您通过工厂生成。我不知道工厂带来了什么好处,如果是合理的,那就继续吧。但如果不是,为什么不使用初始签名呢?你可以:1。在每个B类中添加C属性,采用OO方式引入耦合,或者引入另一个类,同时持有BC,并将该类传递给BProcessor