如何使用MVC模型在Perl Tk(Tkx)GUI编程中避免全局变量

3
我有一个旧的、非常大的Perl Tk GUI应用程序,现在正在重构为Tkx。我想将界面分成几个软件包,以便可以模块化地建立应用程序UI,同时,我想保持视图与模型的分离,使用控制器来提供两者之间的接口。
在我看来,设计这个应用程序的唯一方式是使用两个巨大的全局变量,一个保存模型($MODEL),另一个保存跨越许多软件包的小部件的引用($UI)。然后,控制器使用以下一系列命令来相互配合:
$UI->{'entry_widget'}->configure(-variable=>\$MODEL->{'entry_value'});
$UI->{'button_widget'}->configure(-command=>sub {$MODEL->{'entry_value'} = "New Value"} );

我的问题是:有没有更好的设计应用程序的方法,避免使用这两个大全局变量($UI和$MODEL)?任何建议都非常欢迎。

Valamas,你为什么要-1这个? - LozzerJP
2个回答

1

你不是想要避免全局变量,而是想要使用方法,即用$model->data$self->model->data替换$hashref->{data},其中$model$self是传递给信号处理程序/回调/命令的参数(或像Axeman演示的“单例”),而不是直接访问的哈希表。

你使用方法来修改$model,因为方法可以拒绝使用荒谬/不正确的数据更新模型,它们确保你不会试图用垄断货币支付

你的应用程序将始终创建一个模型变量和一个视图变量,并通过参数传递将它们连接起来(可能通过中介控制器)

它们不必是Perl意义上的实际全局变量(Coping with Scoping),它们可以是my $variables,并且仍然可以像现在一样通过闭包正常工作,从而避免了http://perl.plover.com/varvarname.html的问题,但你不会得到智能模型的好处,它们知道需要哪种燃料(柴油还是无铅汽油);将视图连接到模型需要更多的输入

另请参见什么是模型视图控制器?的答案和链接


Gangrene,这很有帮助。我现在也看到了Axeman的解决方案如何适用。那么,这是否意味着我们现在将两个大的单例引入控制器,以提供实现上述配置的方法? - LozzerJP

1

我认为包方法是一种使某些东西全局可用但不是全局变量的方法。所以像这样的东西会起作用:

package MVC;

use strict;
use warnings;
use Scalar::Util qw<refaddr>;

my %MVCs;

sub _domain { 
    my ( $domain_name, $ref, $value ) = @_;
    my $r = \$MVCs{ $key }{ $domain_name };
    return unless $$r or ref( $value );
    if ( ref $value ) {
        $$r = $value;
    }
    return $$r;
}

sub model      { shift; return _domain( 'model', @_ ); }
sub controller { shift; return _domain( 'controller', @_ ); }
sub view       { shift; return _domain( 'view', @_ ); }

因此在包之外,您只需要调用这个函数:

my $controller = MVC->controller( $self ); 

获取与对象关联的控制器。

您甚至可以将一些导出逻辑放入访问器中,例如:

unless ( $ref->can( $domain_name )) { 
    not strict 'refs';
    *{ ref( $ref ) . "::$domain_name" } 
        = sub { _domain( $domain_name, $ref ) }
        ;
}

所以你可以简单地这样做:

$self->view->view_method( @args );

谢谢 Axeman。我以前没有使用过 Scalar::Util qw<refaddr>。我想在完全理解你的建议之前需要多读一些相关资料! - LozzerJP

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接