ASP.NET MVC - 当 SRP 和 DRY 出现冲突时

发布于 2024-10-01 15:36:40 字数 459 浏览 3 评论 0原文

我最简单的 ASP.NET MVC 2 控制器调用我的服务层,并使用 AutoMapper 将视图模型映射到实体。一切看起来都很棒,而且没有重复的代码。

然而,当我遇到类似行为的场景时,我很难平衡单一职责原则 (SRP) 和不要重复自己 (DRY)。例如,需要添加/编辑车辆,其中某些属性/行为是共享的,而其他属性/行为是特定车辆所独有的。

如果我努力实现真正的瘦控制器(从而遵守单一职责原则),我最终会在视图和控制器中重复代码,并有细微的变化(标题、字段标签、字段可见性、下拉值、选择标准等)。

如果我努力实现不重复的代码,我最终会将太多的逻辑捆绑到单个控制器/视图中,并且它会变得臃肿。

解决控制器/视图中重复代码的方法有哪些?我不是在谈论可以分解到存储库中的数据库代码。我也不是在谈论可以分解到服务层的业务逻辑。我正在寻找能够帮助我在上述场景中制定最佳解决方案的工具和/或经验规则。

My simplest ASP.NET MVC 2 controllers make calls to my service layer and map view models to entities using AutoMapper. Everything looks fantastic and there is no repeated code.

However, when I get into scenarios where I have similar behavior I have trouble balancing Single Responsibility Principle (SRP) with Don't Repeat Yourself (DRY). An example of this might be the need to add/edit vehicles where some properties/behaviors are shared while others are unique to a specific vehicle.

If I strive for really thin controllers (thus honoring Single Responsibility Principle), I end up having repeated code in both the views and controllers with minor variations (title, field labels, field visibility, dropdown values, selection criteria, etc.).

If I strive for non-repeated code I end up bundling too much logic into a single controller/view and it gets bloated.

What are some ways of addressing repeated code in controllers / views? I'm not talking about database code that can be factored out to a repository. Nor am I talking about business logic that can be factored out to a service layer. I'm looking for tools and/or rules of thumb that will help me produce the best solution in the scenario described above.

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

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

发布评论

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

评论(4

美煞众生 2024-10-08 15:36:40

您将获得:

  • 部分
  • RenderAction
  • 操作过滤器
  • 服务层和辅助类(不是 HtmlHelper)
  • 模型绑定器
  • 基本控制器
  • 依赖注入

因此您的视图可以调用类似部分的共享部分/操作,操作过滤器可以准备公共数据,可以隐藏数据库访问代码在智能模型绑定器中,或者您可以拥有子控制器通过特定调整覆盖的父控制器。当然,还有很好的旧服务层,您只需将通用代码提取到帮助器/静态方法中,或者更好的是注入特定的实现。

这不是什么新鲜事,都是老套路。

或者,也许您的控制器做了太多工作?这就是上面的内容也有帮助的地方。 ASP.NET MVC 有非常好的工具来隐藏基础结构层代码并将其移离控制器。如果它不是基础设施 - 它可能属于域层。在那里您可以使用继承、组合和其他 OOP 技巧。

具体例子。假设您的控制器应该以不同的方式设置一些属性。

  1. 如果主要是格式化或选择要显示的属性,您可以让您的视图执行此操作
  2. 您可以让您的实体拥有虚拟方法 - 即重构代码以将决策移动到域层而不是控制器
  3. 您可以拥有帮助器 ViewDetails 类,它将采用您的实体并根据您的需要获取数据;这是一个有点肮脏的把戏,但有时很有用;您将决策委托给另一个“策略”类
  4. 您可以使用操作过滤器将此数据添加到 ViewData,或调整特定的 ViewData.Model 类型(查找它的某些接口)。
  5. 您可以拥有抽象控制器,其中子级将实现详细信息传递给基本构造函数,例如 ():base(repository =>repository.GetSpecificData())

等等。实际上我在适当的地方使用了它们。

You get:

  • partials
  • RenderAction
  • action filters
  • service layer and helper classes (not HtmlHelper)
  • model binders
  • base controllers
  • dependency injection

So your views can invoke shared partials/actions for similar parts, common data can be prepared by action filters, database access code can be hidden in smart model binder, or you can have parent controller that child controllers override with specific tweaks. And, of course, good old service layeres, where you just extract common code into helper/static methods, or, better, inject specific implementations.

That's nothing new, same old tricks.

Or, maybe, your controllers do too much works? This is where stuff above also helps. ASP.NET MVC has very good tools to hide infrastructure-layer code and move it away from controllers. And if it's not infrastructure - it probably belongs to domain layer. There you can use inheritance, composition and other OOP tricks.

Specific example. Suppose your controllers should set few properties in a different way.

  1. You can have your views to do this, if it's mostly formatting or choosing what properties to show
  2. You can have your entities to have virtual methods - i.e. refactor code to move decisions to domain layer instead of controllers
  3. You can have helper ViewDetails classes that will take your entities and get the data based on for what you need it; this is a bit of a dirty trick but sometimes useful; you delegate decision to another "strategy" class
  4. You can use action filters to add this data to ViewData, or to tweak specific ViewData.Model types (look for some interface of it).
  5. You can have abstract controller, where children pass implementation details to the base constructor like (): base(repository => repository.GetSpecificData())

And so on. I actually use all of them in appropriate places.

耳钉梦 2024-10-08 15:36:40

您过于担心 SRP 和 DRY。它们只是原则,并不总是正确的。如果 SRP 和 DRY 使您的代码更易于维护,那么它们就很好,但如果它们妨碍了,请忽略它们。 MVC 类似。它在简单的小型桌面应用程序中很有用,但不适用于 Web 应用程序。 Web Forms 对于互联网世界来说要好得多,而 MVC 则是 20 世纪 80 年代的东西。

You are worrying too much about SRP and DRY. They are principles only and are not always right. SRP and DRY are good if they make your code more maintainable, but if they are in the way then ignore them. MVC is similar. It is useful in simple small desktop applications but is not appropriate for web applications. Web Forms is much better for the internet world while MVC is something from the 1980s.

鹿童谣 2024-10-08 15:36:40

在这些情况下,我建议您使用 SRP 而不是 DRY。我这里写了一个详细的答案。

简而言之,这两条规则都有助于保持代码的可维护性。 DRY是低抽象级别的机制,而SRP是高抽象级别的机制。通过维护应用程序,高抽象级别结构比低抽象级别更重要。

对于你的情况,我认为没有必要放弃 DRY。

这方面的一个例子可能是需要添加/编辑车辆,其中一些
属性/行为是共享的,而其他属性/行为是特定的
车辆。

许多设计模式可以在这种情况下提供帮助。您可以将装饰器、合成器等与不同类型车辆的构建器结合使用。

I recommend you to use SRP over DRY in those cases. I wrote here a detailed answer.

In short both are rules which help to keep your code maintainable. DRY is a low abstraction level mechanism, while SRP is a high abstraction level. By maintain an application the high abstraction level structure is more important than the low abstraction level.

In your case I don't think it is necessary to give up DRY.

An example of this might be the need to add/edit vehicles where some
properties/behaviors are shared while others are unique to a specific
vehicle.

Many design patterns can help in this case. You can use decorator, composition, and so on... combined with builders for the different types of vehicles.

最初的梦 2024-10-08 15:36:40

我发现 ApiEndpoints 对此非常有用。您为每个控制器方法创建一个类。代码有点多,但我认为它非常干净。

I found that ApiEndpoints is very good for this. You create a class for each controller method. A little more code, but I think it's very clean.

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