是否有一种干净的模式允许访问者一次访问两个可访问对象?
例如,如果我的访问者是二进制加法运算符,它需要知道两个可访问输入的数据类型。
我在下面提供了一个解决方案,但感觉它很混乱,因为它要求所有可访问对象都包含所有具体可访问对象的重载,并且还要求访问者包含虚拟访问来解析 r-visitable。
如果访客模式不适合此目的,是否还有其他更适合的模式?
谢谢你
using System;
using System.Diagnostics;
namespace VisitorTest
{
class Program
{
static void Main(string[] args)
{
// Given two visitable objects...
IVisitable stringVisitable = new StringVisitable("987");
IVisitable numberVisitable = new NumberVisitable(123);
// And a visitor that performs a "lVisitable + rVisitable" operation...
PlusOpVisitor plusOpVisitor = new PlusOpVisitor();
// Test "string + string" == "987987"
Console.WriteLine
( stringVisitable.PairAccept
( plusOpVisitor
, stringVisitable
)
);
// Test "string + number" == (convert both to string) == "987123"
Console.WriteLine
( stringVisitable.PairAccept
( plusOpVisitor
, numberVisitable
)
);
// Test "number + string" == (convert both to number) == #1110
Console.WriteLine
( numberVisitable.PairAccept
( plusOpVisitor
, stringVisitable
)
);
// Test "number + number" == #246
Console.WriteLine
( numberVisitable.PairAccept
( plusOpVisitor
, numberVisitable
)
);
}
}
interface IPairVisitor
{ // Messy: Dummies, just to know the l-visitable type, while visiting the r-visitable
IVisitable Visit(StringVisitable lVisitable, IVisitable rVisitable);
IVisitable Visit(NumberVisitable lVisitable, IVisitable rVisitable);
// Actual visitor operations, what to do in each case
IVisitable Visit(StringVisitable lVisitable, StringVisitable rVisitable);
IVisitable Visit(NumberVisitable lVisitable, StringVisitable rVisitable);
IVisitable Visit(StringVisitable lVisitable, NumberVisitable rVisitable);
IVisitable Visit(NumberVisitable lVisitable, NumberVisitable rVisitable);
}
interface IVisitable
{ // Resolve the l-visitable, include the unresolved r-visitable
IVisitable PairAccept(IPairVisitor visitor, IVisitable rVisitable);
// Messy: Resolve the r-visitable, include the previously resolve l-visitable
// The visitable-object must know the type of all objects that it can be
// accepted against (normal visitor pattern doesn't have this restriction)
IVisitable PairAccept(IPairVisitor visitor, StringVisitable lVisitable);
IVisitable PairAccept(IPairVisitor visitor, NumberVisitable lVisitable);
}
class PlusOpVisitor : IPairVisitor
{ // Repeat the accept, but for the other visitable
public IVisitable Visit
( StringVisitable lVisitable
, IVisitable rVisitable
){ return rVisitable.PairAccept(this, lVisitable);
}
public IVisitable Visit
( NumberVisitable lVisitable
, IVisitable rVisitable
){ return rVisitable.PairAccept(this, lVisitable);
}
// Perform the actual operation for each pair
public IVisitable Visit
( StringVisitable lVisitable
, StringVisitable rVisitable
){ return new StringVisitable
( string.Concat
( lVisitable.Value
, rVisitable.Value
)
);
}
public IVisitable Visit
( StringVisitable lVisitable
, NumberVisitable rVisitable
){ return new StringVisitable
( string.Concat
( lVisitable.Value
, rVisitable.Value.ToString()
)
);
}
public IVisitable Visit
( NumberVisitable lVisitable
, StringVisitable rVisitable
){ return new NumberVisitable
( lVisitable.Value
+ int.Parse(rVisitable.Value)
);
}
public IVisitable Visit
( NumberVisitable lVisitable
, NumberVisitable rVisitable
){ return new NumberVisitable
( lVisitable.Value
+ rVisitable.Value
);
}
}
class StringVisitable : IVisitable
{ public StringVisitable
( string value
){ _value = value;
}
public string Value
{ get { return _value; }
}
// Visit as an l-visitable, r-visitable still unresolved
public IVisitable PairAccept
( IPairVisitor visitor
, IVisitable rVisitable
){ return visitor.Visit(this, rVisitable);
}
// Visit as an r-visitable, l-visitable resolved
public IVisitable PairAccept
( IPairVisitor visitor
, StringVisitable lVisitable
){ return visitor.Visit(lVisitable, this);
}
public IVisitable PairAccept
( IPairVisitor visitor
, NumberVisitable lVisitable
){ return visitor.Visit(lVisitable, this);
}
public override string ToString()
{ return string.Concat("\"", _value, "\"");
}
private string _value;
}
class NumberVisitable : IVisitable
{ public NumberVisitable
( int value
){ _value = value;
}
public int Value
{ get { return _value; }
}
public IVisitable PairAccept
( IPairVisitor visitor
, IVisitable rVisitable
){ return visitor.Visit(this, rVisitable);
}
public IVisitable PairAccept
( IPairVisitor visitor
, StringVisitable lVisitable
){ return visitor.Visit(lVisitable, this);
}
public IVisitable PairAccept
( IPairVisitor visitor
, NumberVisitable lVisitable
){ return visitor.Visit(lVisitable, this);
}
public override string ToString()
{ return string.Concat("#", _value);
}
private int _value;
}
}
这可以使用典型的访问者模式来实现,但三重调度除外,其中创建一个访问者来单独解析 LHS,同时存储未解析的 RHS,然后创建另一个访问者来解析 RHS,同时存储已解析的 LHS。
using System;
using System.Diagnostics;
namespace VisitorTest
{
class Program
{
static void Main(string[] args)
{
// Given two visitable objects...
IAcceptor stringAcceptor = new StringAcceptor("987");
IAcceptor numberAcceptor = new NumberAcceptor(123);
// And a visitor that performs a "lVisitable + rVisitable" operation...
PlusOpVisitor plusOpVisitor = new PlusOpVisitor();
// Test "string + string" == "987987"
Console.WriteLine
( plusOpVisitor.Visit
( stringAcceptor
, stringAcceptor
)
);
// Test "string + number" == (convert both to string) == "987123"
Console.WriteLine
( plusOpVisitor.Visit
( stringAcceptor
, numberAcceptor
)
);
// Test "number + string" == (convert both to number) == #1110
Console.WriteLine
( plusOpVisitor.Visit
( numberAcceptor
, stringAcceptor
)
);
// Test "number + number" == #246
Console.WriteLine
( plusOpVisitor.Visit
( numberAcceptor
, numberAcceptor
)
);
}
}
// Typical visitor/acceptor, no strong typing exposed
interface IVisitor
{ IAcceptor Visit(StringAcceptor acceptor);
IAcceptor Visit(NumberAcceptor acceptor);
}
interface IAcceptor
{ IAcceptor Accept(IVisitor visitor);
}
class PlusOpVisitor
{
public IAcceptor Visit(IAcceptor lAcceptor, IAcceptor rAcceptor)
{ return lAcceptor.Accept(new LHSVisitor(rAcceptor));
}
// Visitor to handle the left-hand-side, holding the right-hand-side
// until the LHS is resolvd
private class LHSVisitor : IVisitor
{
public LHSVisitor(IAcceptor rAcceptor)
{ _rAcceptor = rAcceptor;
}
// With the LHS resolved, create a new visitor to handle the RHS
// storing the resolve LHS
public IAcceptor Visit(StringAcceptor lAcceptor)
{ return _rAcceptor.Accept(new LHSStringRHSVisitor(lAcceptor));
}
public IAcceptor Visit(NumberAcceptor lAcceptor)
{ return _rAcceptor.Accept(new LHSNumberRHSVisitor(lAcceptor));
}
private readonly IAcceptor _rAcceptor;
}
// Visitor to handle the RHS when the LHS has been resolved as a string
private class LHSStringRHSVisitor : IVisitor
{
public LHSStringRHSVisitor(StringAcceptor lAcceptor)
{ _lAcceptor = lAcceptor;
}
// Now both LHS and RHS are resolved
public IAcceptor Visit(StringAcceptor rAcceptor)
{ return new StringAcceptor
( string.Concat(_lAcceptor.Value, rAcceptor.Value)
);
}
public IAcceptor Visit(NumberAcceptor rAcceptor)
{ return new StringAcceptor
( string.Concat(_lAcceptor.Value, rAcceptor.Value.ToString())
);
}
private readonly StringAcceptor _lAcceptor;
}
private class LHSNumberRHSVisitor : IVisitor
{
public LHSNumberRHSVisitor(NumberAcceptor lAcceptor)
{ _lAcceptor = lAcceptor;
}
public IAcceptor Visit(StringAcceptor rAcceptor)
{ return new NumberAcceptor
( _lAcceptor.Value + int.Parse(rAcceptor.Value)
);
}
public IAcceptor Visit(NumberAcceptor rAcceptor)
{ return new NumberAcceptor(_lAcceptor.Value + rAcceptor.Value);
}
private readonly NumberAcceptor _lAcceptor;
}
}
class StringAcceptor : IAcceptor
{ public StringAcceptor
( string value
){ _value = value;
}
public string Value
{ get { return _value; }
}
public IAcceptor Accept(IVisitor visitor)
{ return visitor.Visit(this);
}
public override string ToString()
{ return string.Concat("\"", _value, "\"");
}
private string _value;
}
class NumberAcceptor : IAcceptor
{ public NumberAcceptor
( int value
){ _value = value;
}
public int Value
{ get { return _value; }
}
public IAcceptor Accept(IVisitor visitor)
{ return visitor.Visit(this);
}
public override string ToString()
{ return string.Concat("#", _value);
}
private int _value;
}
}