moo*_*dle 7 c# visitor-pattern
我是这种模式的新手,可以请别人帮助我吗?
我有一个像这样的对象:
public class Object
{
public string Name { get; set; }
public object Value { get; set; }
public List<Object> Childs { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
这是一个JSON示例:
{
"Name": "Method",
"Value": "And",
"Childs": [{
"Name": "Method",
"Value": "And",
"Childs": [{
"Name": "Operator",
"Value": "IsEqual",
"Childs": [{
"Name": "Name",
"Value": "5",
"Childs": []
}]
},
{
"Name": "Operator",
"Value": "IsEqual",
"Childs": [{
"Name": "Name",
"Value": "6",
"Childs": []
}]
}]
},
{
"Name": "Operator",
"Value": "IsEqual",
"Childs": [{
"Name": "Name",
"Value": "3",
"Childs": []
}]
}]
}
Run Code Online (Sandbox Code Playgroud)
我的问题如何使访问者模式,以获得这个最后的字符串:
(Name IsEqual 3)And((Name IsEqul 5)And(Name IsEqual 6))
Run Code Online (Sandbox Code Playgroud)
Meh*_*taş 20
要实现访问者模式,您需要两个简单的接口
IVisitable使用Accept具有IVisitor参数的方法.IVisitorVisit每个实现的方法都很多IVisitable因此访问者模式的基本思想是根据实现的类型动态地改变行为.
对于你的情况,你想要访问的东西(可访问的)是Object显然没有不同衍生物的类,你想根据属性值而不是类型来改变行为.所以访客模式不是你真正需要的,我强烈建议你用递归方法来考虑答案.
但是如果你真的想在这里使用访问者模式,它可能看起来像这样.
interface IVisitable { void Accept(IVisitor visitor); }
interface IVisitor {
void VisitAnd(Object obj);
void VisitEquals(Object obj);
}
Run Code Online (Sandbox Code Playgroud)
由于Object该类是一个简单的POCO,我假设你不想实现一个接口并在这个类中添加一个方法.所以你需要一个适配器,该适配对象Object,以IVisitable
class VisitableObject : IVisitable {
private Object _obj;
public VisitableObject(Object obj) { _obj = obj; }
public void Accept(IVisitor visitor) {
// These ugly if-else are sign that visitor pattern is not right for your model or you need to revise your model.
if (_obj.Name == "Method" && _obj.Value == "And") {
visitor.VisitAnd(obj);
}
else if (_obj.Name == "Method" && _obj.Value == "IsEqual") {
visitor.VisitEquals(obj);
}
else
throw new NotSupportedException();
}
}
}
public static ObjectExt {
public static IVisitable AsVisitable(this Object obj) {
return new VisitableObject(obj);
}
}
Run Code Online (Sandbox Code Playgroud)
最后,访问者的实现可能看起来像这样
class ObjectVisitor : IVisitor {
private StringBuilder sb = new StringBuilder();
public void VisitAnd(Object obj) {
sb.Append("(");
var and = "";
foreach (var child in obj.Children) {
sb.Append(and);
child.AsVisitable().Accept(this);
and = "and";
}
sb.Append(")");
}
public void VisitEquals(Object obj) {
// Assuming equal object must have exactly one child
// Which again is a sign that visitor pattern is not bla bla...
sb.Append("(")
.Append(obj.Children[0].Name);
.Append(" Equals ");
.Append(obj.Children[0].Value);
.Append(")");
}
}
Run Code Online (Sandbox Code Playgroud)
JSON清楚地表示令牌树(可能由解析器生成)。
访客模式使用多态。
为了被访问者模式使用,必须对它进行反序列化以获得具有不同访问行为的对象:
然后,IVisitor应该为每个对象实现Visit方法:
public interface IVisitor
{
void Visit(MethodToken token) { /* */ }
void Visit(OperatorToken token) { /* */ }
void Visit(NameToken token) { /* */ }
}
public interface IVisitable
{
void Accept(IVisitor visitor);
}
public class MethodToken : IVisitable
{
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
}
Run Code Online (Sandbox Code Playgroud)
附加说明:
Object是一个非常糟糕的名字,尤其是在C#中Object,每个类的基类都是这样,更不用说冲突了,它没有传达任何特殊含义……令牌呢?
public class Token
{
public string Name { get; set; }
public object Value { get; set; }
public List<Token> Childs { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
访客目的
如果您不知道何时/为什么使用螺丝刀,则不要使用螺丝刀(这样会很危险)。
访客模式非常有用,可以避免“难看” /难以维护/痛苦地阅读许多开关情况,甚至更糟,if else if else同时还为您提供了强大的类型检查优势。它还有助于将相关代码(高内聚性)保持在一个类(Visitor)中。当然,一旦实现,对象树(此处为令牌)就可以被多种类型的访问者访问,只要它们实现了IVisitor接口即可。
在您的情况下,您必须首先将它们转换Token为的强子类型 Token(通过字典映射,以避免任何if / switch或自定义反序列化)