这是为已更改的工作流传递附加数据的更好方法
本文关键字:数据 方法 更好 工作流 | 更新日期: 2023-09-27 18:03:58
设实体A
与实体B
、C
的关系均为1:1. *
同时,B
只包含C
的部分键,但结合A
的键,可以将B
与C
联系起来。
翻译成人类语言:
会计中心(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
包含多个方法,而那些不需要将C
与B
匹配,因此使用构造函数注入是不好的,而方法注入使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方式引入耦合,或者引入另一个类,同时持有B
和C
,并将该类传递给BProcessor
。