0

Soit entité A concerne les entités B et C tant que 1: 1 .. *
En même temps B ne contient qu'une partie de C « clé s, mais avec A » s touche, il est possible de relier B-C .Quelle est la meilleure façon de transmettre des données supplémentaires pour un flux de travail modifié?

La traduction en langage humain:
Le centre de comptabilité (A) se compose de branches (C), et un flux de paiements (B) provient du centre de comptabilité et contient BranchId qui est unique seulement dans un centre de comptabilité donné . . Wee besoin de vérifier l'exactitude de fourni BRANCHID et attacher le paiement à la Direction)

Nous traitons la collection de B s:

class BsProcessor 
{ 
    private BProcessor _processor; 

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


Une des exigences de jour changent, et maintenant BProcessor doit prendre une décision basée sur C correspondant.

Du point de vue de la performance, il est préférable d'obtenir à l'avance tous les C s pour les A s qui s point à B, puis transmettre ces données à BProcessor via la modification Process signature de la méthode.

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 essaie alors de trouver le C correspondant.

Ceci est une solution de travail simple et rapide à coder et simple ...
Pour être honnête, je ne l'aime pas du tout. Je pense que cela viole SRP.
Il n'y a pas d'autre raison que la performance pour le BsProcessor d'être au courant de Cs.

La logique de trouver C candidats et les matches appartient ni à BsProcessor ni à BProcessor, mais un service dédié CMatcher

class CMatcher 
{ 
    private CProvider _provider; 
    private IEnumerable<aKey> _aKeys; 

    public CMatcher(CProvider provider, IEnumerable<aKey> aKeys) 
    { 
     ... 
    } 

    public C Match(aKey akey, cPartialKeyFromB partialKey) 
    { 
    } 
} 

Ce service doit être injecté et utilisé par BProcessor. Comme ce service est contextuelle et nécessite la collecte de aKeys nous devons passer à l'usine:

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

Je pense que cette solution est un peu plus complexe à coder, mais plus facile à tester. Le seul problème que j'ai est que BProcessor contient plus d'une méthode, et ceux-ci ne doivent pas correspondre C à B, par conséquent, il n'est pas bon d'utiliser injection de constructeur, alors que l'injection de méthode fait BsProcessor savoir environ CMatcher ce qui est quelque peu similaire à initial Solution.

Ici, il commence à ressembler à BProcessor mendie être divisé en classes séparées, et je me demande si SRP vaut tant refactoring et où la deuxième approche est en effet mieux que le premier?

Répondre

1

L'astuce, je pense, est de prendre la première approche, mais passe dans leCProvider au cours du traitement, plutôt que contenant la CProvider en tout temps.

En outre, en fonction de ce qui est impliqué dans le traitement, vous n'avez pas besoin de classes BProcessor/BsProcessor; laissez simplement chaque B être capable de se traiter, donné CProvider pour trouver le C correspondant avec.

Voici un exemple rapide et sale, en utilisant votre analogie comptable ci-dessus. Juste avertissement, je ne suis pas en train de valider beaucoup de données, et je pourrais faire de mauvaises suppositions sur les exigences des structures. SRP est juste un guide, et il peut être violé en toute sécurité en vertu d'une décision concise.

// 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... 
    } 
} 
+0

Ce que vous proposez est un DDD. Je doute que l'équipe accepte facilement ce changement de paradigme du script de transaction. = (Néanmoins, merci pour la réponse.) –

1

Donc, si vous ne pensez pas que la séparation ne procure aucun avantage direct, il est bon de la violer.

Mais disons que vous voulez séparer la responsabilité. Je vois que votre solution n'est pas optimale.

  • D'abord, il faut encore savoir l'implication de aKey pendant obtenir le C dans CMatcher. Pourquoi ne pas passer IEnumerable<B> à la place?
  • Deuxièmement, vous modifiez la signature de classe de BsProcessor, de leur fournir à l'origine BProcess maintenant que vous générez via une usine. Je ne sais pas quel avantage l'usine apporte, si c'est justifiable alors continuez. Mais sinon, pourquoi ne pas utiliser la signature initiale? Vous pouvez: 1. Ajouter la propriété C dans chacune des classes B, qui est de type OO et introduire le couplage, ou introduire une autre classe qui contient B et C et transmettre la classe au BProcessor.
+0

Je pensais à passer des Bs à l'usine, mais il y a un autre conseil pour exiger le minimum nécessaire pour effectuer l'opération: –

+0

Probablement, je ne comprends pas bien, mais la touche B au C est reçu de l'événement externe qui active le traitement du B. De plus, B lui-même est créé à partir de cet événement.Puis nous avons besoin de résoudre le C. –

+0

@voroninp Droit de demeter droit? Voir http: // programmeurs. stackexchange.com/a/284146 pour une meilleure explication – Fendy