C # - Should an object be responsible for creating a history object when it changes something like status?

It is more a matter of architecture / best practice than anything else, so please feel free to add your two cents. I know that I indicated the status in the title, but this applies to any basic property of the object. I think the example account below will help demonstrate my question a little better than status.

Here is an example Account object:

public class Account { private IList<Transaction> _transactions; public AddTransaction(trans as Transaction) { _transaction.add(trans) } } 

Now let's say that I want to start logging every time a transaction is added with this object.

 public class AccountHistory { private DateTime _historyDate; private String _details; public AccountHistory(string details) { _historyDate = DateTime.Now; _details = details; } } 

At this level, what I would normally do is add a collection of history events to the account object, and also add a line of code to create a history event inside the AddTransaction () method like this

 public AddTransaction(trans as Transaction) { _transaction.add(trans); **_historyEvents.add(new AccountHistory("Transaction Added: " + trans.ToString());** } 

Now in the next part there is a problem. Suppose I want to make a bulk posting, and I want to keep a record of which accounts were changed in this bulk posting for something like a report, or if I needed to cancel it later. Therefore, I would create such an object.

 public class HistoryGroup() { private IList<AccountHistory> _events; } 

Here I see several different parameters for handling this, since it cannot be handled by the above example.

1) Create a function in the object of the service type, which iterates over the list of accounts that call the AddTransaction () method, and also creates history records associated with the HistoryGroup

  public void AddTransactions(IList<Account> accounts, Transaction trans) { HistoryGroup history = new HistoryGroup(); for (int x=0;x <=accounts.Count - 1; x++) { accounts(x).AddTransaction(trans); history.AddEvent(new AccountHistory("Added Transaction: " + trans.ToString(); } } 

2) Pass some type of HistoryManager object to the AddTransaction method along with the transaction to be added. The function can then use the history manager to create entries.

Ok, this post is long enough. If I was not clear enough, let me know. Thanks for your input.

+4
source share
3 answers

Your method may work very well, but let me suggest an alternative.

Why not add a TransactionAdded Event to the Account class.

You can then subscribe to the Event from (History object) so that a new AccountHistory object is added each time the event is triggered.

UPDATE

As mentioned in the comments, another way to achieve the goal would be to have a HistoryGroup implement the interface (ITransactionLogger or something similar), and then change the Account so that the ITransactionLogger dependency can be introduced.

Switching to any of these routes simplifies management in terms of complexity and debugging, but does not allow the use of several Loggers, for example Events.

This will make your code a little more flexible and at the same time allow other users interested in signing TransactionAdded to sign up.

+9
source

I agree with Justin's answer in a way, but one of the tags on OP is POCO; adding an event to the account class will somehow disable your POCO's POCO.

If you end up in AOP and others like that, you can use interception (most IoC infrastructures, including Unity and Castle, offer this feature) to capture transactions of interest.

The advantage of interception is that your Account class has nothing to do with the AccountHistory class, the interception is configured according to any rules you want, and it can be easily changed without forcing the application to recompile (if you put AccountHistory in another assembly with interception handlers) . Using interception, you make your code more focused on the business domain, rather than on what can be considered an infrastructure task (audit).

Again, this is another alternative for your toolkit; if you don’t need to serialize your POCO by wire for any reason, then implementing an observer pattern (GoF) through events, as Justin suggests, can be easier.

+2
source

A gang of four seems to think so. Transactions, history tracking and non-execution are part of the team template . You can implement a story with a stack. Here is a snippet of the relevant code, including the contract, note that not all methods are implemented or should be implemented:

 public interface ICommand { void execute(); void undo(); void store(); void load(); } public class ManagerMacro : ICommand { List<ICommand> Commands; Stack commandStack; /// <summary> /// Use in combination with AddSteps /// </summary> //public ManagerMacro() //{ //} public ManagerMacro(List<ICommand> commands) { this.Commands = commands; this.commandStack = new Stack(); } #region ICommand Members public void execute() { for (int i = 0; i < Commands.Count; i++) { commandStack.Push(Commands[i]); Commands[i].execute(); } } public void undo() { for (int i = 0; i < Commands.Count; i++) { if (commandStack.Count > 0) { ICommand Command = (ICommand)commandStack.Pop(); Command.undo(); } } } public void store() { throw new NotImplementedException(); } public void load() { throw new NotImplementedException(); } #endregion public void AddSteps(Steps[] steps) { foreach (Steps step in steps) { ICommand thisStep = null; switch (step) { case Steps.Manager1: thisStep = new Step1(); break; case Steps.Manager2: thisStep = new Step2(); break; case Steps.Manager3: thisStep = new Step3(); break; case Steps.Manager4: thisStep = new Step4(); break; } this.Commands.Add(thisStep); } } } 

Please note that I also use the factory pattern.

+1
source

Source: https://habr.com/ru/post/1315556/


All Articles