Es gibt Fälle, wo try{}catch{} möglich ist.
Es geht nicht um möglich (möglich ist es immer!), sondern um nötig.
Jedoch fand ich besher noch keinen solchen Fall. Ich war mit eval{} or do{} auch gut bedient.
Dieser Ansatz hat eine Schwachstelle, die mir persönlich schon zweimal in meiner Karriere begegnet ist; Stichpunkt $EVAL_ERROR im Destruktor. Dieses Problem zeigt seine hässlichen Fratze häufig genug, dass endgültig seine korrekte Lösung formell als Modul kodifiziert wurde.
Lass mich nun deinen Programmschnippsel kritisieren. Auf den ersten Blick sieht man gleich den größten Schwachpunkt, nämlich dass Fehlermeldungen als Funktionsergebnis zurückgegeben werden. Warum das furchtbar ist, ist auf dutzenden Seiten in Kapitel 13 von Perl Best Practices breitgetreten, werde ich hier nicht wiederholen. Dass die Fehlermeldungen auch noch Markup enthalten und somit die Trennung von View und Controller unterlaufen, ist das bizarre Sahnehäubchen. Ich wette, diese Meldungen werden zum Rendering durchgereicht und verbauen auch so die Möglichkeit zur I18n in der Zukunft. Du musst lernen, zu entkoppeln!
Der nächste Schwachpunkt ist, dass die Plugins an einem bestimmten Ort installiert werden müssen, anstatt das Standardsystem Perlincludepfad zu verwenden. Daraus ergibt sich auch, dass Plugins nicht hierarchisch gestaffelt werden können. Diesen Punkt werde ich nicht in meinem Gegenbeispiel ausbessern, um die Anwendung von Exceptionobjektattributen demonstrieren zu können.
Unter der Annahme, dass plötzlich die bereits vorhanden exquisiten Pluginsystem wie MooseX::Object::Pluggable oder Module::Pluggable aufhören zu existieren und ich sie nicht verwenden könnte, würde ich es viel robuster so schreiben:
package App::CMS::EHF;
use Exception::Class (
'App::CMS::EHF::Exception::LoadPlugin' => {
fields => [qw(plugin_file_name does_not_exist not_readable)],
alias => 'throw_load_plugin',
},
);
use Module::Load qw(load);
use Moose qw();
use MooseX::Params::Validate qw(validated_hash);
use Moose::Util::TypeConstraints qw(subtype as where);
use Try::Tiny qw(try catch);
subtype 'App::CMS::EHF::Meta::Types::PluginName' => as 'Str' => where { /\A \w+ \z/msx };
sub load_plugin {
my ($self, %params) = @_;
%params = validated_hash([%params], plugin_name => {isa => 'App::CMS::EHF::Meta::Types::PluginName',},);
my $plugin_file_name = $Path{cgidir} . "EHFPlugins/$params{plugin_name}.pm";
try {
load($plugin_file_name);
} catch {
if (!-e $plugin_file_name) {
throw_load_plugin plugin_file_name => $plugin_file_name, does_not_exist => 1
} elsif (!-r $plugin_file_name) {
throw_load_plugin plugin_file_name => $plugin_file_name, not_readable => 1
} else {
throw_load_plugin plugin_file_name => $plugin_file_name
}
}
}
package an_anderer_Stelle;
my $stash;
try {
...->load_plugin(plugin_name => 'FooBarQuux');
} catch {
if ($_->does_not_exist) {
$stash->{error_message} = sprintf('Plugin %s konnte nicht gefunden werden.', $_->plugin_file_name, );
} elsif ($_->not_readable) {
$stash->{error_message} = sprintf('Plugin %s hat keine Leserechte.', $_->plugin_file_name);
} else {
$stash->{error_message} = sprintf('Plugin %s konnte unerwartet nicht gestartet werden.', $_->plugin_file_name);
$stash->{error_trace} = $_->trace->as_string;
}
};
# $stash ins Templatingsystem übermitteln.