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

时间:2015-07-21 21:33:53

标签: c# dependency-injection architecture argument-passing single-responsibility-principle

让实体A与实体BC相关,均为1:1 .. *
同时B仅包含C个密钥的一部分,但与A密钥一起,可以将BC相关联。

翻译为人类语言:
会计中心(A)由分支(C)组成,支付流(B)从会计中心到达系统,并包含BranchId,BranchId仅在给定的会计中心内是唯一的。我们需要检查提供的BranchId的正确性,并将付款与分支机构联系起来。)

我们处理B s:

的集合
class BsProcessor
{
    private BProcessor _processor;

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


有一天需求变更,现在BProcessor需要根据相应的C做出决定。

从性能的角度来看,最好事先获得C所指向的A的所有B s,然后通过更改{{}将此数据传递给BProcessor。 1}}方法签名。

Process

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

这是一个简单快速的代码和简单的工作解决方案...
说实话,我不是全心全意地喜欢它。我认为它违反了SRP 除了C 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注入和使用。由于此服务是上下文的,需要收集aKeys,我们需要切换到工厂:

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 begs被分成不同的类,我问自己SRP是否值得这么多重构,第二种方法确实比第一种更好?

2 个答案:

答案 0 :(得分:1)

我认为,诀窍是采用第一种方法,但在处理期间传递 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...
    }
}

答案 1 :(得分:1)

SRP只是一个指导,在简明决定下可以安全地违反。因此,如果您不认为分离不会带来直接利益,那么违反它就可以了。

但是,让我们说你想分开责任。我发现你的解决方案并不是最优的。

  • 首先,在user@Mars:~/Documenti/Bello/NVIDIA_CUDA-7.0_Samples/0_Simple/matrixMul$ nvcc matrixMul.cu matrixMul.cu:36:30: fatal error: helper_functions.h: File o directory non esistente #include <helper_functions.h> ^ compilation terminated.获取C期间,仍需要知道aKey的参与情况。为什么不通过CMatcher
  • 其次,您更改IEnumerable<B>的班级签名,从最初为他们提供BsProcessor现在您通过工厂生成。我不知道工厂带来了什么好处,如果合理的话再继续下去。但如果没有,为什么不使用初始签名?您可以:1。在每个BProcess类中添加C属性,这是OO方式并引入耦合,或者引入另一个包含BB的类,并通过该课程进入C