- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
访问者模式在元素层次结构稳定且操作这些元素所需的功能经常变化的情况下很有用。
在元素层次结构发生变化的情况下,访问者模式会受到耦合的影响,这会强制重建元素和功能层次结构中的所有类。
为了对此进行改进,Acyclic Visitor 使用了额外的抽象级别,在顶部有一个空的 Visitor 接口(interface),并为元素层次结构中的每个类提供了一个特定的接口(interface)。
假设有两个具体的元素类型 IntMessage 和 StringMessage,非循环访问者看起来像这样:
abstract class Message // parent for the model/element/data classes
{
public abstract void Accept(Visitor visitor);
}
class IntMessage : Message // concrete element type 1
{
internal int data;
public override void Accept(Visitor visitor)
{
// check if the concrete visitor knows how to work on IntMessage
if (visitor is IntMessageVisitor)
(visitor as IntMessageVisitor).Visit(this);
}
}
class StringMessage : Message // concrete element type 2
{
internal String msg;
public override void Accept(Visitor visitor)
{
// check if the concrete visitor knows how to work on StringMessage
if (visitor is StringMessageVisitor)
(visitor as StringMessageVisitor).Visit(this);
}
}
interface Visitor // empty parent interface for acyclic visitor
{
}
interface IntMessageVisitor : Visitor
{
void Visit(IntMessage message);
}
interface StringMessageVisitor : Visitor
{
void Visit(StringMessage message);
}
一个具体的访问者将从它知道如何访问的元素类型的所有特定访问者接口(interface)继承。这样做的好处是,在将新类添加到元素层次结构的情况下,只有需要访问新元素的具体访问者才会被迫更改。
class PrintVisitor : StringMessageVisitor, IntMessageVisitor
{
public void Visit(IntMessage message)
{
Console.WriteLine("Int message with data = " + message.data);
}
public void Visit(StringMessage message)
{
Console.WriteLine("String message with data = " + message.msg);
}
}
足够的设置,让我们继续这个问题。
问题是,考虑到非循环访问者模式的复杂性,它是否比使用带有 switch-on-type 的简单命令有任何真正的好处?
例如,我们可以将 PrintVisitor 重写为以下打印命令:
class PrintCommand : Command
{
public void Execute(Message message)
{
// switch on type
if (message.GetType() == typeof(IntMessage))
{
Console.WriteLine("Int message with data = " + ((IntMessage)message).data);
}
else
if (message.GetType() == typeof(StringMessage))
{
Console.WriteLine("String message with data = " + ((StringMessage)message).msg);
}
}
}
如果将来在元素层次结构中添加了新类(例如 DateMessage ),那么仍然只有想要在新元素类型上工作的命令需要更改。最终的设计会简单得多,没有多重接口(interface)继承和双重分派(dispatch),代价是使用运行时类型信息而不是虚函数。
就 OCP 和 future 的维护而言,与非循环访问者相比,开启类型似乎没有额外的成本。
是否有任何理由更喜欢 ACyclic Visitor 而不是带有 switch-on-type 的命令?
最佳答案
没有明确的答案。答案取决于您的具体情况。您的示例相对简单,因为它只涉及单一类型的替代方案,而不是类型的替代方案。族。 typical example of a Visitor展示了使用不同的访问者来处理树中多个 不同类型的家族。一个很好的例子是将 XML 呈现为 HTML、PDF 或 word 文档的访问者。这些访问者中的每一个都处理一系列元素。
在这个简单的示例中,Visitor
不是很有用。它以(显着)复杂性为代价增加了类型安全性。
自从 C# 6 引入模式后,所有这些代码都可以大大减少。在 C# 9 中,Execute
可以是:
public static void Execute(Message message)
{
var text=message switch {
IntMessage intM=>$"blah {intM.data}",
StringMessage stM=> $"blah {stM.msg}",
_ => throw new ArgumentException($"Unknown type {message.GetType()}",
nameof(message))
};
Console.WriteLine(text);
}
首先是泛型,然后是动态
,现在是像模式匹配这样的功能特性,极大地减少了需求,同时大大简化了代码很多。函数式语言很少需要访问者,因为编译器可以轻松推断类型并检测遗漏的情况。
如果 C# 已经区分联合,这是一个热切期待但自 C# 7 以来一直推迟的功能,那么甚至不需要 default case。编译器本身会识别缺失的情况。
在这个例子中,更改可以本地化到这个方法。
虽然在渲染器示例中,每个具体访问者都必须处理相同的众所周知的元素集。这些元素具有众所周知的结构。在这种情况下需要访客吗?
也许吧,但它可以简化很多。
模式匹配等功能特性可以轻松处理多种元素类型和多个渲染器。对元素结构的任何更改都可以限制在模式匹配代码中,将特定于渲染器的调用转发给具体的渲染器访问者。
除非它不能。在最简单的情况下,某些 类型的渲染器可能需要不同的方式来处理元素。一些渲染器可能需要多次访问元素。
或者在可能的情况下,数据的大小可能需要不同的策略。当想要导出 1M 行时,最好的选择是流式传输呈现的结果,而不是将它们缓存在内存中。 Excel 虽然是一个 ZIP 包,因此在导出之前需要收集结果。在这种情况下,即使结构和渲染器相同,不同的数据大小也需要不同的实现。
关于c# - Acyclic Visitor 相对于 Switch On 类型命令的优势,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68590896/
首先对WCF与WebService的定义进行概括,接着介绍了WCF的优势,最后就是对WCF与WebService两者的根本区别进行比较,具体内容如下 1、定义 1.WebService:严格来
关闭。这个问题是opinion-based .它目前不接受答案。 想改善这个问题吗?更新问题,以便可以通过 editing this post 用事实和引文回答问题. 5年前关闭。 Improve t
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 8 年前。 Improve
谁能告诉我将下一个脚本标记放在 元素之后和将它放在 元素之前有什么好处? (function (){ var script = document.createElement("script"); s
我最近遇到了一个 JavaScript 脚本,其中作者似乎试图避免在代码中使用字符串并将所有内容都分配给一个变量。 所以代替 document.addEventListener('click', (e
Scott Meyers 在“Effective Modern C++”中说表达式 Matrix sum = m1 + m2 + m3 + m4 (其中所有对象的类型均为 Matrix)“如果 ope
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
免责声明:我是网络开发的新手,所以请多多包涵... 堆栈:Angular 前端,后端的 Tornado(基于 python)网络服务器 我一直在使用 RxJs 和 WebSocket 成功地与后端通信
我一直在调查我的 Flutter 应用程序的 JSON 解析,并且有一个关于我无法解决的工厂构造函数的问题。我试图了解使用工厂构造函数与普通构造函数的优势。例如,我看到很多 JSON 解析示例,它们使
添加 SQL 后尝试打开 TadsQuery 时出现 5400 AE_INTERNAL_ERROR。当我将相同的 SQL 直接放在 TadsQuery 中时,没有错误。您的帮助文件指示我联系 Adva
关闭。这个问题是opinion-based .它目前不接受答案。 想改进这个问题?更新问题,以便 editing this post 可以用事实和引用来回答它. 8年前关闭。 Improve this
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 9 年前。 Improv
我想定义一个函数,当给定一个整数时,它将返回一个字符串。像这样: # Pseudocode function get_string(i) return my_string_array[i] end
我曾尝试在 Linux 和 Windows 上使用 DBD::Advantage,但没有成功。 Windows 版本附带了一个看似完整的安装程序,但它留下了 DBD-Advantage-8.10.ta
为什么说 NoSQL 在结构上比某些 SQL 数据库更快?假设我在某个 SQL 表的列上添加了一些索引。有人可以提供某种更快的查询吗? 我正在阅读有关 redis 的内容。 class User <
我的问题很简单:如果接口(interface)由单个类实现,那么使用接口(interface)有什么好处吗? 我一直认为只有当该接口(interface)有多个实现时,接口(interface)才是好
考虑这些(或多或少)等价的类型签名: f1 :: [a] -> Int -> a f2 :: Integral b => [a] -> b -> a f2 比 f1 更通用,这是一个很大的优势,但是
我们在 Scala 中部分应用了函数- def sum(a:Int,b:Int,c:Int) = a+b+c val partial1 = sum(1,_:Int,8) 我想知道使用部分应用函数的优点
你看下面的代码,我是如何使用 session 变量的;所以这三个问题是: 它们存放在哪里? (服务器或客户端) 它们对于每个网页访问者来说都是独一无二的吗? 我可以在完成工作后使用 ajax 或简单的
我知道这是一个常见问题,互联网上有关于此主题的资源,但我想从这个社区了解每个人在部署博客时对子域与子文件夹的想法(SEO 优势)。 谢谢。 最佳答案 我研究过一次,根据社区反馈以及权衡利弊,我会说去一
我是一名优秀的程序员,十分优秀!