У меня есть вопрос дизайна ОО. Я написал (псевдо) -псевдокод ниже, чтобы проиллюстрировать мой вопрос. (Я говорю «псевдопсевдокод», потому что он в основном правильный, только с несколькими частями глупости ...)
Я использую шаблон Фабрики для создания объектов класса, соответствующего атрибутам, которым я передаю метод Factory::new
. Тем не менее, есть некоторые атрибуты, которые я могу получить только после создания объекта, которые я хочу затем использовать для дальнейшего подкласса или «специализации» типа объекта. Я хочу сделать это, чтобы я мог использовать один и тот же интерфейс для всех объектов в main
независимо от класса объекта (я думаю, это polymorphism
).
Первый, Фабричный класс:
use strict;
use warnings;
package Vehicle::Factory;
sub new {
my ( $class, $args ) = @_;
if ( $args->{class} =~ /car/i ) {
return Vehicle::Car->new($args);
} else {
# other possible subclasses based on attributes
}
}
1;
Теперь для связанных классов:
package Vehicle;
sub new {
my ( $class, $args ) = @_;
bless $self, $class;
$self->color( $args->color );
}
sub color {
$_[1] ? $_[0]->{_color} = $_[1] : return $_[0]->{_color};
}
sub wheels {
$_[1] ? $_[0]->{_wheels} = $_[1] : return $_[0]->{_wheels};
}
1;
И подкласс:
package Vehicle::Car;
use base qw( Vehicle );
sub get_fueltype {
my ( $self, $args ) = @_;
$self->fueltype = check_fuel_type;
}
sub fueltype {
$_[1] ? $_[0]->{_fueltype} = $_[1] : return $_[0]->{_fueltype};
}
1;
Теперь для подклассов "Этап 2". Я могу создать их, только когда узнаю больше об объекте, который уже был создан ...
package Vehicle::Car::Gas;
use base qw( Vehicle::Car );
sub fill_her_up {
# Make sure it's Gas.
# ...
}
1;
package Vehicle::Car::Diesel;
use base qw( Vehilce::Car );
sub fill_her_up {
# Make sure it's Diesel.
# ...
}
1;
package Vehicle::Car::Electric;
use base qw( Vehicle::Car );
sub fill_her_up {
# Find a socket.
# ...
}
1;
И основная часть кода:
package main;
my $thing = Vehicle::Factory->new( color => "red", wheels => 4 );
$thing->get_fueltype;
# Somehow convert $thing to be an object of the appropriate subclass based on
# the "fueltype" attribute
$thing->fill_her_up;
(Надеюсь, мой ужасно надуманный пример имеет смысл!)
Теперь я не уверен ... Должен ли я создать новый объект, используя данные экземпляра из $thing
?
Есть ли способ подкласса объекта, не разрушая и не воссоздавая его?
Может быть, я должен использовать следующий подход и повторно использовать завод по производству автомобилей?
package Vehicle::Factory;
sub new {
my ( $class, $args ) = @_;
if ( $args->{class} =~ /car/i ) {
return Vehicle::Car->new($args);
}
if ( $self->fueltype eq "gas" ) {
return Vehicle::Car::Gas->new($args);
}
if ( $self->fueltype eq "diesel" ) {
return Vehicle::Car::Diesel->new($args);
}
if ( $self->fueltype eq "electric" ) {
return Vehicle::Car::Electric->new($args);
}
}
На данный момент в моем реальном коде - в отличие от моего примера - есть много данных экземпляра, которые затем передаются новому объекту. Я думаю, это может быть немного уродливо, если мне нужно явно передать все данные между старым и новым объектом.
В моем реальном коде могут быть сотни / тысячи таких объектов, передаваемых из файла конфигурации, причем все требуют одинаковой обработки, но с некоторыми различиями в том, как это сделать. Это разница между использованием Expect и SSH для получения данных с удаленного устройства или использованием SNMP. Второй «уровень» информации основан на информации, которую я получаю, когда запрашиваю удаленное устройство и получаю его тип устройства (среди прочего) ...
И последнее замечание: я почти закончил писать программное обеспечение, но возникло очень «запоздалое» и важное требование, которое требует этого изменения. Я действительно хочу разместить поздний запрос как можно проще и элегантнее. Я не хочу "взломать" его и изменить интерфейс в main
.
Заранее спасибо за любые указатели.