Perl - Вызов конструктора подкласса от суперкласса (OO)

Это может оказаться смущающе глупым вопросом, но лучше, чем потенциальное создание смущающе глупого кода.:-) Это - вопрос о дизайне OO, действительно.

Скажем, у меня есть класс объекта 'Foos', который представляет ряд динамических элементов конфигурации, которые получены путем запросов команды на диске, 'mycrazyfoos-getconfig'. Скажем, то, что существует две категории поведения, которое я хочу объекты 'Foos' иметь:

  • Существующие: каждый, запроса, которые существуют в выводе команды, который я просто упомянул (/usr/bin/mycrazyfoos-getconfig'. Сделайте модификации к существующим через выход из оболочки команд.

  • Создайте новые, которые не существуют; новый 'crazyfoos', с помощью сложного набора /usr/bin/mycrazyfoos команды и параметры. Здесь я действительно просто не запрашиваю, но на самом деле выполняю набор системы () команды. Влияние на изменения.

Вот моя структура класса:

Foos.pm

Foos пакета, который имеет новое ($hashref-> {имя => 'myfooname') конструктор, который берет 'crazyfoo ИМЯ' и затем запрашивает существование того ИМЕНИ, чтобы видеть, существует ли он уже (путем выхода из оболочки и выполнения команды mycrazyfoos выше). Если это, crazyfoo уже существует, возвращают Foos:: Существующий объект. Любые изменения в этом объекте требуют выхода из оболочки, выполнения команд и получения подтверждения, что все работало хорошо.

Если это - способ пойти, то новое () у конструктора должен быть тест для наблюдения, какого конструктора подкласса использовать (если это даже имеет смысл в этом контексте). Вот подклассы:

Foos/Existing.pm

Как упомянуто выше, это для того, когда объект Foos уже существует.

Foos/Pending.pm

Это - объект, который будет создан, если, в вышеупомянутом, 'crazyfoo ИМЯ' не будет на самом деле существовать. В этом случае новое () конструктор выше будет проверен на дополнительные параметры, и это будет идти вперед и, на названо использование-> создает (), выходят из оболочки с помощью системы () и создают новый объект... возможно возврат 'Существующего'...

ИЛИ

Поскольку я вывожу это, я понимаю, что это, возможно, лучше иметь сингл:

(альтернативное расположение)

Класс Foos, который имеет a

-> новый (), который берет просто имя

-> создают (), который берет дополнительные параметры создания

-> удаляют (),-> изменение () и другие параметрические усилители, которые влияют на, которые существуют; это должно будет быть просто проверено динамично.

Таким образом, здесь мы, два основных направления для движения с этим. Мне любопытно, который был бы более интеллектуальным способом пойти.

8
задан Emmel 5 May 2010 в 19:39
поделиться

4 ответа

Вообще, это ошибка (с точки зрения дизайна, а не синтаксиса), что метод new не возвращает ничего, кроме нового объекта. Если вы хотите иногда возвращать существующий объект, назовите этот метод как-то иначе, например, new_from_cache().

Мне также кажется странным, что вы разделяете эту функциональность (создание нового объекта и возврат существующего) не только на отдельные пространства имен, но и на разные объекты. В общем, вам ближе второй подход, но вы все равно можете сделать так, чтобы главный конструктор (new) обрабатывал различные аргументы:

package Foos;
use strict;
use warnings;

sub new
{
    my ($class, %args) = @_;

    if ($args{name})
    {
        # handle the name => value option
    }

    if ($args{some_other_option})
    {
        # ...
    }

    my $this = {
        # fill in any fields you need...
    };

    return bless $this, $class;
}

sub new_from_cache
{
    my ($class, %args) = @_;

    # check if the object already exists...

    # if not, create a new object
    return $class->new(%args);
}

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

4
ответ дан 5 December 2019 в 15:21
поделиться

Это также шаблон фабрики (плохо в Perl), если конструктор объекта вернет экземпляр, благословленный более чем одним пакетом.

Я бы создал что-то вроде этого. Если существует names , чем is_created установлено значение 1, в противном случае установлено значение 0 .. Я бы объединил :: Pending и : : Существующие вместе, и если объект не создан, просто поместите его в значение по умолчанию для _object , проверка выполняется лениво. Кроме того, Foo-> delete () и Foo-> change () будут относиться к экземпляру в _object .

package Foo;
use Moose;

has 'name' => ( is => 'ro', isa => 'Str', required => 1 );
has 'is_created' => (
    is => 'ro'
    , isa => 'Bool'
    , init_arg => undef
    , default => sub {
        stuff_if_exists ? 1 : 0
    }
);

has '_object' => (
    isa => 'Object'
    , is => 'ro'
    , lazy => 1
    , init_arg => undef
    , default => sub {
        my $self = shift;
        $self->is_created
            ? Foo->new
            : Bar->new
    }
    , handles => [qw/delete change/]
);
2
ответ дан 5 December 2019 в 15:21
поделиться

Вообще говоря, для суперкласса плохо знать о своих подклассах, принцип, который распространяется на конструкцию. [1] Если во время выполнения вам нужно решить, какой объект создать (и вы это сделаете), создайте четвертый класс, чтобы иметь именно это задание. Это один из видов «фабрики».

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

Итак, как это предложение[3]: Сделайте Foos::Exists и Foos::P ending два отдельных и несвязанных класса и предоставьте (в Foos) метод, который возвращает соответствующий. Не называйте его new; Вы не делаете новый Foos.

Если вы хотите унифицировать интерфейсы, чтобы клиентам не нужно было знать, о каком виде они говорят, то мы можем поговорить о подклассах (или, еще лучше, делегировании лениво созданной и обновленной Foos::Handle).

[1]: Объяснение того, почему это так, является достаточно большой темой для книги[2], но короткий ответ заключается в том, что она создает цикл зависимостей между подклассом (который зависит от его суперкласса по определению) и суперклассом (который становится зависимым от своего подкласса плохим проектным решением).
[2]: Лакос, Джон. (1996). Крупномасштабный дизайн программного обеспечения C++. Эддисон-Уэсли.
[3]: Не рекомендация, так как я не могу достаточно хорошо разобраться в ваших требованиях, чтобы убедиться, что я не стреляю в рыбу в темном океане.

4
ответ дан 5 December 2019 в 15:21
поделиться

Интересные ответы! Я перевариваю это, пробуя разные вещи в коде.

У меня есть еще один вариант того же вопроса - тот же вопрос, заметьте, просто другая проблема для того же класса: проблема создания подкласса!

На этот раз:

Этот код представляет собой интерфейс к командной строке, который имеет ряд различных сложных параметров. Я уже говорил вам о / usr / bin / mycrazyfoos раньше, верно? Что ж, что, если бы я сказал вам, что этот двоичный файл изменяется в зависимости от версии, а иногда полностью меняет его базовые параметры. И этот класс, который мы пишем, должен уметь учитывать все эти вещи. Цель (или, возможно, идея) состоит в следующем: (возможно, это называется FROM класса Foos, который мы обсуждали выше):

Foos :: Commandline, который имеет в качестве подклассов различные версии базовой команды '/ usr / bin / mycrazyfoos' .

Пример:

 my $fcommandobj = new Foos::Commandline;
 my @raw_output_list = $fcommandobj->getlist();
 my $result_dance    = $fcommandobj->dance();

где «getlist» и «dance» зависят от версии. Я думал об этом:

 package Foos::Commandline;

 new (
    #Figure out some clever way to decide what version user has
    # (automagically)

    # And call appropriate subclass? Wait, you all are telling me this is bad OO:

    # if v1.0.1 (new Foos::Commandline::v1.0.1.....
    # else if v1.2 (new Foos::Commandline::v1.2....
    #etc

 }

, затем

 package Foos::Commandline::v1.0.1;

 sub getlist ( eval... system ("/usr/bin/mycrazyfoos", "-getlistbaby"
  # etc etc

и (разные файлы .pm, в подкаталоге Foos / Commandline)

 package Foos::Commandline::v1.2;

 sub getlist ( eval...  system ("/usr/bin/mycrazyfoos", "-getlistohyeahrightheh"
  #etc

Имеет смысл? Я выразил в коде то, что хотел бы сделать, но это кажется неправильным, особенно в свете того, что обсуждалось в ответах выше. Что ДЕЙСТВИТЕЛЬНО кажется правильным, так это то, что для командной строки должен быть общий интерфейс / суперкласс ... и что разные версии должны иметь возможность переопределять его. Верно? Был бы признателен за пару предложений по этому поводу. Грасиас.

1
ответ дан 5 December 2019 в 15:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: