Perl入門ゼミ

テキスト処理、Linuxサーバー管理、Web開発ならPerl
  1. Perl
  2. モジュール
  3. Object::Simple

Object::Simple - 簡単すぎるクラスビルダー、Mojo::Baseの移植、速くて省メモリ【日本語訳】

  • 簡単すぎるクラスビルダー。 覚えることは「has」関数だけ。
  • Mojo::Baseの移植。 Mojoliciousが好きですか。もしそうなら、これは良い選択だ!
  • 速くって、省メモリ。速い「new」メソッドとアクセッサメソッド。省メモリの実装。

使い方

  package SomeClass;
  use Object::Simple -base;
  
  # アクセッサの作成
  has 'foo';
  
  # デフォルト値を持つアクセッサを作成
  has foo => 1;
  has foo => sub { [] };
  has foo => sub { {} };
  has foo => sub { OtherClass->new };
  
  # 一度に複数のアクセッサを作成
  has ['foo', 'bar', 'baz'];
  has ['foo', 'bar', 'baz'] => 0;

オブジェクトを作成しましょう。

  # 新しいオブジェクトの作成
  my $obj = SomeClass->new;
  my $obj = SomeClass->new(foo => 1, bar => 2);
  my $obj = SomeClass->new({foo => 1, bar => 2});
  
  # 値の設定と取得
  my $foo = $obj->foo;
  $obj->foo(1);
  
  # 値の設定のためのアクセッサは、チェーンできる
  $obj->foo(1)->bar(2);

継承の書き方。

  package Foo;
  use Object::Simple -base;
  
  # BarはFooを継承する
  package Bar;
  use Object::Simple 'Foo';
  
  # 継承の他の書き方(これはObject::Simpleだけの書き方)
  package Bar;
  use Object::Simple -base => 'Foo';

  # 継承の他の書き方(これはObject::Simpleだけの書き方)
  use Foo -base;

説明

Object::Simpleは、簡単すぎるクラスビルダーです。覚えることは「has」関数だけ。すべての機能をたった一時間で覚えることができます。難しいものは何もありません。

Mojoliciousが好きですか。実際に、Object::SimpleはMojo::Baseの移植です。Mojo::Baseは、Mojoliciousの基本的なクラスビルダーです。もしMojoliciousが好きなら、これは良い選択です。すでにMojo::Baseを知っているなら、覚えることは何もありません。

「new」メソッドとアクセッサは速いです。実装は、ピュアなPerlで、単純なハッシュベースのオブジェクトです。メモリは省メモリ。余分なオブジェクトは何も作られません。とても軽いオブジェクト指向モジュールです。

Class::Accessor::Fastとの比較

Class::Accessor::Fastは簡単です。けれどもいくつかの機能が欠けています。「new」メソッドは、ハッシュの引数を受け取ることができません。デフォルト値の設定ができません。複数の値が、アクセッサに与えられたとき、警告なしに、配列のリファレンスへと変換されてしまいます。

Mooseとの比較

Mooseはとても複雑な構文を持ち、とてもたくさんのモジュールに依存しています。あなたは、オブジェクト指向プログラミングをするために、とても多くのことを学ばなければなりません。ソースコードを理解することは、難しいです。コンパイル時間はとても遅く、メモリ使用率も高いです。実行速度も速くありません。単純なオブジェクト指向をするには、大きすぎます。Mooは、この点で、改善がされています。

チュートリアル

クラスとアクセッサの作成

最初に、クラスを作ってみましょう。

  package SomeClass;
  use Object::Simple -base;

「-base」オプションを使うと、「SomeClass」はObject::Simpleを継承し、「has」メソッドをインポートします。

Object::Simpleは「new」メソッドを持ちます。「new」メソッドはコンストラクタです。Cメソッドは、ハッシュとハッシュリファレンスを受け取ることができます。

  my $obj = SomeClass->new;
  my $obj = SomeClass->new(foo => 1, bar => 2);
  my $obj = SomeClass->new({foo => 1, bar => 2});

「has」関数を使って、アクセッサを作成することができます。

  has 'foo';

アクセッサを作成すれば、値を設定、また取得することができます。

  # 値の設定
  $obj->foo(1);
  
  # 値の取得
  my $foo = $obj->foo;

値を設定するアクセッサは、チェーンすることができます。

  $obj->foo(1)->bar(2);

デフォルト値を設定することもできます。

  has foo => 1;

もし「C」の値が存在しなければ、デフォルト値が利用されます。

  my $foo_default = $obj->foo;

もし、リファレンスやオブジェクトをデフォルト値として使いたい場合は、コードリファレンスによって、デフォルト値を囲む必要があります。戻り値が、デフォルト値になります。

  has foo => sub { [] };
  has foo => sub { {} };
  has foo => sub { SomeClass->new };

複数のアクセッサを一度に生成することもできます。

  has ['foo', 'bar', 'baz'];
  has ['foo', 'bar', 'baz'] => 0;

メソッドのオーバーライド

メソッドは、オーバーライドすることができます。

サンプル

オブジェクトの初期化。

  sub new {
    my $self = shift->SUPER::new(@_);
    
    # 初期化
    
    return $self;
  }

サンプル

「new」の引数の変更。

  sub new {
    my $self = shift;
    
    $self->SUPER::new(x => $_[0], y => $_[1]);
    
    return $self;
  }

上記のようにすれば、配列も渡せます。

  my $point = Point->new(4, 5);

クラス、アクセッサ、継承、メソッドオーバーライドのサンプル

Object::Simpleのサンプルを紹介しよう。

ポイントクラス: ふたつのアクセッサ「x」と「y」を持つ。そして、「x」と「y」値を0に設定する「clear」メソッドを持つ。

  package Point;
  use Object::Simple -base;

  has x => 0;
  has y => 0;
  
  sub clear {
    my $self = shift;
    
    $self->x(0);
    $self->y(0);
  }

ポイントクラスを使ってみましょう。

  use Point;
  my $point = Point->new(x => 3, y => 5);
  print $point->x;
  $point->y(9);
  $point->clear;

Point3Dクラス: Point3Dはポイントクラスを継承します。

Point3Dクラスは、「x」「y」に加えて、「z」アクセッサを持ちます。

「clear」メソッドはオーバーライドされ、「x」「y」「z」の値をクリアします。

  package Point3D;
  use Object::Simple 'Point';
  
  has z => 0;
  
  sub clear {
    my $self = shift;
    
    $self->SUPER::clear;
    
    $self->z(0);
  }

Point3Dクラスの使用。

  use Point3D;
  my $point = Point->new(x => 3, y => 5, z => 8);
  print $point->z;
  $point->z(9);
  $point->clear;

オブジェクト指向プログラミングとは何ですか

オブジェクト指向プログラミングの概念について紹介します。

継承

最初の概念は「継承」です。継承は「もしクラスQがクラスPを継承していたら、クラスQはクラスPのすべてのメソッド利用できる」ということを意味します。

  +---+
  | P | ベースクラス
  +---+   「method1」と「method2」を持つ
    |
  +---+
  | Q | サブクラス
  +---+   「method3」を持つ。

「クラスQ」は「クラスP」を継承しています。「Q」は「Q」のメソッドに加え「P」のすべてのメソッドを呼び出すことができます。

言い換えれば「Q」は、「method1」「method2」「method3」を呼び出すことができます。次のように記述すると「Q」は「P」を継承します。

  # P.pm
  package P;
  use Object::Simple -base;
  
  sub method1 { ... }
  sub method2 { ... }
  
  # Q.pm
  package Q;
  use Object::Simple 'P';
  
  sub method3 { ... }

Perlはオブジェクト指向プログラミングのための便利な関数とメソッドを持ちます。

もし、オブジェクトがどのクラスに属しているのかを知りたいのであれば、「ref」関数を使用できます。

  my $class = ref $obj;

どのクラスを継承しているのかを知りたければ、「isa」メソッドを使用することができます。

  $obj->isa('SomeClass');

オブジェクト、あるいはクラスが、特定のメソッドを持っているかを調べるには、「can」メソッドを使用できます。

  SomeClass->can('method1');
  $obj->can('method1');

カプセル化

二番目の概念は、カプセル化です。「カプセル化」は「内部的なデータ構造に直接アクセスしないで! 内部のデータにアクセスするときは、公開メソッドを使うべきだ」ということを意味しています。

このルールを守るには、アクセッサを作成して、常にそれを使ってください。

  my $value = $obj->foo;
  $obj->foo(1);

ポリモーフィズム

三つ目の概念は、「ポリモーフィズム」です。「ポリモーフィズム」は、さらに二つの概念「オーバーロード」と「オーバーライド」に分割することができます。

Perlプログラマーは、オーバーロードについては意識する必要がありません。Perlは動的型を持つ言語であり、サブルーチンは、どんな値もとることができるからです。

「オーバーライド」は「サブクラスで、メソッドの振る舞いを変更することができる」ということを意味しています。

  # P.pm
  package P;
  use Object::Simple -base;
  
  sub method1 { return 1 }
  
  # Q.pm
  package Q;
  use Object::Simple 'P';
  
  sub method1 { return 2 }

Pの「method1」は「1」を返します。Qの「method1」は「2」を返します。Qの「method1」は、Pの「method1」をオーバライドしています。

  # Pの「method1」は「1」を返す
  my $obj_a = P->new;
  $obj_p->method1; 
  
  Qの「method1」は「2」を返す
  my $obj_b = Q->new;
  $obj_q->method1;

もし、サブクラスで、スーパークラスのメソッドを使いたい場合は、「SUPER」疑似クラスを使うことができます。

  package Q;
  use Object::Simple 'P';
  
  sub method1 {
    my $self = shift;
    
    # スーパークラスPの「method1」を呼び出す
    my $value = $self->SUPER::method1;
    
    return 2 + $value;
  }

この三つの概念を理解すれば、オブジェクト指向プログラミングの主要な部分をマスターしたことになります。

関数

has

アクセッサを作成します。

  has 'foo';
  has ['foo', 'bar', 'baz'];
  has foo => 1;
  has foo => sub { {} };

  has ['foo', 'bar', 'baz'];
  has ['foo', 'bar', 'baz'] => 0;

「has」関数は、アクセッサ名とデフォルト値を受け取ります。デフォルト値は、オプショナルです。複数のアクセッサを一度に作成したい場合は、第一引数に、アクセッサの名前を格納した、配列のリファレンスを指定します。

もし、デフォルト値にリファレンスかオブジェクトを指定したい場合は、他のオブジェクトと共有しないために、コードリファレンスとして、渡す必要があります。

値の取得と設定。

  my $foo = $obj->foo;
  $obj->foo(1);

もし、デフォルト値が指定されており、値が存在しなければ、デフォルト値を取得することができます。

設定のためのアクセッサは、戻り値として自分自身を返します。それで、メソッドをチェーンすることができます。

  $obj->foo(1)->bar(2);

メソッド

new

  my $obj = Object::Simple->new;
  my $obj = Object::Simple->new(foo => 1, bar => 2);
  my $obj = Object::Simple->new({foo => 1, bar => 2});

新しいオブジェクトを生成します。「new」はハッシュとハッシュリファレンスを引数として受け取ることができます。

attr

  __PACKAGE__->attr('foo');
  __PACKAGE__->attr(['foo', 'bar', 'baz']);
  __PACKAGE__->attr(foo => 1);
  __PACKAGE__->attr(foo => sub { {} });

  __PACKAGE__->attr(['foo', 'bar', 'baz']);
  __PACKAGE__->attr(['foo', 'bar', 'baz'] => 0);

アクセッサを作成します。「attr」メソッドは「has」関数と使用方法がが全く同じです。

オプション

-base

「-base」オプションを使うと、Object::Simpleを継承し、「has」関数をインポートします。

  package Foo;
  use Object::Simple -base;
  
  has x => 1;
  has y => 2;

「strict」と「warnings」が自動的に有効になります。

もし特定のクラスを継承したい場合は、次のように書きます。

  # BarはFooを継承する
  package Bar;
  use Object::Simple 'Foo';

次の構文も使うことができます。これはObject::Simpleだけの機能です。

  # 上と同じ
  package Bar;
  use Object::Simple -base => 'Foo';

「-base」オプションは、サブクラスでも、他のクラスを継承するために、使うことができます。これは、Object::Simpleだけの機能です。

  # 上と同じ
  package Bar;
  use Foo -base;

FAQ

本当にこんなに少ない機能で十分なオブジェクト指向プラグラミングを行うことができるんですか。

はい、できます。たとえば、Mojoliciousはとても大きなプロジェクトです。実際に、ソースコードはクリーンで、単一継承だけを使っています。一般的にいって、読みやすいソースコードは、複雑な機能ではなく、単純な概念の上に成り立っています。

Mooの「BUILD」「BUILDARGS」「DEMOLISH」メソッドのようなものは、よいオブジェクト指向で必須ですか?もし、多重継承やロールを使いたいのであれば、これらのメソッドは必須です。

でも、わたしは、オブジェクト指向プログラミングにおいて、単一継承だけを使うことを強く勧めます。単一継承は、クリーンで、読みやすいからです。

もし単一継承だを使うのであれば、カスタムコンストラクタを作って、正しい順序で、コンストラクタを呼ぶことができます。また、カスタムデストラクタを作って、正しい順序で、デストラクタを呼ぶことができます。

カスタムコンストラクタの作成は、とてもとても簡単で、難しいところは、ぜんぜんありません。

  # カスタムコンストラクタ
  sub new {
    # 最初にスーパークラスのコンストラクタを読んで、次にしたいことをする
    my $self = shift->SUPER::new(@_);
    
    # したいこと
    
    return $self;
  }
  
  # カスタムデストラクタ
  sub DESTROY {
    my $self = shift;
    
    # したいこと

    # 最初に、したいことを行って、次に、スーパークラスのデストラクタを呼ぶ
    $selft->SUPER::DESTROY;
    
    return $self;
  }

Object::Simpleは、最も速いオブジェクト指向モジュールですか?

いいえ、Object::Simpleは最も速いモジュールではありませんが、十分に速いです。 もし本当にパフォーマンスが必要になったら、ハッシュの値に直接アクセスすることができます。

  # ある部分で、パフォーマンスが必要だ。さぁ、直接、ハッシュの値にアクセスしよう
  # Object::Simpleは、単純なハッシュベースのオブジェクトです。
  $self->{x};

Mojo::Baseと比較した場合の利点は何ですか。

  • Perl 5.8をサポートしています。
  • ほんの少しのファイルしかないので、インストールはとても高速です。
  • いくらかの人々は、Mojo::Baseを使うためだけに、Mojoliciousすべてに依存したくはないと考えています。Object::Simpleはその需要を満たします。

なぜObject::Simpleはいくつかの点でMojo::Baseと異なるのですか。

古い時代、Object::SimpleはMojo::Baseの移植ではありませんでした。異なることに挑戦していました。

今は、Object::SimpleをPerl5.8をサポートする点を除いて、Mojo::Baseと完全に同じにしたいと思っています。

後方互換性のポリシー

もし機能が、廃止予定になった場合に、あなたは、DEPRECATED警告によって、それを知ることができます。すべての廃止予定の機能は、ドキュメントに記載されます。廃止予定の機能は、5年後に取り除かれます。けれども、少なくとも一人の人が、その機能を使っていて、そのことを私に伝えてくれれば、そのたびに、1年間、延長します。

実験的な(EXPERIMENTAL)機能は、警告なしに変更されます。

(このポリシーは2011/10/22に現在のものに変更されました。)

廃止予定

  「new」と「attr」をエクスポートする機能 # 2021/6/1に取り除かれます。
  
  # 複数のキーと値を受け取る文法
    has x => 1, y => 2;      
    __PACAKGE__->attr(x => 1, y => 2);
  # 2021/6/1に取り除かれます。
  
  class_attrメソッド # 2017/1/1に取り除かれます。
  dual_attrメソッド # 2017/1/1に取り除かれます

バグ

バグを発見したら教えてください。メール(kimoto.yuki@gmail.com)かGitHub([http://github.com/yuki-kimoto/Object-Simple])で。

著者

木本裕紀(kimoto.yuki@gmail.com)

応援メッセージを送ってくれると喜びます。あなたのメッセージだけで、力がみなぎります。

ユーザー

Object::Simpleを使っているプロジェクト。

  • GitPrep - ポータブルなGitHubシステムをあなたのサーバーへ。 [https://github.com/yuki-kimoto/gitprep]
  • DBIx::Custom - insert, update, delete, selectを簡単に実行するためのDBIの拡張
  • Validator::Custom - HTMLフォームバリデーション。簡単で、とても柔軟。

他のモジュール

CPANはさまざまなクラスビルダーを持っています。ぜひObject::Simpleと比べてみてください。

Mojo::Base, Class::Accessor::Fast, Moose, Moo, Class::Tiny.

COPYRIGHT & LICENSE

Copyright 2008-2016 Yuki Kimoto, all rights reserved.

This program is free software; you can redistribute it and/or modify it

under the same terms as Artistic v2.

これはMojoliciousのライセンスと同じです。

([http://search.cpan.org/dist/Object-Simple/lib/Object/Simple.pm] 3.18を翻訳)

  • Perlとはテキスト処理の記述性とパフォーマンスに優れ、正規表現が言語に組み込まれているプログラミング言語です。
  • Linuxサーバーでのフィルタリングプログラム、複数行の文字列を処理、ファイル内容の検索・置換などが得意
  • Perlはgitopensslなど広く普及したUnix/Linuxミドルウェアの補助ツールとして採用実績あり。後方互換性とポータビリティの高さがひとつの理由と推測。
  • 大量のテキストを扱うWeb開発も得意。ロングテールSEOを意識したWebサイト、アドテクやソーシャルゲームでの50ms以内のJSONの生成など。