让实体A
与实体B
和C
相关,均为1:1 .. *
同时B
仅包含C
个密钥的一部分,但与A
密钥一起,可以将B
与C
相关联。
翻译为人类语言:
会计中心(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
包含多个方法,而且不需要将C
与B
匹配,因此使用构造函数注入并不好,而方法注入让BsProcessor
了解CMatcher
与初始解决方案有些相似之处。
这里开始看起来像BProcessor
begs被分成不同的类,我问自己SRP是否值得这么多重构,第二种方法确实比第一种更好?
答案 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方式并引入耦合,或者引入另一个包含B
和B
的类,并通过该课程进入C
。