回答
您可以通過提供適當的getter/setter方法來包裝類/實例變量。在內部不應該直接訪問,特別是從類本身以外(這是明智的類內沒有這樣做要麼,除了爲維護特定屬性的實際方法,這裏是一個非常簡單的例子:
use warnings;
use strict;
package A;
sub new {
my ($class, %args) = @_;
my $self = bless {}, $class;
$self->x($args{x});
$self->y($args{y});
return $self;
}
sub x {
my ($self, $x) = @_;
$self->{x} = $x if defined $x;
return $self->{x} // 1;
}
sub y {
my ($self, $y) = @_;
$self->{y} = $y if defined $y;
return $self->{y} // 2;
}
package main;
my $obj = A->new(x => 5, y => 3);
print $obj->x ."\n";
print $obj->y ."\n";
現在,您可以輕鬆地做到print $obj->{x}
,但這就是您的問題所在。當代碼比這複雜得多時,出於某種原因,您想將x
屬性名稱更改爲foo
,但保留x()
方法$obj->{x}
現在是undef
,因爲它從未設置過。始終使用提供的方法來訪問類/對象的屬性。諸如此類的封裝是面向對象編程的主要部分。
DRY - 除非您通過寫入訪問者浪費的行數獲得付款。 –
原理很好,但是當調用者嘗試將值設置爲undef(有意或無意)時,那些訪問器失敗的情況非常糟糕。你應該檢查提供的參數個數('my $ self = shift; $ self - > {x} = shift if @_; return $ self - > {x};')並且* add *檢查和轉換if期望。 – ikegami
你可以,但你可能不應該。這裏的問題是 - 如果你直接訪問類中的變量,那麼你就可以。你可以通過一些解決方法來避免這種情況 - 這就是Moose進來的地方。
而且還有一些有點冒險的技巧,像內向外對象(我認爲這些技巧不再常見 - Perl最佳實踐在幾年前提倡他們)或使用匿名哈希來維持狀態。
但是失敗 - 爲什麼不使用存取器,並使用'AUTOLOAD'自動生成一個存取器。
#!/usr/bin/env perl
package MyClass;
use strict;
use warnings;
use vars '$AUTOLOAD';
sub AUTOLOAD {
my ($self) = @_;
my $subname = $AUTOLOAD =~ s/.*:://r;
if ($self -> {$subname}) {
return $self -> {$subname};
}
warn "Sub called $subname was called\n";
return "$subname";
}
sub new {
my ($class) = @_;
my $self = {};
bless $self, $class;
}
package main;
use strict;
use warnings;
my $object = MyClass -> new;
$object -> {var} = "fleeg";
print "Undef fiddle was: ", $object -> fiddle,"\n";
print "But 'var' was: ", $object -> var,"\n";
這有同樣的問題,因爲更改方法名稱可能會導致事情中斷。然而,它的優點是你可以處理'無效的'方法調用,不過你喜歡。
但是對於大多數使用情況來說,真正顯式的'get'和'set'方法是更好的選擇。
'AUTOLOAD'將是我爲此推薦的最後一件事。爲定義良好的列表生成訪問器不需要更多的代碼,並且更健壯。除此之外,還有'Object :: Accessor','Class :: Accessor :: *','Moo(se)?, etc. –
http://perltraining.com是一個很好的內外物體入門書。au/tips/2006-03-31.html雖然現在有很多模塊可以做到這一點,但我認爲它很好地解釋了這個概念。 –
- 1. 在JavaScript中捕獲對未定義屬性的訪問
- 2. 在Javascript中訪問未定義的值
- 3. Perl - 在類中捕獲未定義的鍵?
- 4. 未捕獲ReferenceError:PushNotification未定義
- 5. 未捕獲ReferenceError:未定義
- 6. 未捕獲ReferenceError:sendcard未定義
- 7. 未捕獲ReferenceError:myFunction未定義
- 8. 未捕獲ReferenceError:rgba未定義
- 9. 未捕獲ReferenceError:Dygraph未定義
- 10. 未捕獲ReferenceError:AdjustIframeHeightOnLoad未定義
- 11. 未捕獲ReferenceError:calcDollar未定義
- 12. 未捕獲ReferenceError:toggleSidebar未定義
- 13. 未捕獲ReferenceError:「$未定義」
- 14. 未捕獲ReferenceError:ctx未定義
- 15. 未捕獲ReferenceError:doit未定義
- 16. 未捕獲ReferenceError:getProduct未在HTMLButtonElement.onclick處定義
- 17. jQuery在Grails-未捕獲ReferenceError:$未定義
- 18. 未捕獲的ReferenceError:函數未定義 - WordPress的特定問題
- 19. 獲取未捕獲ReferenceError:未定義cordova
- 20. 未捕獲的ReferenceError:未定義正文
- 21. 未捕獲的ReferenceError:未定義
- 22. 未捕獲的ReferenceError:未定義toggleTest
- 23. 未捕獲ReferenceError:未定義的錯誤
- 24. 未捕獲的ReferenceError:isApp未定義
- 25. 「消息」:「未捕獲的ReferenceError:myFunction未定義」?
- 26. 未捕獲的ReferenceError:__WEBPACK_EXTERNAL_MODULE_XX__未定義
- 27. 未捕獲的ReferenceError:X未定義
- 28. 未捕獲的ReferenceError:DocxGen未定義
- 29. 解決未捕獲的ReferenceError:未定義(...)
- 30. 未捕獲的ReferenceError:url未定義
你不應該去內部。改爲定義訪問器方法。你的對象的內部實現可以改變。改爲使用定義的API。另外,這是一個對象,而不是一個類。 – simbabque
您可以防止添加密鑰,但這並不妨礙從不存在的密鑰讀取。正如你暗示的那樣,這會返回'undef',如果你期待一個值,這應該會導致錯誤。 – ikegami
你可以使你的對象基於數組,而不是基於散列,以使它更難使用不正確(至少是無意)。還有其他一些技巧,但在某些時候,你只是說:「如果你不使用我們提供的訪問器,如果你被咬了,這是你自己的錯誤!」 – ikegami