- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我最近学习了SOLID开发,现在我面临着一些挑战,那就是什么时候是好的习惯,什么时候不是。
例如,我开发了一个包含成员的网站。我构建了一个Authentication Business Logic类,该类应确定身份验证方案,这些方案是:
登录用户
获取登录用户
注册用户
恢复用户密码
此类具有4个依赖项:
数据库/服务
HttpContext(用于用户状态)
ValidationRules(一些其他业务逻辑规则,以便登录用户)
SMTP-用于发送
现在,由于未使用依赖项注入,因此感觉有些代码有异味。
我不确定的一些问题:
我可以直接将SMTP依赖项注入到“还原密码和注册”方法中,因为所有其他方法均与此无关。我是不是该?
某些方法(例如注销)没有使用数据库,为什么当我知道不需要使用数据库依赖项时为什么要启动数据库依赖项。
这是针对我的一些业务逻辑。在我的控制器中,这个问题要大得多,因为我需要加载的负载超过一个BL,并且我的所有业务类都具有相同的难闻气味。
我觉得我做错了,请帮忙!
最佳答案
认证业务逻辑类,应确定
身份验证方案,包括:登录用户,获取登录用户,
注册用户,找回用户密码。
您的身份验证业务逻辑类已经违反了Single Responsibility Principle。尽管您可能认为该类的唯一职责是“身份验证”,但这几乎不能称为一种职责,因为您可以实现数百个用于身份验证的用例,从而导致一个丑陋的大屁股类。那个班级还会负有责任吗?而是,为每个类实现一个用例。在这种情况下,您的课程将具有非常明确和狭窄的职责。
HttpContext(用于用户状态)
您的业务逻辑应该对所使用的技术一无所知(当然,除了它是.NET之外,我们不能真正抽象出.NET),因此它不应依赖于HttpContext
。取决于HttpContext
违反了Dependency Inversion Principle(DIP)和Interface Segregation Principle(ISP)。
DIP说:“高级模块应该……取决于抽象。”您的业务层是“较高级别的模块”,但它不依赖于抽象,而是依赖于较低级别的模块(HttpContext)。这将业务层类与实际的Web逻辑紧密结合在一起。
此外,即使您使用System.Web.HttpContextBase
抽象,您仍将依赖于由较低层模块定义的抽象,而根据DIP,“抽象归上层/策略层所有”。其背后的想法是,您不希望您的代码对较低的层有强烈的依赖性,因为这会使较高层的层更依赖于此,并且除此以外,较低层的层无法定义以下抽象:为您的应用程序正确。接下来表达这个问题。
ISP指出“不应强迫任何客户端依赖其不使用的方法”。换句话说,抽象应该针对客户的特殊需求而定制,并且应该狭窄。 “这些缩小的接口也称为角色接口”。 HttpContext
和HttpContextBase
都违反了ISP,因为它们很宽并且可以用于一般用途。它们是一个大问题,其中一切都是字符串。这使您的代码可以使用非常广泛的API,这使该API难以使用。这也阻碍了可测试性,因为不可能伪造HttpContext
,甚至抽象的HttpContextBase
都不容易伪造。如果您使用ISP,则您的生产代码和测试代码都将变得更加干净。这也阻碍了应用程序的可重用性,因为您可能希望稍后在没有Web请求概念的后台进程中运行业务逻辑的某些部分。而且它妨碍了灵活性,因为您可能想一次拦截或修饰在该HttpContext
上执行的某些操作。例如,您可能想在HttpContext请求用户详细信息时记录日志。但是,当您的代码直接依赖于HttpContext时,这意味着您被迫在整个代码库中进行全面更改。必须在整个代码中进行大范围更改,这表明您违反了Open/Closed Principle。
而是在BL中定义您自己的IUserContext
抽象,并在您的Web应用程序中创建一个AspNetUserContext
实现(实现IUserContext
)。请注意,您不应该定义IHttpContext
抽象,因为它仍然会违反SRP和ISP,并且基本上是泄漏抽象(泄漏抽象是DIP违规),因为业务层不必了解网络请求的存在。
我可以直接注入SMTP依赖项
这样做时,您将再次违反Dependency Inversion Principle。您的业务层不依赖于抽象,而是依赖于较低级别的模块(您的SMTP类)。这将业务层类与实际的SMTP逻辑紧密结合在一起。相反,您应该为此定义自己的抽象,例如IMailSender
。
这听起来可能很奇怪,您可能会觉得您永远都不会更改邮件的发送方式,因为电子邮件是唯一的方式。但是,即使电子邮件已经存在了很多年,您仍可能希望更改电子邮件的处理方式,因为在当前的实现中,电子邮件不会随业务交易自动发送。这意味着您可能已将邮件推送到SMTP服务器(无法回滚的操作),但是此后总操作仍然可能失败。如果失败,则意味着回滚打开的数据库事务,但此时仍将发送邮件。您无法将其回叫,最终将在以后再次发送该邮件。这将使您的接收者感到烦恼,甚至可能使您发送事务回滚后不再存在的信息(例如包含数据库ID的url)。
为了减轻这种情况,您将必须将邮件写入事务性队列(例如,带有应发送邮件的数据库表)。此操作应在与业务逻辑运行相同的事务中运行。这意味着您可以在同一事务中对多个消息进行排队,并且当操作失败时,它们都会全部回滚。操作成功后,所有消息将变为可用,并且某些其他(后台)进程可以将其提取并发送。
这只是一种可能的方式。邮件的发送方式将来可能会更改,但是如果没有IMailSender
抽象,则将很难解决。
某些方法(例如注销)没有使用数据库,为什么我应该
当我知道不需要使用它时,请启动db依赖项。
通常,当不总是使用依赖项时,这不是问题,因为创建对象图(具有所有直接和间接依赖项的服务)should be very fast。但是在您的情况下,您所看到的是您的方法不够紧密。这表明违反了单一责任。相反,您应该为Logout
操作提供自己的类。由于此操作不需要数据库,因此这里不需要数据库依赖项。
在我的控制器中,这个问题要大得多,因为我需要加载更多
那么一个BL和我所有的商务舱都有同样的难闻的气味。
您的控制器可能也违反了“单一责任原则”。 SRP违规的一个很好的指示是类具有的依赖项数量。启发式方法是4或5。具有5个以上的依赖项,您的类很可能承担太多职责。
当您遵循SOLID时,您将获得许多小型且专注的课程。乍看起来,这似乎需要大量工作和代码,但实际上,这会使系统更易于维护。如果您在为如何创建业务逻辑而苦苦挣扎,请查看this article。
关于.net - 现实生活与SOLID开发一起工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19608208/
我在Windows 10中使用一些简单的Powershell代码遇到了这个奇怪的问题,我认为这可能是我做错了,但我不是Powershell的天才。 我有这个: $ix = [System.Net.Dn
var urlsearch = "http://192.168.10.113:8080/collective-intellegence/StoreClicks?userid=" + userId +
我有一个非常奇怪的问题,过去两天一直让我抓狂。 我有一个我试图控制的串行设备(LS 100 光度计)。使用设置了正确参数的终端(白蚁),我可以发送命令(“MES”),然后是定界符(CR LF),然后我
我目前正试图让无需注册的 COM 使用 Excel 作为客户端,使用 .NET dll 作为服务器。目前,我只是试图让概念验证工作,但遇到了麻烦。 显然,当我使用 Excel 时,我不能简单地使用与可
我开发了简单的 REST API - https://github.com/pavelpetrcz/MandaysFigu - 我的问题是在本地主机上,WildFly 16 服务器的应用程序运行正常。
我遇到了奇怪的情况 - 从 Django shell 创建一些 Mongoengine 对象是成功的,但是从 Django View 创建相同的对象看起来成功,但 MongoDB 中没有出现任何数据。
我是 flask 的新手,只编写了一个相当简单的网络应用程序——没有数据库,只是一个航类搜索 API 的前端。一切正常,但为了提高我的技能,我正在尝试使用应用程序工厂和蓝图重构我的代码。让它与 pus
我的谷歌分析 JavaScript 事件在开发者控制台中运行得很好。 但是当从外部 js 文件包含在页面上时,它们根本不起作用。由于某种原因。 例如; 下面的内容将在包含在控制台中时运行。但当包含在单
这是一本名为“Node.js 8 the Right Way”的书中的任务。你可以在下面看到它: 这是我的解决方案: 'use strict'; const zmq = require('zeromq
我正在阅读文本行,并创建其独特单词的列表(在将它们小写之后)。我可以使它与 flatMap 一起工作,但不能使它与 map 的“子”流一起工作。 flatMap 看起来更简洁和“更好”,但为什么 di
我正在编写一些 PowerShell 脚本来进行一些构建自动化。我发现 here echo $? 根据前面的语句返回真或假。我刚刚发现 echo 是 Write-Output 的别名。 写主机 $?
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 4年前关闭。 Improve thi
我将一个工作 View Controller 类从另一个项目复制到一个新项目中。我无法在新项目中加载 View 。在旧项目中我使用了presentModalViewController。在新版本中,我
我对 javascript 很陌生,所以很难看出我哪里出错了。由于某种原因,我的功能无法正常工作。任何帮助,将不胜感激。我尝试在外部 js 文件、头部/主体中使用它们,但似乎没有任何效果。错误要么出在
我正在尝试学习Flutter中的复选框。 问题是,当我想在Scaffold(body :)中使用复选框时,它正在工作。但我想在不同的地方使用它,例如ListView中的项目。 return Cente
我们当前使用的是 sleuth 2.2.3.RELEASE,我们看不到在 http header 中传递的 userId 字段没有传播。下面是我们的代码。 BaggageField REQUEST_I
我有一个组合框,其中包含一个项目,比如“a”。我想调用该组合框的 Action 监听器,仅在手动选择项目“a”完成时才调用。我也尝试过 ItemStateChanged,但它的工作原理与 Action
你能看一下照片吗?现在,一步前我执行了 this.interrupt()。您可以看到 this.isInterrupted() 为 false。我仔细观察——“这个”没有改变。它具有相同的 ID (1
我们当前使用的是 sleuth 2.2.3.RELEASE,我们看不到在 http header 中传递的 userId 字段没有传播。下面是我们的代码。 BaggageField REQUEST_I
我正在尝试在我的网站上设置一个联系表单,当有人点击发送时,就会运行一个作业,并在该作业中向所有管理员用户发送通知。不过,我在失败的工作表中不断收到此错误: Illuminate\Database\El
我是一名优秀的程序员,十分优秀!