访问者设计模式是一种将算法与其操作的对象结构分离的方法。这是它的官方定义。我试图弄清楚这是如何不破坏封装的。如果说例如我为不同类型的银行账户[保存/固定/当前]实现抽象类账户有不同类型的类,我应该将计算利息方法作为抽象方法放在抽象账户类中,还是我发送账户?键入访问者实现并在那里计算?
方法1:访客实施是否应负责计算不同帐户类型的兴趣?
public interface IInterestVisitor
{
void GetInterest(Savings AccountType);
void GetInterest(Fixed AccountType);
void GetInterest(Current AccountType);
}
方法2:或者Account类的实施者应该这样做吗?
public abstract class Account
{
public abstract void AcceptCalculateInterestVisitor(IInterestVisitor iv);
public abstract int CalculateInterestAmount();
}
如果我使用方法1,即上面实现IInterestVisitor的访问者实现,那么计算兴趣的工作将被委托给访问者类。使用这种方法,如果我添加其他帐户类型,那么每次新帐户出现时我都需要修改访问者实施。
但是,如果我将兴趣计算位留在方法2中的抽象Account类实现中,那么在我看来[如果我在这里纠正错误我]我没有打破封装。此外,修改的代码较少,因为我所做的只是添加一个新类,并让访问者实现如下所示的界面
public interface IInterestVisitor
{
void GetInterest(Account AccountType);
}
public class InterestVisitor : IInterestVisitor
{
public void GetInterest(Account AccountType)
{
int i = AccountType.CalculateInterestAmount();
Console.WriteLine(i);
}
}
正如您所看到的,使用方法2的兴趣访问者类不需要修改。方法1是否打破了封装?方法2仍然可以称为访客模式吗?
感谢阅读...
答案 0 :(得分:5)
我根本没有看到访问者的需要 - 您的方法完美地说明您可以使用子类可以实现的CalculateInterestAmount
方法使用多态来解决此问题。
在我看来,你真的需要一个非常引人注目的案例来考虑使用访问者模式 - 大多数时候其他解决方案更直接,更自然地适合。
话虽如此,你的第2版实际上只是使用多态 - 以这种方式使用Visitor没有任何好处。版本1更好地说明了访问者的“双重调度”方法,这就是访问者通常的工作方式。
答案 1 :(得分:5)
访问者允许您在不更改类的情况下定义新操作 它运作的元素。
在提供的代码中,我没有看到您需要更改对象以满足您的需求,Visitor
基本上做的是通过使用提供的Properties
/ {{1来更改对象状态对象本身的/ Methods
。
因此,通过我,您的代码可以适合访问者模式,如果这实际上是问题,也会导致模式是指南和不严格的规则。
我个人会选择第二种方式,因为它更清晰,面向OOP。
问候。
答案 2 :(得分:2)
以下是访问者以多种方式报告状态而不影响原始帐户类/对象的示例。在诸如PrintReport(),SmsReport()等虚拟函数中替换Visitors的功能是不好的,在这些函数中你将有太多的方法来实现。使用Visitor,您可以以其他方式扩展功能而不会影响对象。 我举了这个例子,因为你已经询问/提到了访问者的位置 ..在我看来,访问者不适合你提到的方法。
public interface IAccount
{
//Below are properties and methods which you expose outside
// accoring to your class/domain
double Interest { get;}
//double Balance { get;}
//ContactInfo Contact{ get;}
void Visit(IAccountVisitor visitor);
}
public class AccountBase
{
}
public class FixedDeposit: AccountBase, IAccount
{
double Interest {
get{
return CalculateInterest(); //don't change object state
}
;}
protected double CalculateInterest()
{
return <<your expression to calculate>>;
}
public void Visit(IAccountVisitor visitor)
{
visitor.Visit(this);
}
}
public class XYZBlashBlahDeposit: AccountBase, IAccount
{
public void Visit(IAccountVisitor visitor)
{
visitor.Visit(this);
}
}
...
...
public interface IAccountVisitor
{
//void Report(IAccount account); //This is prefered and safe and
void Visit(Account account);
}
public class PrintReportVisitor: IAccountVisitor
{
public void Visit(Account account)
{
//Get object information
//Print to printer
}
}
public class DisplayReportVisitor: IAccountVisitor
{
public void Visit(Account account)
{
//Get object information
//Display on monitor
}
}
public class ReportAudioVisitor: IAccountVisitor
{
public void Visit(Account account)
{
//Get object information
//Read out the information
}
}
public class SmsReportVisitor: IAccountVisitor
{
public void Visit(Account account)
{
//Get object information
//Send SMS to my mobile registered with account
}
}
public class EmailReportVisitor: IAccountVisitor
{
public void Visit(Account account)
{
//Get object information
//Send Email to my email registered with account
}
}
查看您是否有申请,如何使用访客。
public class ReportViewer: UserControl
{
IAccount _Account;
public ReportViewer(IAccount account)
{
this._Account = account;
InitializeComponent();
}
void btnClick_Print()
{
_Account.Visit(new PrintReportVisitor());
}
void btnClick_ViewReport()
{
_Account.Visit(new DisplayReportVisitor(this));
}
void btnClick_SendSMS()
{
_Account.Visit(new SMSReportVisitor(this));
}
void btnClick_SendEmail()
{
_Account.Visit(new EmailReportVisitor(this));
}
}
答案 3 :(得分:1)
访问者在逻辑可能依赖于Visted类型的类型以及访问者类型的情况下提供帮助 - 这是构建双重调度的一种方式。
在您的方案中,您需要计算利息,这是帐户的合理操作,并且非常明确地取决于帐户的性质。因此,当你指出访客没有增加价值时,似乎不合适。
现在让我们进行一些稍微复杂的计算。想象一下,你有各种储蓄账户,有些是定期储蓄账户,有些是以股市行为为条件,有些则有人寿保险。对于此类账户,我们有获取数据的方法,如利率,奖金率,定期付款时间表,到期日等,每种账户都有不同类型。
我们还需要执行某些计算:特定日期帐户的预测值是多少?现在贷款合并的账户价值是多少?如果今天关闭,该帐户的价值是多少?对于每种帐户类型,需要以不同方式(可能使用原始数据获取者)进行此类计算。
现在我们在访客模式中看到了一些价值。当我们设计一个新的有趣计算时,我们编写了一个新的访问者,我们不需要发布新版本的帐户类。
答案 4 :(得分:1)
@Tigran指出,当你无法改变被访问的类时,访问者很有用。
由于您使用的是C#,因此使用Extension Methods可能更具模块性和有效性。他们做同样的事情,事后为类型添加功能,但你不再需要在一个地方随身携带所有可能的实现。