【问题标题】:Which is the better C# class design for dealing with read+write versus readonly哪个是更好的 C# 类设计来处理 read+write 与 readonly
【发布时间】:2011-02-17 19:31:41
【问题描述】:

我正在考虑两种不同的类设计来处理一些存储库是只读的而其他存储库是读写的情况。 (我预计不需要只写存储库。)


类设计 1 -- 在基类中提供所有功能,然后在子类中公开适用的功能

public abstract class RepositoryBase
{
    protected virtual void SelectBase() { // implementation... }
    protected virtual void InsertBase() { // implementation... }
    protected virtual void UpdateBase() { // implementation... }
    protected virtual void DeleteBase() { // implementation... }
}

public class ReadOnlyRepository : RepositoryBase
{
    public void Select() { SelectBase(); }
}

public class ReadWriteRepository : RepositoryBase
{
    public void Select() { SelectBase(); }
    public void Insert() { InsertBase(); }
    public void Update() { UpdateBase(); }
    public void Delete() { DeleteBase(); }
}

类设计2 - 读写类继承自只读类

public class ReadOnlyRepository
{
    public void Select() { // implementation... }
}

public class ReadWriteRepository : ReadOnlyRepository
{
    public void Insert() { // implementation... }
    public void Update() { // implementation... }
    public void Delete() { // implementation... }
}

这些设计中的一个明显比另一个强吗?如果有,是哪一个?为什么?

附:如果这听起来像是一个家庭作业问题,那不是,但如果你愿意,可以随意使用它:)

【问题讨论】:

  • 为什么要区分两种repository?
  • @John,好吧,也许不是,但我想我想确保我保护不应该因意外编辑而更改的数据库表。
  • @DanM:您的 DBA 对此有何看法?大多数人会觉得保护数据库的地方是在数据库中,而不是在你的代码中。
  • @John:我是 DBA :) 在这种情况下,数据库只不过是一个带有一些外键的数据存储。数据库中没有任何内容可以防止对本应为只读的表进行插入/更新/删除。
  • @John:通过代码保护数据库免受意外愚蠢的影响并非完全不合理(即,使代码更清晰,以提高可维护性)。当然,为了防止可能导致您的程序以意外方式运行的恶意用户,我想说这不是一个理想的方法。

标签: c# design-patterns architecture class-design


【解决方案1】:

第三个选项怎么样,与第一个密切相关,但使用接口代替:

public interface IReadRepository {
    public void Select();
}

public interface IWriteRepository {
    public void Insert();
    public void Update();
    public void Delete();
}

// Optional
public interface IRepository : IReadRepository, IWriteRepository {
}

public class Repository : IRepository {
   // Implementation
}

通过这种方式,实现全部(或可以)在一个地方,区别仅在于您正在查看的接口。

【讨论】:

  • 我认为这是一个有趣的模式,因为您可以通过赋予程序中“需要写访问权限并被清除为写访问权限”的对象来强制执行至少一种“语义”类型的安全性到“可写”接口。我不仅认为这是一个安全问题(因为安全问题是一个数据库权限问题),而且是一个“尽量减少在系统内为各种对象提供多少接口”,作为一个好的 OOP 原则。
  • 但是,除非域调用 WriteOnly 类,否则我会链接 2 个接口 (IWriteRepository: IReadRepository),这意味着继承问题已转移到接口。
  • @Henk - 确切地说,您可以跳过 IWriteRepository 并简单地让 IRepository 接口定义写入方法。这将为您提供他的第二个设计的优势,但不需要他在两个类之间拆分实现。
  • @stakx:这就是我的意思,这取决于域。
【解决方案2】:

编辑:我认为 Eric Petroelje 在他的回答中提供了一个非常好的基于界面的解决方案。首先,我可能会投票支持他的建议。)

从你的两个选择中,我显然会投票给 design #2。

对于设计#1,我认为拥有一个内部根本不是只读的“只读”类是没有意义的:

  1. 只读类比它需要的“重”。

  2. 任何人都可以从您的只读类派生,然后调用任何基类的修改方法。至少,对于设计#1,您应该将只读类设为sealed

使用设计#2,它比只读类更清楚的是全功能类的简化版本(基类),或措辞不同。

【讨论】:

  • 是的。这正是我在需要单独的 RW 和 R 接口的项目中使用的设计。
  • 我想我也更喜欢 Eric 的解决方案,但您的第 2 点 +1。这是我没有想到的问题。
【解决方案3】:

我会说设计#2,但是您应该将 ReadOnlyRepository 的名称更改为 ReadRepository 之类的名称。

继承定义了类之间的 IS-A 关系,并且说“ReadWriteRepository 是 ReadOnlyRepository”听起来不合逻辑。但是“ReadWriteRepository 是 ReadingRepository”。

【讨论】:

    【解决方案4】:

    首先让我承认我正在对您可能打算做什么做出一些假设。如果这没有抓住重点,请告诉我。

    我不确定这些课程在您的两个选项中会有多大用处。我假设您调用的代码会使用只读存储库的实例,有时还会使用读/写存储库的实例,但是接口不匹配,所以无论如何您都必须区分代码?

    如果您在存储库处于就绪状态时尝试写入存储库并让您的代码处理异常,则最好提供一个通用接口然后引发异常。

    【讨论】:

      【解决方案5】:

      我肯定会说设计 2 是最强的。如果你想有一个只读的实现,它不需要知道任何关于写的东西。使用插入、更新和删除方法扩展只读实现是非常有意义的。我也觉得这个设计是最符合Open-Closed principle的设计。

      【讨论】:

        【解决方案6】:

        我建议有一个基类 ReadableFoo,带有一个密封派生类 ImmutableFoo,其构造函数采用 ReadableFoo,以及可能可继承的派生类 MutableFoo,以及可能的类 ReadableShadowFoo,其构造函数将采用 ReadableFoo(可能也可能不是可变的),但它将充当它的只读包装器。

        【讨论】:

          【解决方案7】:

          Eric 的回答是指 SOLID Principle ISP。它使用起来非常简单和基本。

          【讨论】:

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