SOLID - 单一职责原则和 Open/Closed 原则是否相互排斥?
SOLID - are the Single Responsibility Principle and the Open/Closed Principle mutually exclusive?
单一职责原则指出:
A class should have one, and only one, reason to change.
Open/Closed 原则 指出:
You should be able to extend a classes behavior, without modifying it.
如果一个 class 应该只有一个更改的理由,但不应该被修改,那么开发人员如何能够同时遵守这两个原则?
例子
工厂模式是一个很好的例子,它具有单一职责,但可能违反 open/closed 原则:
public abstract class Product
{
}
public class FooProduct : Product
{
}
public class BarProduct : Product
{
}
public class ProductFactory
{
public Product GetProduct(string type)
{
switch(type)
{
case "foo":
return new FooProduct();
case "bar":
return new BarProduct();
default:
throw new ArgumentException(...);
}
}
}
当我需要在稍后阶段将 ZenProduct
添加到工厂时会发生什么?
- 这肯定违反了open/closed原则?
- 我们如何防止这种违规行为?
A class should have one, and only one, reason to change.
这基本上意味着,您的 class 应该代表单一职责,之后不应修改以适应新功能。
例如,如果您有class,负责打印pdf格式的报告。后来,你想添加新功能来支持打印其他格式的报告。那么不要修改现有代码,你应该扩展它以支持其他格式,这也意味着 扩展 classes 行为,而不修改它
我认为这取决于您对 SRP 的解读。这东西总是有些主观。让 100 个人来定义 "single responsibility",你可能会得到 100 个不同的答案。
使用 中的场景,典型的解决方案可能是使用 ReportGenerator
class 公开 GeneratePdf
方法。如果需要,稍后可以使用额外的 GenerateWord
方法对其进行扩展。不过和你一样,我觉得这有点儿味道。
我可能会将 GeneratePdf
方法重构为 PdfReportGenerator
class,然后通过 ReportGenerator
公开它。这样 ReportGenerator
就只有一个责任;这是公开各种报告生成机制(但不包含它们的逻辑)。然后可以在不扩展该责任的情况下扩展它。
我想说的是,如果您发现冲突,很可能是一种架构风格,需要快速审查,看看是否可以用更好的方式完成。
我有一个 class StudentOrganiser
class 需要 IStudentRepository
依赖。 IStudentRepository
公开的接口是 GetStudent(int studentId)
Class 服从 SRP,因为它没有任何与管理与存储库源的连接相关的逻辑。
Class 服从 OCP,因为如果我们想将存储库源从 SQL 更改为 XML,StudentOrganiser
不需要进行任何更改 => 开放扩展但是因修改而关闭。
考虑如果 StudentOrganiser
被设计为不依赖 IStudentRepository
,那么 class 内部的方法本身必须负责实例化 new StudentSqlRepository()
如果以后需要在某些 运行 时间条件的基础上也开始支持 StudentXMLRepository
,您的方法将以某种 case switch
范式结束,因此违反了 SRP,因为方法也沉迷于实际存储库中决定因素。通过注入存储库依赖项,我们从 class 身上卸下了这个责任。现在 StudentOrganiser
class 无需任何修改即可扩展为支持 StudentXMLRepository
。
这感觉像是在讨论'extend a classes behaviour'的语义。将新类型添加到工厂是修改现有行为,而不是扩展行为,因为我们没有改变工厂所做的一件事。我们可能需要扩展工厂,但我们还没有扩展它的行为。扩展行为意味着引入新的行为,并且每次创建类型的实例或授权工厂的调用者时都会更多地遵循事件 - 这两个示例都扩展(引入新的)行为。
A class should have one, and only one, reason to change.
问题中的示例是一个用于创建 Product
实例的工厂,对其进行更改的唯一正当理由是更改它创建的 Product
个实例的某些内容,例如添加一个新的ZenProduct
.
You should be able to extend a classes behavior, without modifying it.
实现这一点的一个非常简单的方法是使用 Decorator
The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.
public interface IProductFactory
{
Product GetProduct(string type);
}
public class ProductFactory : IProductFactory
{
public Product GetProduct(string type)
{
\ find and return the type
}
}
public class ProductFactoryAuth : IProductFactory
{
IProductFactory decorated;
public ProductFactoryAuth(IProductFactory decorated)
{
this.decorated = decorated;
}
public Product GetProduct(string type)
{
\ authenticate the caller
return this.decorated.GetProduct(type);
}
}
在应用 SOLID 原则时,装饰器模式是一种强大的模式。在上面的示例中,我们在不更改 ProductFactory
.
的情况下向 ProductFactory
添加了身份验证
单一职责原则指出:
A class should have one, and only one, reason to change.
Open/Closed 原则 指出:
You should be able to extend a classes behavior, without modifying it.
如果一个 class 应该只有一个更改的理由,但不应该被修改,那么开发人员如何能够同时遵守这两个原则?
例子
工厂模式是一个很好的例子,它具有单一职责,但可能违反 open/closed 原则:
public abstract class Product
{
}
public class FooProduct : Product
{
}
public class BarProduct : Product
{
}
public class ProductFactory
{
public Product GetProduct(string type)
{
switch(type)
{
case "foo":
return new FooProduct();
case "bar":
return new BarProduct();
default:
throw new ArgumentException(...);
}
}
}
当我需要在稍后阶段将 ZenProduct
添加到工厂时会发生什么?
- 这肯定违反了open/closed原则?
- 我们如何防止这种违规行为?
A class should have one, and only one, reason to change.
这基本上意味着,您的 class 应该代表单一职责,之后不应修改以适应新功能。
例如,如果您有class,负责打印pdf格式的报告。后来,你想添加新功能来支持打印其他格式的报告。那么不要修改现有代码,你应该扩展它以支持其他格式,这也意味着 扩展 classes 行为,而不修改它
我认为这取决于您对 SRP 的解读。这东西总是有些主观。让 100 个人来定义 "single responsibility",你可能会得到 100 个不同的答案。
使用 ReportGenerator
class 公开 GeneratePdf
方法。如果需要,稍后可以使用额外的 GenerateWord
方法对其进行扩展。不过和你一样,我觉得这有点儿味道。
我可能会将 GeneratePdf
方法重构为 PdfReportGenerator
class,然后通过 ReportGenerator
公开它。这样 ReportGenerator
就只有一个责任;这是公开各种报告生成机制(但不包含它们的逻辑)。然后可以在不扩展该责任的情况下扩展它。
我想说的是,如果您发现冲突,很可能是一种架构风格,需要快速审查,看看是否可以用更好的方式完成。
我有一个 class StudentOrganiser
class 需要 IStudentRepository
依赖。 IStudentRepository
公开的接口是 GetStudent(int studentId)
Class 服从 SRP,因为它没有任何与管理与存储库源的连接相关的逻辑。
Class 服从 OCP,因为如果我们想将存储库源从 SQL 更改为 XML,StudentOrganiser
不需要进行任何更改 => 开放扩展但是因修改而关闭。
考虑如果 StudentOrganiser
被设计为不依赖 IStudentRepository
,那么 class 内部的方法本身必须负责实例化 new StudentSqlRepository()
如果以后需要在某些 运行 时间条件的基础上也开始支持 StudentXMLRepository
,您的方法将以某种 case switch
范式结束,因此违反了 SRP,因为方法也沉迷于实际存储库中决定因素。通过注入存储库依赖项,我们从 class 身上卸下了这个责任。现在 StudentOrganiser
class 无需任何修改即可扩展为支持 StudentXMLRepository
。
这感觉像是在讨论'extend a classes behaviour'的语义。将新类型添加到工厂是修改现有行为,而不是扩展行为,因为我们没有改变工厂所做的一件事。我们可能需要扩展工厂,但我们还没有扩展它的行为。扩展行为意味着引入新的行为,并且每次创建类型的实例或授权工厂的调用者时都会更多地遵循事件 - 这两个示例都扩展(引入新的)行为。
A class should have one, and only one, reason to change.
问题中的示例是一个用于创建 Product
实例的工厂,对其进行更改的唯一正当理由是更改它创建的 Product
个实例的某些内容,例如添加一个新的ZenProduct
.
You should be able to extend a classes behavior, without modifying it.
实现这一点的一个非常简单的方法是使用 Decorator
The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.
public interface IProductFactory
{
Product GetProduct(string type);
}
public class ProductFactory : IProductFactory
{
public Product GetProduct(string type)
{
\ find and return the type
}
}
public class ProductFactoryAuth : IProductFactory
{
IProductFactory decorated;
public ProductFactoryAuth(IProductFactory decorated)
{
this.decorated = decorated;
}
public Product GetProduct(string type)
{
\ authenticate the caller
return this.decorated.GetProduct(type);
}
}
在应用 SOLID 原则时,装饰器模式是一种强大的模式。在上面的示例中,我们在不更改 ProductFactory
.
ProductFactory
添加了身份验证