访客模式和封装

时间:2011-08-14 20:25:13

标签: c# design-patterns

访问者设计模式是一种将算法与其操作的对象结构分离的方法。这是它的官方定义。我试图弄清楚这是如何不破坏封装的。如果说例如我为不同类型的银行账户[保存/固定/当前]实现抽象类账户有不同类型的类,我应该将计算利息方法作为抽象方法放在抽象账户类中,还是我发送账户?键入访问者实现并在那里计算?

方法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仍然可以称为访客模式吗?

感谢阅读...

5 个答案:

答案 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可能更具模块性和有效性。他们做同样的事情,事后为类型添加功能,但你不再需要在一个地方随身携带所有可能的实现。