[英]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
与实体B
和C
均为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
的密钥一起可以将B
与C
关联。
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
包含多个方法,并且不需要将C
与B
匹配,因此使用构造函数注入不是很好,而方法注入使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.
我发现您的解决方案不是最佳的。
C
in CMatcher
. C
的CMatcher
。 Why don't you pass IEnumerable<B>
instead? IEnumerable<B>
呢? 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. 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
. B
类的每个类中添加C
属性,这是OO方式并引入耦合,或者引入另一个包含B
和C
的类,然后将该类传递给BProcessor
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.