LINUX.ORG.RU

Скелетон ОО-класса, прячущего данные и автоподгружающего методы

 ,


0

1

Раньше писал с использованием Moose, а давеча решил попробовать написать скелет работоспособного класса на чистом Perl.

Получилось так:

use v5.14.1;
use utf8;
use strict;
package FRC::skel;
use Carp qw(croak);
use Data::Dumper;
our $AUTOLOAD;

sub new {
  my ($slfClass, %settings)=@_;
  my %privArea;
  
  my %attrs=(
    'name'=>undef,
  );
  
  sub attrUniAccessor {
    my ($attrName,$attrValue)=@_;
    say scalar($attrValue?'Set':'Get')." $attrName ".scalar($attrValue?'to value: '.$attrValue:'value');
    return undef unless exists($attrs{$attrName});
    my $hndlAttr=$attrs{$attrName};
    unless (ref($hndlAttr) eq 'HASH') {
      if ($attrValue) {
        return $privArea{$attrName}=$attrValue
      } else {
        return $privArea{$attrName}
      }
    } else {
      if ($attrValue) {
        return exists($hndlAttr->{'set'})?$hndlAttr->{'set'}->($attrValue):$privArea{$attrName}=$attrValue
      } else {
        return exists($hndlAttr->{'get'})?$hndlAttr->{'get'}->():$privArea{$attrName}
      }
    }
  }
  
  my (%methods, %cacheMethods);
  %methods=(
    'checkMethodExists'=>sub {
      my $method=shift;
      return [0, $methods{$method}] if exists($methods{$method});
      return [1, \&attrUniAccessor] if exists($attrs{$method});
      undef
    },
    'getMeOut'=>sub {
      say 'Get me out called!';
    },
    'DESTROY'=> sub {
      %methods=();
      %attrs=();
      say 'Ohh... You killed me :(';      
    }
  );
  
  my $obj=bless sub {
    my ($method,@pars)=@_;
    $method=~s%^.*::(.+?)$%$1%;
    printf "You called method %s with [%s]\n", $method, join(','=>@pars);
    return $methods{'checkMethodExists'}->(($pars[0]=~m/(?:^.+::)?(.+)$/)[0]) if $method eq 'checkMethodExists';
    croak "No such method: $method" unless my $realProc=$cacheMethods{$method} || do { $cacheMethods{$method}=$methods{'checkMethodExists'}->($method) };
    $realProc->[1]->(($realProc->[0]?$method:()),@pars);
  };
  while (my ($a,$v)=each %settings) {
    $obj->($a,$v);
  }
  return $obj;
}

sub AUTOLOAD {
  my ($slf)=@_;
  say "You attempted to do: $AUTOLOAD";
  no strict 'refs';
  my $method=$AUTOLOAD;  
  croak sprintf("No such method %s in package %s\n", $method, ref($slf))
   unless $method=~m/::(?:checkMethodExists|DESTROY)$/ or $slf->('checkMethodExists',$method);
  *{$method}=sub { my $slf=shift; $slf->($method,@_) };
  $method->(@_);
}

1;

Вывод «приложения», использующего этот класс, выглядит вот так (для примера)

You called method name with [Eihta Hamminess]
Set name to value: Eihta Hamminess
You attempted to do: FRC::skel::getMeOut
You called method checkMethodExists with [FRC::skel::getMeOut]
You called method getMeOut with []
Get me out called!
You attempted to do: FRC::skel::name
You called method checkMethodExists with [FRC::skel::name]
You called method name with []
Get name value
Eihta Hamminess
You called method getMeOut with []
Get me out called!
You called method name with [Kulvargia Zidatlis]
Set name to value: Kulvargia Zidatlis
You called method name with []
Get name value
My new name is: Kulvargia Zidatlis
You attempted to do: FRC::skel::DESTROY
You called method DESTROY with []
Ohh... You killed me :(

Собственно, от использования замыкания в качестве «сырья» для производства объекта отказываться точно не буду, от полной изоляции всех данных объекта, недоступности их для произвольной модификации извне - тоже.

Хотелось бы замечаний от тех, кто уже реализовывал что-то отдалённо похожее, но сделал это лучше (я всё ещё весьма далёк от понимания ООП,как удобной методологии разработки, и пока написал в ООП-стиле весьма немного).

P.S. pastebin.com залочен на работе, так что прошу прощения за эту адскую скатерть прямо в посте.

★★★★★

Последнее исправление: DRVTiny (всего исправлений: 3)

Обязательно показывай этот код потенциальным работодателям. Он прекрасно тебя характеризует. Не прогадаешь.

pastebin.com залочен на работе

Что кровавый режим с людьми делает.

outtaspace ★★★
()
Последнее исправление: outtaspace (всего исправлений: 1)

Ты пишешь класс или ООП-фреймворк? Если первое, то это какое-то дикое переусложнение

Кстати, есть замечательный Plack::Util::Accessor, ни от чего не зависит и код очень прост

annulen ★★★★★
()
Ответ на: комментарий от annulen

Ты пишешь класс или ООП-фреймворк?

Это скелет для довольно сложного класса (хочу свой модуль Zabipi на ООП перевести, но пока не знаю, как правильно подступиться).

Например, предполагается, что большая часть доступных методов должна формироваться динамически - разбором HTML-страниц документации по API с сайта Zabbix.

Т.е. для меня важно, чтобы пользователь мог легко и свободно сказать:

my @hosts=@{$zbx->host_get(
 'search'=>{'host'=>'*somename*'},
 'searchWildcardsEnabled'=>1
)};
...но при этом не мог бы попросить чего-то вроде $zbx->group_get (поскольку такого метода в API нет).

DRVTiny ★★★★★
() автор топика

Обязательно показывай этот код потенциальным работодателям. Он прекрасно тебя характеризует. Не прогадаешь.

Таки плюсую.

// Пошел капать глаза.

joy4eg ★★★★★
()
Ответ на: комментарий от annulen

Несмотря на кривой синтаксис, у меня лучше, причём уже очень давно. Мало того, мой модуль исправляет/дополняет многие методы API, да ещё добавляет свои собственные.

Проблема именно в том, что модуль - не ООП, и это проблема чисто практическая: невозможно использовать подключение к 2-м фронтендам одновременно.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

Например, предполагается, что большая часть доступных методов должна формироваться динамически - разбором HTML-страниц документации по API с сайта Zabbix.

Что-то мне кажется, тут вводится лишняя сущность...

hobbit ★★★★★
()
Ответ на: комментарий от hobbit

Это просто способ дать пользователю понять, что он делает что-то не то до того, как он отправит запрос на сервер. На стороне сервера API работает ужасающе медленно, поэтому лучше его лишний раз не «беспокоить».

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

А кто такой пользователь в данном контексте?

Что-то я подозреваю, что это не тот, кто жамкает кнопки в приложении/на сайте. Он страшное слово «API» может вообще не знать.

Если это программист, применяющий API в какой-то своей разработке, то для него чтение документации на API обязательно.

hobbit ★★★★★
()
Ответ на: комментарий от hobbit

API ещё и штука динамичная в теории (на практике это такой кусок Г, который в Zabbix не будут дорабатывать, поскольку они-таки знают, что это Г. Но и на стороне сервера доделывать реализацию API тоже не будут).

Так что пользователь моего модуля может и не знать того, что какой-то метод появился или удалился. Неплохо ему сообщить о его неправоте до того, как будет отправлен запрос на потенциально тормозной веб сервер, выполняющий сильно неоптимально написанный php-код.

DRVTiny ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.