【问题标题】:SOLID principles and web page classSOLID原理和网页类
【发布时间】:2012-07-27 14:33:19
【问题描述】:

我在编写新类和代码时尝试遵循 SOLID 原则。

我的问题是关于扩展 Page 类的 ASP.NET 类,即 ASPX 文件。如果我有一个页面类,它有一个 page_load 事件,它创建多个对象的实例,例如Person 类、Sport 类等然后我相信这个页面类与这些类紧密耦合。是这种情况还是我遗漏了一些明显的东西?是不是所有的类都应该暴露接口,而客户端(aspx页面)应该使用接口而不是直接实例化类。

如果涉及多态性,我发现接口很有用,例如使用 Student 接口在运行时创建研究生或本科生的实例。所有类都应该有接口吗?

【问题讨论】:

  • SOLID 原则?如?所有类都应该公开接口,并且客户端(aspx 页面)应该使用接口而不是直接实例化类。这绝不是固定的
  • @Zia,这里描述了 SOLID 原则:(remondo.net/solid-principles-csharp-interface-segregation
  • @Zia,我没说是。我说我正在尝试遵循 SOLID 原则,但我对 Page 类有疑问。
  • 谢谢你的链接
  • @Zia,你能解释一下为什么“这不可能”

标签: asp.net .net vb.net design-patterns solid-principles


【解决方案1】:

如果您在 Page 类(表示层)中创建实体,则显然违反了单一职责原则,因为页面类将有多种更改原因。

相反,将此逻辑移至业务层并创建处理此逻辑的服务。

您的页面需要与服务接口而不是实现 (DIP) 通信,并且该服务接口需要窄化 (ISP);可能只有一种方法。

如果您将服务的所有参数打包到一个对象中,从而分离数据和行为,并为您的服务使用通用接口(即ICommandHandler<TCommand>),您甚至可以遵守 OCP,因为您现在可以添加行为(例如验证、事务、死锁检测、异步处理、排队)到服务,而不对应用程序进行任何更改。

最后一点,不要为实体创建接口。这是相当无用的,并且会掩盖您的代码。

【讨论】:

  • 谢谢。有问题的 Web 应用程序非常大,有很多页面。您能否推荐一种设计模式或将我转发到确认这一点的网页?
  • 谢谢,但我在此链接中没有看到任何对页面类的引用。
  • 本文描述了命令/处理程序模式,这是应用 SOLID 原则时的重要模式。使用 ASP.NET 页面类查看 wheb 的另一种模式是 Model View Presenter 模式。
  • 你认为哪种模式更好?我想这取决于场景。
【解决方案2】:

我不会在后面的代码中实例化对象。我所做的是调用一个实例化获取对象的服务——它用数据库中的数据实例化它们。我还在母版页中公开了该服务,并像这样引用它:

IUser _user = (Page.Master as MasterBase).RegistrationService.GetPersonFromEmailAddress(txtUser.text);

当然,您应该检查以确保 (Page.Master as MasterBase) 不为空......


按要求编辑

我创建了一个基础母版页,所有其他站点母版页都必须从该母版页继承。在这个基本母版页的构造函数中,我实例化了所有必要的服务。当然,如果这些服务没有在整个站点中使用,它们可以在不同的、更合适的部分通用母版页上实例化。

public readonly IRegistration RegistrationService;
public readonly IEventService EventService;
public readonly IEmailSendingService EmailService;
private readonly IMenuService _menuService;

public MasterBase()
{
    RegistrationService = new BusinessLogic.Registration(DatabaseConnectionString);
    EventService = new EventService(DatabaseConnectionString);

    var fromAddress = ConfigurationManager.AppSettings["Webmaster"];
    var defaultToEmailAddress = ConfigurationManager.AppSettings["toEmail"];
    EmailService = new EmailSendingService(BaseUrl, fromAddress, defaultEmailAddress);
    _menuService = new MenuService(DatabaseConnectionString);
}

如上所述,网站上使用的任何母版页都将从基础继承:

public partial class MasterContent : MasterBase

任何引用母版页的 UI 页面(在我的例子中几乎全部)

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="activities.aspx.cs" Inherits="activities" MasterPageFile="~/MasterContent.Master" %>

现在可以访问后面代码中的不同服务:

IUser _user = (Page.Master as MasterBase).RegistrationService.GetPersonFromEmailAddress(txtUser.text);

正如下面的 cmets 所述,不同的 UI 页面确实与基本母版页紧密耦合,但我不知道在一般的 ASP.NET Web 表单应用程序中有什么更好的方法来做到这一点。它减轻了在每个 UI 页面上实例化服务的需要,并且从特定于项目的角度来看,让所有母版页都继承自该基本页面是可以接受的。

【讨论】:

  • 所以基本上你提倡使用全局范围。您正在以这种方式在页面和母版页之间创建非常紧密的耦合。不需要存在的耦合
  • @RuneFS 我同意,它确实创建了一个紧密耦合,但我通过创建一个所有站点 MasterPages 都继承自的 Base MasterPage 来缓解这种紧密性。对于这个特定项目,任何新的母版页都必须继承它,因此它们将有权访问 RegistrationService。我看到的唯一替代方法是找到一种方法将服务依赖项注入到每个需要该服务的 System.Web.UI.Page 中。我还没有找到在 ASP.NET 中执行此操作的好方法(MVC 是另一回事,但不是一般的 Web 窗体)您建议如何完成?
【解决方案3】:

您可以这样做的一种方法是在 Global.asax 中配置您的依赖项

public static void RegisterRoutes(RouteCollection routes)
{
    routes.MapPageRoute("",
        "Create/{Service}",
        "~/categoriespage.aspx",{"Service", new WhatEverSErvice()} );
}

您需要在哪里适应 URL 模式和文件路径以适应您的网站。这将通过 Service 路由值为您提供服务对象的句柄

然后您可以声明一个基页类

public abstract class BasePage<TService> : Page where TService : IService {

   protected TService Service{
       get {
           return Page.RouteData.Values["Service"] as TService;
       }
   }
}

这里IService 是一个接口,您必须定义它是您的服务的基础(如果这样的接口有意义,否则只需省略 where 子句)

然后让你所有的页面都派生自这个类,当你需要你的特定服务时,你只需编写类似

的东西
var myFoo = Service.CreateFoo();

当用户在 / 之后提供一个字符串时,这种方法在错误情况下确实有点奇怪。您可以通过扩展属性来处理这个问题,因为它基本上是一个未找到的文件(提供的 URL 无效)您可以返回 404 FNF 响应

【讨论】:

    猜你喜欢
    • 2017-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-01
    • 2018-11-11
    • 2012-11-21
    相关资源
    最近更新 更多