4

这更像是一个架构/最佳实践问题,所以请随意添加您的两分钱。我知道我在标题中声明了状态,但这适用于对象的任何基本属性。我认为下面的帐户示例将有助于证明我的问题比状态更好。

这是一个示例 Account 对象:

public class Account
{
   private IList<Transaction> _transactions;

   public AddTransaction(trans as Transaction)
   {
      _transaction.add(trans)
   }
}

现在假设我想开始记录每次使用此对象添加事务时的历史记录。

public class AccountHistory
{
   private DateTime _historyDate;
   private String _details;

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

在这个级别上,我通常会向帐户对象添加一组历史事件,并添加一行代码在 AddTransaction() 方法中创建一个历史事件,如下所示

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

现在下一部分是问题开始出现的地方。假设我要进行批量发布,并且我想保留在此批量发布中更改了哪些帐户的记录,以用于报告之类的事情,或者我以后需要撤消它。所以我会创建一个这样的对象。

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

从这里我看到了一些不同的选项来处理这个问题,因为上面的示例代码无法处理它。

1) 在 Service 类型对象中创建一个函数,该函数遍历调用 AddTransaction() 方法的帐户列表,并创建与 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) 将某种类型的 HistoryManager 对象与要添加的事务一起传递到 AddTransaction 方法中。然后该函数可以使用历史管理器来创建记录。

好的,这篇文章足够长了。如果我不够清楚,请告诉我。感谢您的输入。

4

3 回答 3

10

你的方法可能工作得很好,但让我提出一个替代方案。

为什么不将 TransactionAdded 事件添加到 Account 类。

然后,您可以从(我在这里猜测)HistoryGroup 对象订阅事件,以便在每次触发事件时添加一个新的 AccountHistory 对象。

更新

正如评论中提到的,实现目标的另一种方法是让 HistoryGroup 实现一个接口(ITransactionLogger 或类似的东西),然后修改 Account 以便可以注入 ITransactionLogger 依赖项。

从复杂性和调试的角度来看,采用这些路线中的任何一条都会使事情变得更容易管理,但不允许像事件这样的多个 Logger。

这将使您的代码更加灵活,同时允许其他对 TransactionAdded 事件感兴趣的消费者订阅。

于 2010-07-13T16:25:37.307 回答
2

我在某些方面同意贾斯汀的回答,但 OP 上的标签之一是 POCO;在 Account 类中添加一个事件会在某些方面取消 POCO 您的 POCO。

如果您喜欢 AOP 等,您可以使用拦截(大多数 IoC 框架,包括 Unity 和 Castle 提供此功能)来获取感兴趣的事务。

拦截的好处是您的 Account 类与 AccountHistory 类没有任何耦合,拦截可以根据您想要的任何规则进行高度配置,并且可以轻松更改而不强制重新编译应用程序(如果您将 AccountHistory 放入不同的程序集中拦截处理程序)。通过使用拦截,您可以使您的代码更加专注于业务领域,而不是可能被视为基础设施任务(审计)。

同样,这是您工具箱的另一种选择;如果您出于任何原因不需要通过网络对 POCO 进行序列化,那么按照 Justin 的建议通过事件实现观察者模式 (GoF) 可能是一种更轻量级的方法。

于 2010-07-13T16:39:36.347 回答
1

四人组似乎也是这么想的。交易、历史跟踪和撤销都是命令模式合约的一部分。您可以使用堆栈实现历史记录。这是包含合同的相关代码片段,请注意并非所有方法都已实现或必须实现:

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

请注意,我也使用工厂模式。

于 2010-07-13T16:48:46.277 回答