gpt4 book ai didi

c# - SOLID 原则以及如何实际实现它们

转载 作者:行者123 更新时间:2023-11-30 16:21:48 24 4
gpt4 key购买 nike

最近我一直在研究 TDD 和基于 SOLID 原则的编码。我有一个场景如下:

  • 一个人可以有 IRecurringProfile,它按一定的时间间隔执行一系列付款,例如每月
  • 当付款尝试通过但失败时,将创建一个链接到 IRecurringProfilePaymentIRecurringProfileTransaction 以表明付款没有通过。
  • 支付的失败计数增加。
  • 重试付款和发送另一个失败通知(如果付款失败)的日期/时间也会更新。
  • 如果支付失败次数达到最大阈值(例如 3 次失败尝试),IRecurringProfile 将被暂停。
  • 此外,对于每次失败,都会发送一条通知,告知客户付款未成功,并将再次重试。

下面是我创建的一些示例代码,主要处理将定期个人资料付款标记为失败的任务。我尝试遵循 SOLID 原则以及构造函数注入(inject)。想知道这是否违反了这些原则或任何编程最佳实践,并对代码进行任何形式的审查,以便对其进行改进。该代码还使用 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;
}

}

}

--

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

最佳答案

在我看来,您违反了单一职责原则,因为您的 RecurringPaymentMarkAsFailure有两个职责。除了将支付视为失败的责任外,它还增加了管理交易的责任(using (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 */
));

正如我所说,这个实现有点天真,因为您可能有许多需要包装的类,这意味着每个类都会有自己的装饰器。虽然这是完全 SOLID,但它不是 DRY。

取而代之的是,让所有执行用例的类都实现一个通用接口(interface),比如ICommandHandler<TCommand>。 .这允许您创建一个通用的 TransactionCommandHandlerDecorator<TCommand>类来包装所有这些实例。查看本文以了解有关此模型的更多信息:Meanwhile… on the command side of my architecture .

关于c# - SOLID 原则以及如何实际实现它们,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12929547/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com