简体   繁体   English

为更改的工作流程传递其他数据的更好方法是哪种?

[英]Which is the better way to pass additional data for a changed workflow?

Let entity A relates to entities B and C both as 1:1..* 设实体A与实体BC均为1:1 .. *
At the same time B contains only part of C 's key, but together with A 's key it is possible to relate B to C . 同时, B仅包含C的密钥的一部分,但是与A的密钥一起可以将BC关联。

Translation to human language: 翻译成人类语言:
Accounting Center (A) consists of Branches (C), and a stream of Payments (B) comes to the system from Accounting Center and contains BranchId which is unique only within a given Accounting Center. 计费中心(A)由分支(C)组成,并且付款流(B)从计费中心进入系统,并包含BranchId,该ID仅在给定的计费中心内是唯一的。 Wee need to check the correctness of provided BranchId and tie the Payment to the Branch.) 我们需要检查所提供的BranchId的正确性,并将付款与分支联系起来。)

We process the collection of B s: 我们处理B的集合:

class BsProcessor
{
    private BProcessor _processor;

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


One day requirements change, and now BProcessor needs to make a decision based on corresponding C . 一天的需求发生了变化,现在BProcessor需要基于相应的C做出决定。

From the view of performance it's better to get in advance all C s for the A s which B s point to, and then pass this data to BProcessor via changing Process method signature. 从性能的角度来看,最好预先获取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 then tries to find the matching C . 然后BProcessor尝试找到匹配的C

This is a straightforward fast to code and simple working solution... 这是一种直接的快速编码和简单的工作解决方案。
To be honest I don't like it wholeheartedly. 老实说,我不是全心全意地喜欢它。 I think it violates SRP. 我认为它违反了SRP。
There is no any other reason but performance for the BsProcessor to be aware of Cs . 除了性能之外,没有任何其他原因使BsProcessor了解Cs

The logic of finding C candidates and matches belongs neither to BsProcessor nor to BProcessor , but a dedicated service CMatcher 发现的逻辑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)
    {
    } 
}

This service should be injected and used by BProcessor . 此服务应由BProcessor注入和使用。 As this service is contextual and requires collection of aKeys we need to switch to the factory: 由于此服务是上下文相关的,并且需要收集aKey,因此我们需要切换到工厂:

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

I think this solution is a bit more complex to code, but easier to test. 我认为此解决方案的代码稍微复杂一点,但更易于测试。 The only problem I have is that BProcessor contains more than one method, and those do not require to match C to B , hence it is not good to use constructor injection, whereas method injection makes BsProcessor know about CMatcher what is somewhat similar to initial solution. 我唯一的问题是BProcessor包含多个方法,并且不需要将CB匹配,因此使用构造函数注入不是很好,而方法注入使BsProcessor知道CMatcher与初始解决方案有些相似。

Here it starts looking like BProcessor begs for being divided into separate classes, and I ask myself whether SRP is worth so much refactoring and where second approach is indeed better than the first one? 在这里,它开始看起来就像BProcessor乞求将其划分为不同的类一样,我问自己是否SRP值得进行如此大量的重构,并且第二种方法确实比第一种更好?

The trick, I think, is to take the first approach, but pass in the CProvider during processing, rather than containing the CProvider at all times. 我认为,诀窍是采用第一种方法,但在处理过程中传递 CProvider ,而不是CProvider 包含 CProvider

Also, depending on what else is involved in processing, you may not need to have BProcessor / BsProcessor classes at all; 此外,根据其它什么是参与处理,你可能不需要有BProcessor / BsProcessor班所有; simply let each B be capable of processing itself, given a CProvider to find the corresponding C with. 给定一个CProvider来查找对应的C ,只需让每个B都能够处理自身即可。

Here's a quick and dirty example, using your accounting analogy above. 这是一个快速而肮脏的示例,使用上面的会计类比。 Fair warning, I'm not validating much data, and I might be making bad assumptions about the requirements of the structures. 合理的警告,我没有验证太多数据,并且可能对结构的要求做出了错误的假设。

// 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 is just a guidance, and it can be safely violated under concise decision. SRP只是一个指导,可以根据简要的决定安全地违反它。 So if you don't think that the separation does not give direct benefit, it is fine to violate it. 因此,如果您不认为分离不会带来直接收益,则可以违反分离。

But let's say that you want to separate the responsibility though. 但是,假设您要分离责任。 I see that your solution isn't optimal. 我发现您的解决方案不是最佳的。

  • First, it still need to know the involvement of aKey during getting the C in CMatcher . 首先,它仍然需要知道aKey在CMatcher中获取CCMatcher Why don't you pass IEnumerable<B> instead? 为什么不通过IEnumerable<B>呢?
  • Second, you change the BsProcessor 's class signature, from originally provide them with BProcess now you are generating via a factory. 其次,你改变BsProcessor的类签名,由原来他们提供BProcess现在你通过一个工厂产生。 I don't know what the benefit the factory bring, if it is justifiable then go on. 我不知道工厂能带来什么好处,如果有正当理由,那就继续吧。 But if not, why not use the initial signature? 但是,如果不是,为什么不使用初始签名? You can: 1. Add C property in each of B class, which is OO way and introduce coupling, or introduce another class which hold B and C , and pass the class into the BProcessor . 您可以:1.在B类的每个类中添加C属性,这是OO方式并引入耦合,或者引入另一个包含BC的类,然后将该类传递给BProcessor

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM