Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop 多态性与切换情况的权衡_Oop_Design Patterns_Solid Principles - Fatal编程技术网

Oop 多态性与切换情况的权衡

Oop 多态性与切换情况的权衡,oop,design-patterns,solid-principles,Oop,Design Patterns,Solid Principles,我还没有找到任何关于这方面的明确文章,但我想知道为什么多态性是推荐的设计模式,而不是详尽的开关用例/模式匹配。我这样问是因为我从经验丰富的开发人员那里得到了很多不使用多态类的热,这一直困扰着我。我个人在多态性方面经历了一段糟糕的时光,在切换案例方面经历了一段美好的时光,在我看来,抽象和间接性的减少使得代码的可读性变得非常容易。这与通常被视为行业标准的“干净代码”之类的书形成了直接对比 注意:我使用TypeScript,因此以下示例可能不适用于其他语言,但我认为只要您有详尽的模式匹配/切换案例,该

我还没有找到任何关于这方面的明确文章,但我想知道为什么多态性是推荐的设计模式,而不是详尽的开关用例/模式匹配。我这样问是因为我从经验丰富的开发人员那里得到了很多不使用多态类的热,这一直困扰着我。我个人在多态性方面经历了一段糟糕的时光,在切换案例方面经历了一段美好的时光,在我看来,抽象和间接性的减少使得代码的可读性变得非常容易。这与通常被视为行业标准的“干净代码”之类的书形成了直接对比

注意:我使用TypeScript,因此以下示例可能不适用于其他语言,但我认为只要您有详尽的模式匹配/切换案例,该原则通常适用

列出选项 如果您想知道一个操作的可能值是多少,使用enum、switch case,这很简单。对于类来说,这需要一些反射魔法

//这里肯定有两个动作,我甚至可以用基本原语以编程方式循环它们
枚举操作{
A=‘A’,
B=‘B’,
}
遵循代码 依赖注入和抽象类意味着跳转到定义将永远不会到达您想要的地方

functiondoletterthing(myEnum:Action){
开关(myEnum){
案例行动A:
返回;
案例行动B;
返回;
违约:
排气检查(髓鞘);
}
}

function-doLetterThing(操作:BaseAction){
action.doAction();
}
如果我跳转到
BaseAction
doAction
的定义,我将最终进入抽象类,这无助于我调试函数或实现。如果您有一个只包含一个类的依赖项注入模式,这意味着您可以通过转到主类/函数并查找“BaseAction”是如何实例化的,然后按照该类型进行定位并滚动找到实现来“猜测”。但对于开发者来说,这似乎是一个糟糕的用户体验

(关于依赖注入是否好的小说明,在必要的情况下,trait似乎做得足够好(尽管作为一种规则而不是必要性过早地进行,似乎会导致更难遵循代码))

少写代码 这取决于具体情况,但如果必须为基类型定义一个额外的抽象类,并重写所有函数类型,那么代码如何比单行切换情况少?如果您在枚举中添加了一个选项,那么您的类型检查器将标记您需要处理的所有位置,这通常涉及为案例分别添加1行和为实现添加1+行。将其与多态类进行比较,多态类需要定义一个新的类,该类需要具有正确参数的新函数语法以及开始和结束参数。在大多数情况下,开关盒的代码和行数较少

托管 一个类型的所有东西都在一个地方,这很好,但通常每当我实现这样一个函数时,我都会寻找一个类似的实现函数。对于switch case,它与派生类非常接近,我需要在另一个文件或目录中查找和定位派生类

如果我实现了一个特性更改,比如为一种类型修剪字符串末端的空格,那么我需要打开所有类文件,以确保它们是否实现了类似的功能,从而在所有类文件中都正确实现了该功能。如果我忘记了,我可能会在不知情的情况下对不同类型的人有不同的行为。通过开关,同一位置使这一点非常明显(尽管不是万无一失的)

结论
我错过什么了吗?我们有这些明确的设计原则是没有意义的,我基本上只能找到一些肯定的文章,但与一些基本的模式匹配风格开发相比,没有看到任何明显的好处和严重的缺点。拥有一个干净的代码的主要目的并不是为了在实现当前功能时让您的生活更轻松,而是在将来扩展或维护代码时让您的生活更轻松

在您的示例中,您可能会觉得使用switch case实现两个操作。但如果将来需要添加更多操作,会发生什么?使用抽象类,您可以轻松地创建新的操作类型,并且不需要修改调用方。但如果您继续使用switch case,它将更加混乱,尤其是对于复杂的情况

此外,遵循更好的设计模式(本例中为DI)将使代码更易于测试。当你只考虑简单的情况时,你可能找不到使用合适的设计模式的有用性。但是如果你考虑更广泛的方面,它确实是有回报的。

考虑一下,特别是OCP和DI

  • 要在将来扩展交换机案例或枚举并添加新功能,必须修改现有代码。修改遗留代码既危险又昂贵。风险很大,因为您可能会无意中引入回归。代价高昂,因为您必须了解(或重新了解)实现细节,然后重新测试遗留代码(可能是在您修改它之前工作的)

  • 对具体实现的依赖会造成紧密耦合,并抑制模块化。这使得代码僵化而脆弱,因为一个地方的更改会影响许多依赖项

此外,考虑可伸缩性。抽象支持任意数量的实现,其中许多在创建抽象时可能是未知的。开发人员不需要理解或关心其他实现。一个开发人员可以在一个开关中处理多少个案例,10个?一百

注意,这并不意味着多态性(或OOP)适用于每个类或应用程序。例如,在考虑可扩展性和可伸缩性时,有一个假设是
interface Communication {
   sendMessage()
}

Letter implements Communication {

   sendMessage() {
     // get receiver
     // get sender
     // set message
     // send message
  }

}
interface Communication {
   default sendMessage() {
     getMessageFactory().sendMessage(getSender(), getReceiver(), getBody())
   }

   getSender()
   getReceiver()
   getBody()
}

Letter implements Communication {

   getSender() { returns sender }
   getReceiver() {returns receiver }
   getBody() {returns body}
   getMessageFactory {returns LetterMessageFactory}
}