我想知道这是否是单一责任原则的最佳方法。 此类的责任是提醒用户,由于不活动,帐户即将过期。
在30天不活动时,用户会收到一封电子邮件。 在45天不活动时,用户不会收到他们被禁用的电子邮件。
public class AccountReminder
{
private readonly IUnitOfWork _uow;
private readonly ISendExpiringEmail _sendExpiringEmail;
public AccountReminder(IUnitOfWork uow, ISendExpiringEmail sendExpiringEmail)
{
_uow = uow;
_sendExpiringEmail = sendExpiringEmail;
}
public void NotifyUpcomingExpiringAccounts()
{
// Establish dates
DateTime accountWarningDateTime = DateTime.Now.AddDays(-30).Date;
DateTime expirationDateTime = DateTime.Now.AddDays(-45).Date;
// Retrieve users whose accounts are either about to expire or have already expired
var users = _uow.UserRepository
.FindBy(element => (DbFunctions.TruncateTime(element.LastLoginDate) == accountWarningDateTime));
// Send a reminder
foreach (var user in users)
{
string htmlBody = "<html xmlns:v=\"urn:...xmlns=\"http://www.w3.org/TR/REC-html40\" >";
htmlBody += "<head><meta http-equiv=...</head>";
htmlBody += "<body>";
//following line for only outlook to display image
htmlBody += "<v:shape id=\>...</v:shape>";
htmlBody += "<table width='100%'...</table>";
htmlBody += "</body></html>";
AlternateView htmlView = AlternateView.CreateAlternateViewFromString(htmlBody, null, MediaTypeNames.Text.Html);
AlternateView plainView = AlternateView.CreateAlternateViewFromString(htmlBody, null, "text/plain");
LinkedResource pic1 = new LinkedResource("images/Logo.jpg", MediaTypeNames.Image.Jpeg);
pic1.ContentId = "Logo";
htmlView.LinkedResources.Add(pic1);
_sendExpiringEmail.SendTo(new[] { user.Email }, "EmailSubject", htmlView, plainView);
}
DisableExpiringAccounts(expirationDateTime);
}
private void DisableExpiringAccounts(DateTime expirationDateTime)
{
var expiredUsers = _uow.UserRepository.FindBy(element => element.Enabled && (DbFunctions.TruncateTime(element.LastLoginDate) <= expirationDateTime));
foreach (var user in expiredUsers)
{
user.Enabled = false;
}
if (expiredUsers.Count > 0)
{
_uow.SaveChanges();
}
}
}
答案 0 :(得分:1)
我认为您在太高层次上解释责任。这是一个常见问题。罗伯特马丁建议解释它like this:
单一责任原则(SRP)规定每个软件模块应该只有一个改变的原因
以及(同一链接):
当您编写软件模块时,您希望确保在请求更改时,这些更改只能来自一个人,或者更确切地说,来自一个紧密耦合的人群,代表一个狭义的业务功能。< / p>
在您的示例中,业务要求的更改可能会涉及:
帐户到期机制背后的逻辑
用于通知用户的通信渠道
消息的格式
所以我在这里至少可以看到3个职责,至少3个层次结构(名称只是示例):
IAccountExpirationAgent
- 验证帐户是否已过期的部分,如果是,则禁用它,可能只有一个实现
IAccountExpirationNotifier
- 发送电子邮件的类(例如AccountExpirationEmailNotifier
),或短信,或其他
IAccountExpirationMessageFormatter
- 格式化消息的类(例如AccountExpirationMessageHtmlFormatter
,AccountExpirationMessagePlainTextFormatter
)
分别
答案 1 :(得分:0)
确实,这个问题应该发布在codereview.stackexchange.com上,但我仍然想提供一些指示......
这可能是错的;但我解释单一责任委托人的方式是“一种方法应该有一份工作”。这使我们无法编写单一的600行方法来完成所有操作并且维护起来非常麻烦。
更小,更专注(单一责任)的方法更容易维护,因为它们使更大的应用程序更易于理解。
如果我要重写你的课程,我可能会这样做:
拆分电子邮件发送功能
拆分HTML正文生成;可能会将用户模型发送到Razor视图
public class AccountReminder
{
private readonly IUnitOfWork _uow;
private readonly ISendExpiringEmail _sendExpiringEmail;
public AccountReminder(IUnitOfWork uow, ISendExpiringEmail sendExpiringEmail)
{
_uow = uow;
_sendExpiringEmail = sendExpiringEmail;
}
public void NotifyUpcomingExpiringAccounts()
{
// Establish dates
DateTime accountWarningDateTime = DateTime.Now.AddDays(-30).Date;
DateTime expirationDateTime = DateTime.Now.AddDays(-45).Date;
// Retrieve users whose accounts are either about to expire or have already expired
var users = _uow.UserRepository
.FindBy(element => (DbFunctions.TruncateTime(element.LastLoginDate) == accountWarningDateTime));
foreach (var user in users)
{
SendReminder(user);
}
DisableExpiringAccounts(expirationDateTime);
}
private void SendReminder(User user)
{
// pass a model to a view to generate the HTML
// the razor implementation should also handle plain-text vs. html mail; you can have two separate templates
string htmlBody = MyRazorImplementation.GenerateBody(user);
_sendExpiringEmail.SendTo(new[] { user.Email }, "EmailSubject", htmlView, plainView);
}
private void DisableExpiringAccounts(DateTime expirationDateTime)
{
var expiredUsers = _uow.UserRepository.FindBy(element => element.Enabled && (DbFunctions.TruncateTime(element.LastLoginDate) <= expirationDateTime));
foreach (var user in expiredUsers)
{
user.Enabled = false;
}
if (expiredUsers.Count > 0)
{
_uow.SaveChanges();
}
}
}