문제의 역사:
Perl 5의 첫 번째 출시 이후 언어는 패키지와의 참조 결합(blessing) 메커니즘을 통해 기본적인 객체 모델을 지원했습니다. Perl에서 객체는 bless 함수에 의해 "신성화된" 일반 참조(해시, 배열 또는 스칼라)에 해당하며, 이는 시스템이 패키지(namespace)를 통해 메서드를 찾을 수 있도록 합니다.
문제:
신성화되지 않은 참조는 일반 데이터 구조로 남아 있으며, 이에 메서드를 호출하려고 하면 런타임 오류가 발생합니다. 프로그래머는 참조의 유형에 대한 적절한 주의 없이 객체의 내부를 실수로 조작할 위험이 있어 캡슐화를 위반할 수 있습니다. 또한, 제어 없이 "원시" 포인터 해제 연산 {} 또는 @{}을 사용하는 것은 어렵게 잡히는 버그로 이어질 수 있습니다.
해결책:
객체를 bless를 통해 올바르게 생성한 다음, Scalar::Util 모듈을 사용하여 유형을 확인하면서 데이터에 접근할 때 참조 해제를 수행하는 것이 좋습니다 (ref, blessed). 클래스의 메서드 외부에서 구조체에 대한 직접 접근(예: $obj->{key})을 피하고, 제어를 위한 접근자(getter/setter)를 구현하는 것이 좋습니다.
코드 예시:
package Animal; sub new { my $class = shift; my $self = { name => shift }; bless $self, $class; return $self; } sub name { my $self = shift; $self->{name} = shift if @_; return $self->{name}; } my $dog = Animal->new("Rex"); print $dog->name; # 접근자를 통한 안전한 접근 print $dog->{name}; # 직접 접근, 권장되지 않음
주요 특징:
blessed 또는 ref를 통해 발견된 버그 수를 줄여줍니다.bless 없이 일반 해시에 대한 링크를 객체로 사용할 수 있나요?
아니요, bless를 호출하지 않으면 참조는 패키지와의 연관성이 없으며, Perl은 메서드를 찾지 못해 오류 "Can't call method"를 발생시킵니다. 내부 구조는 동일하더라도 말이죠.
변수가 진짜 객체인지 단순한 참조인지 안전하게 확인하는 방법은 무엇인가요?
Scalar::Util::blessed($obj) 기능을 사용하여 참조가 신성화되었는지를 확인합니다. 신성화된 참조만 객체로 간주됩니다.
use Scalar::Util 'blessed'; my $obj = {}; print blessed($obj) ? 'yes' : 'no'; # 'no'가 출력됩니다.
비객체 참조에서 메서드를 호출해도 오류가 발생하나요?
비신성화된 참조에서 메서드를 호출하면 치명적인 오류가 발생합니다: Perl은 참조가 패키지를 알아야 한다고 예상합니다. AUTOLOAD 메커니즘 및 동적 로드와 같은 예외가 있지만, 이는 오류를 초래하는 안티 패턴입니다.
젊은 개발자가 수동으로 구조체 $person = {name => 'Vasya'}를 생성하고 bless를 호출하는 것을 잊어버려 $person->name()을 호출하려고 해 런타임 오류가 발생합니다.
장점:
단점:
코드에서는 항상 객체를 생성할 때 bless를 호출하고 데이터에 접근할 때는 오로지 name() 메서드만 사용합니다. blessed()를 통한 검사로 오류를 메서드 실행 전 처리할 수 있습니다.
장점:
단점: