这是否违反了 Open/Closed 原则?
Does this break the Open/Closed Principle?
我下午大部分时间都在阅读Open/Closed原则,我似乎无法完全理解它。
这里有一些我已经阅读过的参考文章,似乎我错过了什么。
- Understanding the Open Closed Principle
- The end of dependency injection - who creates the dependencies?
假设我有一个基本的通用存储库,它公开了一些最通用的方法,这些方法有望满足存储库的任何需求。
存储库
public abstract class Repository<TModel> where TModel : class {
protected Repository() { }
public abstract IList<TModel> FilterBy(
Expression<Func<TModel, bool>> filterExpression);
public abstract IList<TModel> GetAll();
public abstract TModel GetById(int id);
public abstract Save(TModel instance);
}
然后,我想专攻 ProductRepository。
ProductRepository
public abstract class ProductRepository : Repository<Product> {
protected ProductRepository() : base() { }
}
让我们假设我在这里的基本存储库中拥有我需要的所有内容。如果是这样,那么我觉得我没有违反 Open/Closed 原则,因为我没有定义任何新成员等。
但是,如果我需要一种特殊类型的存储库,比如说 AlertLevelConfigurationRepository,并且业务需求说明我一次只能有一个 AlertLevelConfiguration。因此,存储库需要始终获取当前配置。
AlertLevelConfigurationRepository
public abstract class AlertLevelConfigurationRepository
: Repository<AlertLevelConfiguration> {
protected AlertLevelConfigurationRepository() : base() { }
public abstract AlertLevelConfiguration GetCurrent();
}
现在,我觉得因为这个新方法我打破了 Open/Closed 原则,因为这个 class 是从其祖先修改的派生类型。它被修改为存储库的基本定义不提供此 GetCurrent
方法。此外,我很确定我永远不会使用任何基本方法,因为 Save
方法除外,因为更改级别配置可以是可配置的!
最后,我想知道我是否理解了Open/Closed原则,不知何故我怀疑我是否理解。
我想知道这是否是一个违反原则的例子,如果不是,那么我想得到对原则本身的一些解释。
你那里的内容看起来像 "Open/Closed Principle" 的定义 - Repository
class 对扩展开放,但对修改关闭。您可以通过扩展它来添加新功能(以新子 classes 的形式),而无需修改存储库 class。将 GetCurrent()
调用添加到 Alert
subclass 是原理的 "Open to extension" 部分的一部分。
Open/Closed 是关于特定的 class 是 Open/Closed,而不是整个继承层次结构。你想写 class 一次,并且只出于一个原因(单一责任原则)更改它。
您提出了一个单独的问题:
"Besides, I'm quite sure I will never use the any of the base method, given the exception of the Save method, since the alter level configuration can be, well, configurable!"
这表明继承层次结构设计不当,或者选择从不正确的 class 继承。如果你继承了很多你不需要或不想使用的功能,它不是正确的基础 class,或者 class 做的太多了(违反单一责任原则) .
您可能想要将其中的一些功能(例如您的 Alert class 不需要的 GetAll()
和 GetById()
调用封装到单独的 classes 只有需要该功能的派生 classes 才会作为依赖项接受(假设您使用的是 DI)。或者可能将该功能放入从 Repository
派生的 class 中,并使 class 需要它从 class 派生(我更喜欢组合解决方案而不是继承解决方案)。
我下午大部分时间都在阅读Open/Closed原则,我似乎无法完全理解它。 这里有一些我已经阅读过的参考文章,似乎我错过了什么。
- Understanding the Open Closed Principle
- The end of dependency injection - who creates the dependencies?
假设我有一个基本的通用存储库,它公开了一些最通用的方法,这些方法有望满足存储库的任何需求。
存储库
public abstract class Repository<TModel> where TModel : class {
protected Repository() { }
public abstract IList<TModel> FilterBy(
Expression<Func<TModel, bool>> filterExpression);
public abstract IList<TModel> GetAll();
public abstract TModel GetById(int id);
public abstract Save(TModel instance);
}
然后,我想专攻 ProductRepository。
ProductRepository
public abstract class ProductRepository : Repository<Product> {
protected ProductRepository() : base() { }
}
让我们假设我在这里的基本存储库中拥有我需要的所有内容。如果是这样,那么我觉得我没有违反 Open/Closed 原则,因为我没有定义任何新成员等。
但是,如果我需要一种特殊类型的存储库,比如说 AlertLevelConfigurationRepository,并且业务需求说明我一次只能有一个 AlertLevelConfiguration。因此,存储库需要始终获取当前配置。
AlertLevelConfigurationRepository
public abstract class AlertLevelConfigurationRepository
: Repository<AlertLevelConfiguration> {
protected AlertLevelConfigurationRepository() : base() { }
public abstract AlertLevelConfiguration GetCurrent();
}
现在,我觉得因为这个新方法我打破了 Open/Closed 原则,因为这个 class 是从其祖先修改的派生类型。它被修改为存储库的基本定义不提供此 GetCurrent
方法。此外,我很确定我永远不会使用任何基本方法,因为 Save
方法除外,因为更改级别配置可以是可配置的!
最后,我想知道我是否理解了Open/Closed原则,不知何故我怀疑我是否理解。
我想知道这是否是一个违反原则的例子,如果不是,那么我想得到对原则本身的一些解释。
你那里的内容看起来像 "Open/Closed Principle" 的定义 - Repository
class 对扩展开放,但对修改关闭。您可以通过扩展它来添加新功能(以新子 classes 的形式),而无需修改存储库 class。将 GetCurrent()
调用添加到 Alert
subclass 是原理的 "Open to extension" 部分的一部分。
Open/Closed 是关于特定的 class 是 Open/Closed,而不是整个继承层次结构。你想写 class 一次,并且只出于一个原因(单一责任原则)更改它。
您提出了一个单独的问题:
"Besides, I'm quite sure I will never use the any of the base method, given the exception of the Save method, since the alter level configuration can be, well, configurable!"
这表明继承层次结构设计不当,或者选择从不正确的 class 继承。如果你继承了很多你不需要或不想使用的功能,它不是正确的基础 class,或者 class 做的太多了(违反单一责任原则) .
您可能想要将其中的一些功能(例如您的 Alert class 不需要的 GetAll()
和 GetById()
调用封装到单独的 classes 只有需要该功能的派生 classes 才会作为依赖项接受(假设您使用的是 DI)。或者可能将该功能放入从 Repository
派生的 class 中,并使 class 需要它从 class 派生(我更喜欢组合解决方案而不是继承解决方案)。