Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/324.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 坚实的原则以及如何实际实施这些原则_C#_Dependency Injection_Solid Principles - Fatal编程技术网

C# 坚实的原则以及如何实际实施这些原则

C# 坚实的原则以及如何实际实施这些原则,c#,dependency-injection,solid-principles,C#,Dependency Injection,Solid Principles,最近,我一直在修补TDD和基于可靠原则的编码。我有一个场景如下: var marker = new TransactionRecurringPaymentMarkAsFailureDecorator( new RecurringPaymentMarkAsFailure( /* dependencies */ )); 您可以使用IRecurringProfile,它每隔一段时间(例如每月)执行一系列付款 当付款尝试通过但失

最近,我一直在修补TDD和基于可靠原则的编码。我有一个场景如下:

var marker =
    new TransactionRecurringPaymentMarkAsFailureDecorator(
        new RecurringPaymentMarkAsFailure(
            /* dependencies */    
        ));
  • 您可以使用
    IRecurringProfile
    ,它每隔一段时间(例如每月)执行一系列付款
  • 当付款尝试通过但失败时,将创建一个链接到
    IRecurringProfilePayment
    IRecurringProfileTransaction
    ,以显示付款未通过
  • 付款的失败计数将增加
  • 重试付款并发送另一个失败通知(如果付款失败)的日期/时间也会更新
  • 如果支付的失败计数达到最大阈值(例如3次失败的尝试),则将暂停
    IRecurringProfile
  • 此外,每次失败时,都会发送一个通知,通知客户付款未通过,并将再次重试
下面是我创建的一些示例代码,主要处理将定期概要文件付款标记为失败的任务。我试图遵循坚实的原则,以及构造函数注入。我想知道这是否违反了这些原则或任何编程最佳实践,并对代码进行任何形式的审查,以便对其进行改进。该代码还使用NHibernate作为ORM

public class RecurringPaymentMarkAsFailure
{
    private readonly IPaymentFailedNotificationSender paymentFailedNotificationSender;
    private readonly IRecurringProfileFailureNextNotificationDateUpdater failureNotificationDateUpdater;
    private readonly IRecurringProfileSuspender recurringProfileSuspender;

    public RecurringPaymentMarkAsFailure(IPaymentFailedNotificationSender paymentFailedNotificationSender, IRecurringProfileSuspender recurringProfileSuspender,
        IRecurringProfileFailureNextNotificationDateUpdater failureNotificationDateUpdater)
    {
        this.paymentFailedNotificationSender = paymentFailedNotificationSender;
        this.failureNotificationDateUpdater = failureNotificationDateUpdater;

        this.recurringProfileSuspender = recurringProfileSuspender;

    }

    private void checkProfileStatus(IRecurringProfile profile)
    {
        if (profile.Status != Enums.RecurringProfileStatus.Active)
        {
            throw new Exceptions.RecurringProfileException("This cannot be called when the profile is not marked as active");
        }
    }


    private void incrementFailureCount(IRecurringProfilePayment payment)
    {
        payment.FailureCount++;
    }

    public IRecurringProfileTransaction MarkPaymentAsFailed(IRecurringProfilePayment payment, string failureData)
    {
        using (var t = BeginTransaction())
        {
            checkProfileStatus(payment.RecurringProfile);


            IRecurringProfileTransaction transaction = payment.Transactions.CreateNewItem();
            transaction.OtherData = failureData;
            transaction.Status = Enums.RecurringProfileTransactionStatus.Failure;
            paymentFailedNotificationSender.CreateAndQueueNotification(transaction);
            failureNotificationDateUpdater.UpdateNextFailureNotificationDate(payment);
            incrementFailureCount(payment);

            if (payment.FailureCount >= payment.RecurringProfile.MaximumFailedAttempts)
            {
                recurringProfileSuspender.SuspendRecurringProfile(payment.RecurringProfile);
            }
            transaction.Save();
            t.Commit();
            return transaction;
        }

    }

}
--


作为旁注,这个问题补充了我最近关于类似主题的帖子。

在我看来,你违反了单一责任原则,因为你的
重复支付MarkasFailure
有两个责任。除了将付款视为失败的责任外,它还增加了管理交易的责任(使用(BeginTransaction);这是一个贯穿各领域的问题

在系统中,您可能会有许多这样的类来处理业务逻辑,而且可能是全部(或许多)考虑到这个完全相同的事务代码,而应考虑通过允许将此行为添加为装饰器来坚持打开/关闭的原理。这是很可能的,因为事务是这个代码中的第一个也是最后一个操作。这个装饰器的一个天真的实现可以是这样的:

public class TransactionRecurringPaymentMarkAsFailureDecorator
    : RecurringPaymentMarkAsFailure
{
    private RecurringPaymentMarkAsFailure decoratedInstance;

    public RecurringPaymentMarkAsFailure(
        RecurringPaymentMarkAsFailure decoratedInstance)
    {
        this.decoratedInstance = decoratedInstance;
    }

    public override IRecurringProfileTransaction MarkPaymentAsFailed(
        IRecurringProfilePayment payment, string failureData)
    {
        using (var t = BeginTransaction())
        {
            var transaction = this.decoratedInstance
                .MarkPaymentAsFailed(payment, failureData);

            t.Commit();

            return transaction;
        }
    }
}
此装饰器允许您按如下方式包装类:

var marker =
    new TransactionRecurringPaymentMarkAsFailureDecorator(
        new RecurringPaymentMarkAsFailure(
            /* dependencies */    
        ));
正如我所说的,这个实现有点幼稚,因为您可能会有许多需要包装的类,这意味着每个类都会有自己的decorator


相反,让所有执行用例的类实现一个通用接口,类似于
ICommandHandler
。这允许您创建一个通用
TransactionCommandlerDecorator
类来包装所有这些实例。请看这篇文章以了解有关此模型的更多信息:。

这两个接口是什么职责?这篇链接的文章读得很好-我目前正在研究如何为发布的代码实现一个通用的装饰器模式。第一个职责是实际的业务逻辑。另一个职责是运行数据库事务中的所有内容(这是一个交叉关注点)。更改事务策略并不是在更改业务逻辑的同时进行更改。我一直在尝试decorator&command方法,这是有意义的。但是,我的主要问题是,如果存在乐观并发异常,在这种情况下,我通常要做的是刷新数据库项,然后执行相同的代码。我尝试创建了一个
dboptimisticconcurrentyretrycorator
,它可以工作。但是,当它调用
MarkAsFailureCommand.Handle()时
,由于乐观并发错误,这无法知道它需要刷新数据库项。有没有办法解决这个问题?@Steven,你的博客太棒了。非常感谢!@KarlCassar:对我来说,这似乎是过早的优化。在过去的几年中,我体验到命令的性能是h从来都不是问题。在你的情况下,你想要什么,一个快速的系统还是一个正确的系统?我打赌它会足够快。此外,从业务层的角度来看,这只是巧合,这些数据已经加载。关于在使用id时失去编译时支持,有一些简单的方法可以解决这个问题。例如,使用cons接受实体但存储其id的tructor。