C# - 当对象更改状态等内容时,是否应该负责创建历史对象?

发布于 2024-09-09 11:39:25 字数 1693 浏览 4 评论 0原文

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

这是一个示例 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 方法中额外。然后该函数可以使用历史管理器来创建记录。

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

This is more of an architecture/best practices question than anything else, so please feel free to add your two cents. I know i stated status in the title, but this goes for any basic property of an object. I think the account example below will help demonstrate my question a little better than status.

Here is a sample Account object:

public class Account
{
   private IList<Transaction> _transactions;

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

Now lets say I want to start keeping a history of 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 of the AddTransaction() method like this

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

Now the next part is where the problem starts to arise. Suppose I want to do a bulk posting and I want to retain a record of which accounts were changed in this bulk posting for something like a report or if I needed to undo it later. So I would create an object like this.

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

From here I see a few different options to handle this since it can't be handled by the example code above.

1) Create a function in a Service type object that loops through a list of accounts calling the AddTransaction() method and also creating history records tied to a 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 into the AddTransaction method along with the transaction to be added. Then the function could use the history manager to create the records.

Ok this post is long enough. If i've not been clear enough let me know. Thanks for you input.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

卖梦商人 2024-09-16 11:39:27

我在某些方面同意Justin的回答,但OP上的标签之一是POCO;将事件添加到 Account 类会在某种程度上取消 POCO 您的 POCO。

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

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

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

I agree with Justin's answer in some ways, but one of the tags on the OP is POCO; adding an event to the Account class would in some ways un-POCO your POCO.

If you're into AOP and other such, you could use interception (most IoC frameworks, including Unity and Castle offer this functionality) to grab transactions of interest.

The benefit of interception is that your Account class has no coupling whatsoever with the AccountHistory class, the interception is highly configurable according to whatever rules you want, and it is easily changed without forcing an application recompile (if you put AccountHistory into a different assembly with the interception handlers). By using interception you are making your code more focused on the business domain rather on what could be considered an infrastructure task (auditing).

Again, this is another alternative for your toolbox; if you don't need to serialize your POCO's over the wire for any reason, then implementing the Observer Pattern (GoF) through events as suggested by Justin may be a more light-weight approach.

慕烟庭风 2024-09-16 11:39:27

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

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

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

The gang of four seem to think so. Transactions, history tracking, and un-doing are all part of a command pattern contract. You can implement history with a stack. Here's a snippet of relevant code including the contract, note that not all methods are or have to 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);
        }
    }
}

Note that I also use a factory pattern.

成熟稳重的好男人 2024-09-16 11:39:26

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

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

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

更新

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

从复杂性和调试的角度来看,采用这两种方法都可以使事情更容易管理,但不允许使用多个记录器(例如事件)。

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

Your method might work just fine, but let me propose an alternative.

Why not add a TransactionAdded Event to the Account class.

You could then subscribe to the Event from (I'm guessing here) the HistoryGroup object so that a new AccountHistory object was added every time the Event fired.

UPDATE

As mentioned in the comments, another method of accomplishing the goal would be to have HistoryGroup implement an interface (ITransactionLogger or something similar) and then modify Account so that the ITransactionLogger dependency can be injected.

Going either of these routes makes things a little easier to manage from the complexity and debugging standpoint, but doesn't allow for multiple Loggers like Events.

That would make your code a little more flexible and at the same time allow other consumers interested in the TransactionAdded Event to subscribe.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文