假设我有一个看起来像这样的开关盒:(只是一个例子)
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,有没有办法消除它,并在此代码中使用其他东西?
谢谢。
答案 0 :(得分:0)
您正在使用switch-statement根据类型参数创建实例。在这种情况下或基本上在对数据类型使用条件操作时,每次要扩展应用程序时,都必须通过添加和修改条件来触及此语句。 很容易想象这会如何使您的状况检查爆炸。 请注意,您的示例是通过引入工厂或工厂方法来创建具体类型而不是直接在依赖项实际存在的类中创建它来隐藏依赖项。
只有在实例化构造复杂的类型时才应该尝试使用工厂(例如,您必须实例化其他类型以满足所有依赖关系)或需要其他配置。在这种情况下,可以使用交换机检查参数以确定要在创建的实例上应用的配置。在这种情况下,Builder模式也很有用。 通过封装复杂的构造或配置,您也可以消除重复的代码,因为现在所有代码都在一个地方。
要消除此开关,我会将每个类型构造提取到(如果需要)一个单独的工厂,该工厂只知道如何构造此类型。因此,您应该拥有一个方法 ConnectorFactory.CreateDbConnector(type) 的 MySqlFactory.CreateConnector(参数)强> 和 OracleDbFactory.CreateConnector()方法。 当以这种方式消除类型开关(每种类型的工厂)时,引入新类型只需要创建一个专用于这种类型的新工厂。没有修改现有代码。
鉴于您可以修改示例中的连接器类型,通过将每个(工厂)方法移动到它们实际创建的对象来保留坏工厂是更好的解决方案: MySqlFactory.CreateConnector(args) )变为 MySqlConnector.CreateInstance(args)。并且这里一个方法也封装了每个条件,并且在工厂(方法)的情况下也封装了应用的实例修改。这意味着如果我们有一个创建只读连接器的情况,我们现在将为连接器添加一个名为 MySqlConnector.CreateReadOnlyInstance(args)的附加方法(如果我们保留了工厂)。如果您想公开单例,则需要引入 MySqlConnector.CreateSharedInstance(args)。 由于每种类型现在都具有自己的工厂方法,因此向应用程序添加新类型不会破坏任何现有方法。
如果不需要工厂,我将始终直接实例化该类型。
作为解决方案的“Builder”模式还提供对实例化的灵活控制并封装过程,所有这些都不需要switch语句。但是,构建器应该只与一种类型相关联。
如果您需要可扩展,例如你想要替换一个类型(或实现)我会以相同的方式重构它,但是你应该根据“抽象工厂模式”实现所有工厂并使用依赖性反转。然后,所有类型实例化应仅在工厂或工厂方法中进行。除了内置类型之外,其他任何地方都不允许使用“new”关键字。通过使工厂成为必须更改具体类型的唯一位置,这减少了修改现有代码所需的工作量。完成后,如果需要,您也准备好使用依赖注入。
简而言之,通常您可以将开关的每个条件提取到一个单独的方法中,并使用描述该案例的描述性名称。这次调用者被迫选择所需的方法(代表一个案例)。调用者当然已经知道了关于他自己的一切(也就是你自己的类型)。他也会知道他喜欢的动作。不需要切换以选择正确的动作。 考虑到“Tell-Don't-Ask”原则,不应在第二种类型中放置开关来决定对第一种类型执行哪种操作。第一种类型必须调用适当的方法对其数据或代表他执行预期的操作。
并非每一个开关声明都是违规行为,如果你现在的后果,违反原则是好的。例如。使用开关来检查私人标志是没关系的。 switch语句等同于嵌套的if-then-else语句。应避免类型切换,例如通过使用继承并为边界定义良好的接口。当你使用例如多态性和依赖性反转您很可能不需要进行类型检查。