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 添加到工厂时会发生什么?

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 添加了身份验证