InsideOut Object
펄의 일반적인 [오브젝트] 는 그 오브젝트를 만드는 블레스된 레퍼런스가 가리키는 데이터 구조에 오브젝트 데이터를 저장한다.
*인사이드-아웃 오브젝트*는 아예 데이터구조를 사용하지 않는다. 대신 클래스에 멤버 변수마다 하나의 렉시컬 해쉬 변수를 만든다.
클래스의 인스턴스들은 멤버 변수의 값을 자신의 블레스된 레퍼런스의 메모리 주소를 키로 이용하여 이 해쉬 변수에 저장할 수 있다.
(메모리 주소는 "
Scalar::Util 모듈의
"refaddr" 값을 사용하여 정한다.)
예)
전통적인 해쉬 기반 오브젝트
%PERL%
package
MyClass? ;
sub new {
my $class = shift;
my $self = {
name => 'pung96',
}
return bless $self, $class;
}
package main;
my $obj =
MyClass? ->new;
print $obj->{'name'}
%ENDPERL%
Inside-out object:
package Rectangle; {
my %name;
sub new {
my $class = shift;
my $self = bless \(my $dummy), $class;
$name{refaddr} = 'pung96';
return $self;
}
sub get_name {
my $self = shift;
return $name{refaddr $self};
}
sub set_name {
my $self = shift;
$name{$self} = shift;
}
바뀐것들
최신 인사이드-아웃 오브젝트들에 몇몇 변화들이 있다.
- 오브젝트에 대해 다양한 레퍼런스를 사용한다.(예를들어 블레스된 스칼라, 배열, 해쉬, 정규표현, glob 등)
- 키를 블레스된 레퍼런스 안에 저장한다.
- 해쉬 대신에 배열에 오브젝트 데이터를 저장한다. (그리고 오브젝트안에 인덱스를 저장한다.)
장점
오브젝트와 데이터를 분리하는 것은 몇가지 목적이 있다.
컴파일 시간의 오타 검사
인사이드-아웃 오브젝트는 코드에서 멤버변수의 이름을 식별자로 만들기 때문에 컴파일러는 멤버변수 이름에 대한 오타를 찾아낼 수 있다. 기존의 오브젝트에서는 멤버변수 이름이 해쉬의 키였기 때문에 컴파일 시간에 오타를 찾을 수 없었다
use strict 'vars';
# ordinary object
$self->{naem} = "Larry"; # runtime bug
# inside-out object
$naem{ refaddr $self } = "Larry"; # compile-time error
캡슐화
인사이드-아웃 오브젝트는 강력한 캡슐화가 가능하다. 이것은 서로 독립적인 코드들을 만들 수 있다는 의미이다. 예를 들어
Option to enforce privacy
기존의 블레스된 해쉬 레퍼런스는 누구나 오브젝트의 창자를 부여잡고(-_-;;;) 캡슐화를 위반할 수 있었다. 인사이드-아웃 오브젝트는 오브젝트 데이터를 클래스 스코프의 코드들만이 접근할 수 있도록 저장하여 순수한 펄만을 사용하여 데이터 캡슐화를 지킬 수 있다.
Note however that this is just one aspect of encapsulation. You can use package variables to opt out of enforced privacy and still receive
all other benefits of inside-out objects, including all other facets of encapsulation.
올바른 멤버변수의 네임스페이스 Proper namespacing for member variables
서브클래스가 상위 클래스의 멤버변수와 동일한 이름의 멤버 변수를 사용한다고 생각해보자. 기존의 해쉬 기반의 오브젝트에서는 부모클래스와 자식클래스가 같은 해쉬의 같은 키를 사용하기 때문에 충돌이 생기고 버그를 만든다
인사이드-아웃 오브젝트에서는 멤버 변수의 이름이 렉시컬 스코프 안에 같혀있기 때문에 심지어 클래스와 그 상위 클래스들이 같은 이름의 멤버변수를 사용해도 아무 문제가 생기지 않는다.
Isolation from implementation details
기존의 오브젝트에서 다른 사람이 만든 코드의 서브클래스를 만들때, 그 코드의 새버젼에서 블레스된 레퍼런스의 데이터 구조가 바뀔 수도 있다. 예를 들어 해쉬가 배열이나 glob로 바뀌때 서브클래스의 코드가 데이터 구조와 연관되어 있다면 서브클래스는 작동하지 않는다.
인사이드-아웃 오브젝트는 이런 데이터 구조를 전혀 사용하지 않기때문에 더 독립적이라고 말할 수 있다. -> 이건 약간 억지다.
같은 이유로 인사이드-아웃 형태는 어떤 종류의 오브젝트와 데이터든 연결하여 사용할 수 있다. (심지어 서브루틴이나 정규표현식의 레퍼런스도)
특히 파일핸들이나 IO::File 오브젝트에서 핸들 레퍼런스를 다른 종류의 데이터 구조로 감싸거나 (코드를 지저분하게 만드는)glob의 해쉬슬롯을 사용하지 않고 않고 오브젝트로 직접 사용고자 할때 유용하다.
이렇게 만든 오브젝트는 파일핸들처럼 사용할 수 있다.
이 장점은 오브젝트의 키로 메모리 어드레스를 사용하는 경우에만 적용된다. 오브젝트의 키를 오브젝트안에 저장하는 방법(보통은 블레스된 스칼라 레퍼런스)은 융통성에 제약이 있다.
--+++ Reduced memory footprint
아주 많은 오브젝트 인스턴스를 만들때, 인사이드-아웃 오브젝트는 메모리 오버헤드를 조금 줄여준다. 인스턴스마다 하나의 해쉬를 만드는 대신에 멤버변수마다 하나의 해쉬를 만들기 때문에 해쉬의 개수가 인스턴스의 개수와 상관없이 일정하고 보통은 개수가 적다. (하지만 단점에서 설명하는 것처럼 쓰레드-안전을 지키려면 오브젝트를 추적하는데 필요한 추가해쉬가 필요하기 때문에 이러한 장점이 상쇄된다.
단점
인사이드-아웃 오브젝트의 단점은 오브젝트의 밖에 데이터를 저장하기 때문에 복잡성이 증가한다는 것이다. 결국 인사이드-아웃 오브젝트를 안정적으로 쓰기 위해서는 추가적인 코드가 필요하다.
`DESTORY or 메모리 누수`
오브젝트 데이터를 오브젝트의 밖(클래스)에 저장하기 때문에 오브젝트가 소멸할때 데이터가 자동으로 청소되지 않는다. 따라서 메모리를 청소하기 위한 DESTORY 서브루틴이 필요하다.
sub DESTROY {
my $self = shift;
delete $name{ refaddr $self };
}
Serialization needs hooks
인사이드-아웃 오브젝트에서는 표준 serialization 기술이 자동으로 작동하지 않는다. "Storable"<http://search.cpan.org/dist/Storable/Storable.pm> 같은 serializers을 인사이드-아웃 오브젝트에 적용하기 위해선 훅킹 코드를 만들어야 한다.
`CLONE` for thread safety
오브젝트의 메모리 어드레스를 키로 사용하는 해쉬 기반의 인사이드-아웃 오브젝트는 보통 쓰레드에 안전하지 않다. 새 쓰레드에서 오브젝트의 메모리 주소는 달라질 것이고 오브젝트와 관련된 데이터들을 읽어버릴 것이다.
(메모리 누수도 생긴다.)
펄5.8에서, 쓰레드를 복제하는 동안 데이터 구조를 청소하기 위한 CLONE 메소드를 추가할 수 있다.
An inside-out class can use a registry hash of objects along with `CLONE` to move data from the old memory address to the new one.
모듈
다음 모듈들은 인사이드-아웃 클래스를 안정적으로 구현하였다. 첨부터 인사이드-아웃을 구현하려고 하지 말고 그냥 이 모듈들을 쓰는게 좋다.
- "Class::InsideOut"<http://search.cpan.org/dist/Class-InsideOut/lib/Class/InsideOut.pod>
- "Object::InsideOut"<http://search.cpan.org/dist/Object-InsideOut/lib/Object/InsideOut.pod>
펄 5.10에는
- "Hash::Util::FieldHash"<http://search.cpan.org/perldoc?Hash::Util::FieldHash>
몇가지 다른 모듈들도 있는데 특히 serialization 이나 쓰레드와 관련하여 예상하지 않은 결과가 나오는등 여러가지로 문제가 많다.
- "Class::BuildMethods"<http://search.cpan.org/dist/Class-BuildMethods/lib/Class/BuildMethods.pm>
- "Class::MakeMethods::Template::InsideOut"<http://search.cpan.org/dist/Class-MakeMethods/MakeMethods/Template/InsideOut.pm>
- "Class::Std"<http://search.cpan.org/dist/Class-Std/lib/Class/Std.pm> (though serialization is available from "Class::Std::Storable"<http://search.cpan.org/dist/Class-Std-Storable/lib/Class/Std/Storable.pm>)
- "Lexical::Attributes"<http://search.cpan.org/dist/Lexical-Attributes/lib/Lexical/Attributes.pm>
참고
- "Re: Where/When is OO useful?"<http://perlmonks.org/index.pl?node_id=178518> ([Abigail], Perl Monks, July 2002)
- "Yet Another Perl Object Model (Inside Out Objects)"<http://perlmonks.org/index.pl?node_id=219924> ([demerphq], Perl Monks, Dec 2002)
- "Threads and fork and CLONE, oh my!"<http://perlmonks.org/index.pl?node_id=483162> ([David Golden], Perl Monks, Aug 2005)
- "Anti-inside-out-object-ism"<http://perlmonks.org/index.pl?node_id=515650> ([Jerry Hedden], Perl Monks, Dec 2005)
- "Introduction to Inside-Out Objects"<http://dagolden.com/files/Eversion_101.pdf> ([David Golden], YAPC::NA 2006)
- "Inside-out objects(perltraining, Mar 2006)"<http://perltraining.com.au/tips/2006-03-31.html>
- "Class::Insideout, Object InsideOut? 한국어 소개 (장범수, 펄마니아, Mar 2008)"<http://www.perlmania.or.kr:9000/trac/wiki/pung96/InsideOut>
--
BeomsuChang - 25 May 2008