如何消除代码中的 switch-case?

How to eliminate switch-case in code?

假设我有一个看起来像这样的开关盒:(只是一个例子)

switch(Type) {
    case MSSQL:   /
      Connector = new MSSQLCOnnector(
              args);
      break;
    case MYSQL:
      Connector = new MYSQLConnector(
              different_args);
      break;
    case ORACLE:
      databaseConnector = new OracleConnector(
              again_different_args);
      break;
      default:
          break;
  }

既然 Switch-case 违反了 OCP,有没有办法消除它,并在这段代码中使用其他东西?

谢谢。

您正在使用 switch 语句创建基于类型参数的实例。在这种情况下,或者基本上在对数据类型使用条件操作时,每次您想要扩展您的应用程序时,您都必须通过添加和修改条件来触及此语句。 很容易想象这会如何使您的条件检查爆炸。 请注意,您的示例通过引入工厂或工厂方法来创建具体类型而不是直接在依赖项实际存在的 class 中创建它来隐藏依赖项。

您应该仅在实例化构造复杂的类型(例如,您必须实例化其他类型以满足所有依赖项)或需要额外配置时才尝试使用工厂。在这种情况下,可以使用开关来检查参数以确定要应用于已创建实例的配置。 Builder 模式在这种情况下也很有用。 通过封装复杂的构造或配置,您还可以消除重复的代码,因为现在所有内容都在一个地方。

为了消除这个开关,我会将每个类型构造提取到(如果需要)一个单独的工厂,该工厂只知道如何构造这个类型。因此,与其使用 ConnectorFactory.CreateDbConnector(type) 方法,不如使用方法 MySqlFactory.CreateConnector(参数) 和一个 OracleDbFactory.CreateConnector() 方法。 当以这种方式消除类型开关时(每个类型一个工厂),引入新类型只需要创建一个专用于该类型的新工厂。不修改现有代码。

鉴于您可以修改示例中的连接器类型,通过将每个(工厂)方法移动到它们实际创建的对象来让不良工厂单独存在将是一个更好的解决方案:MySqlFactory.CreateConnector(args) 变为 MySqlConnector.CreateInstance(args)。这里还有一个方法封装了每个条件,如果是工厂(方法),还有应用的实例修改。这意味着如果我们遇到创建只读连接器的情况,我们现在将向连接器添加一个名为 MySqlConnector.CreateReadOnlyInstance(args) 的附加方法(同样适用于我们会留在工厂)。如果你想公开一个单例,你会引入一个 MySqlConnector.CreateSharedInstance(args)。 由于每种类型现在都具有自己的工厂方法,因此向应用程序添加新类型不会破坏现有的任何东西。

如果不需要工厂,我总是会直接在适当的位置实例化类型。

"Builder" 模式作为解决方案还提供了对实例化的灵活控制并封装了过程,所有这些都不需要 switch 语句。但是构建器应该只与一种类型相关联。

如果您需要可扩展,例如你想要替换一个类型(或实现)我会用同样的方式重构它但是你应该根据 "Abstract Factory Pattern" 实现所有工厂并全面使用依赖倒置。所有类型的实例化都应该只发生在工厂或工厂方法中。 "new" 关键字在除内置类型之外的任何其他地方都不允许使用。通过使工厂成为必须更改具体类型的唯一位置,这减少了修改现有代码所需的工作量。完成后,如果需要,您也可以充分准备使用依赖注入。

长话短说,通常您可以将开关的每个条件提取到一个单独的方法中,该方法具有描述情况的描述性名称。这次调用者被迫选择所需的方法(代表一个案例)。来电者当然已经知道关于他自己的一切(也就是他自己的类型,就像你的例子一样)。他还将知道他喜欢执行的操作。无需切换即可选择正确的操作。 并且考虑到 "Tell-Don't-Ask" 原则,不应在第二个类型中放置任何开关来决定对第一个类型执行哪个操作。第一种类型必须调用适当的方法来对他的数据或代表他执行预期的操作。

并不是每个 switch 语句都是违规的,违反原则是可以的,如果你现在承担后果的话。例如。使用开关来检查私有标志会很好。 switch 语句等同于嵌套的 if-then-else 语句。应避免类型切换,例如通过使用继承和为边界定义良好的接口。当你使用例如多态性和依赖倒置你很可能不需要类型检查。