Модераторы: korob2001, ginnie
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Переход от процедурного к ОО программированию. 
:(
    Опции темы
fedor-semakov
Дата 26.4.2005, 06:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 26.4.2005

Репутация: 2
Всего: 2



Всем привет.
Походил по форуму и прочитал тут все, что касается ООП.
Начал с этой темы http://forum.vingrad.ru/index.php?showtopic=43392
Спасибо korob2001 за такие понятные и поучительные сообщения.

У меня вопрос, как мне перейти на ООП.
Но сначало опишу ситуацию.

Есть у меня большой и работающий проект.
Когда писал его, ничего не знал о ООП.
Вернее знал, потому что активно использую сторонние модули со CPAN но вот писать сам все на модулях не умел, да и не видел смысла, ибо разделять свой код ни с кем не собирался, а значит и не видел смысла оформлять все в модули.
Все было хорошо, пока я не решил перейти на mod_perl
Необходимость такого перехода объективно назрела, ибо в связи с растущей посещаемостью сайта нужно поднять производительность моих скриптов.
Ну так вот.
Пошел изучать mod_perl и начал пробовать на него переходить.
Все доки по mod_perl советуют именно ООП, чтобы экономить память.
Дело в том, что разница большая писать вот так например

Код

use CGI qw(:all);
print header();
print "hello";


или вот так

Код

use CGI;
my $q=new CGI;
print $q->header();
print "hello";


В первом случае первая строка экспортирует все подпограммы в скрипт.
А во втором случае, ничего не экспортируется, создается объект, и используется метод header

Так в чем же разница?
А разница в том, что mod_perl для первого случая будет раздувать каждый процесс, ибо экспортирует туда функции модуля CGI

Допустим сто клиентов ходят по сайту, будет сто процессов и в каждом функции CGI

Во втором же случае, во первых никакого экспорта нет, а во вторых мы в startup.pl пишем
use CGI();
и этот модуль расшаривается между процессами!
Экономия памяти просто огромная.
Теперь процессам не выделяется память под свой модуль CGI для каждого, они все пользуются расшаренным CGI.

Вопрос номер один.
Я правильно понимаю это?


Далее..

Ну так вот.


У меня все на функциях. Функции сгруппированы и разбиты по файлам.
Но я тут опишу все попроще.

Допустим вот так.
(я специально пишу по простому, не ищите тут ошибок, я только пытаюсь объяснить в общих чертах как у меня сейчас все сделано)


есть файл mylib.lib
в нем функции

Код

sub get_id{
my $id = param("id");
return $id;
}

sub print_header{
print header();
}

sub connect_to_mysql{
#bla bla
return $dbh;
}

sub get_user_name{
my $id=shift;
my $sql="SELECT name FROM users WHERE id=$id";
my ($user) = $dbh->selectrow_array($sql);
return $user;
}
1;


А теперь скрипт, который при запросе script.cgi?id=1 пишет Привет "Вася", а при script.cgi?id=2 Привет "Петя"

Код

#!/usr/bin/perl
use strict;
use CGI qw(:all);
use vars qw($dbh);

require "mylib.lib";


my $id=get_id();
connect_to_mysql();
my $name = get_user_name($id);
print_header();
print "Привет $name";


Ну и тд. принцип думаю понятен.

Проект большой, функций огромное кол-во, разбиты по разным файлам.
Вызывают друг друга, итд итп.

Там где нужно вызвать какую то функцию пишу require "file.lib"

Вообщем такая схема была удобна для меня, и она меня устраивала.

Надо написать новый скрипт?
Нет проблем.
У меня уже куча готовых функций, я в скрипте подключаю необходимые библиотеки, и все.

Надо добавить функциональность?
Нет проблем, пишу дополнительную функцию.

Вообщем все красиво, но только не под mod_perl



Кто знает как работает mod_perl меня поймет.
Сейчас что происходит?

Каждый апачев процесс хранит в себе огромную кучу кода, все эти функции.
При запуске нового процесса, опять компилит всю эту махину.
Мало того, что это потеря времени, так память просто съедается катастрофически!!!!

Вот если бы оформить все это дело в ООП и расшарить эти библиотеки между процессами, вот было бы то что надо.
Но я никогда не писал ООП

я не знаю как и что мне сейчас делать.


Допустим я могу просто все свои библиотеки передалать вот так.

Код

package Mylib;

sub get_id{
my $id = param("id");
return $id;
}

sub print_header{
print header();
}

#итд
1;


а в скрипте писать так.

use Mylib;
my $id=Mylib::get_id();

и я решаю все свои проблемы с mod_perl
Функции больше не экспортируются, все расшаривается, память не расходуется итд..
но где тут ООП?

Уж очень хочется перейти именно на ООП.

тоесть вызывать функции не как подпрограммы, а именно как методы объекта.

Но что есть объект в моем случае?

ну допустим я пишу.
my $mylib = new Mylib();
my $id=$mylib->get_id();

и что это поменяло?

А у меня этих библиотек с функциями разбито по куче файлов.
Мне что, теперь создавать в скрипте два десятка обьъектов, чтобы пользоваться нужными мне функциями?
Или как?

А допустим один файл с функциями он там использует функцию из второго файла, а та в свою очередь из третьего, раньше я об этом даже не думал, ибо в скрипте у меня идет
require "file1";
require "file2";
require "file3";


А теперь что?
Мне в скрипте писать
use File1;
use File2;
use File3;

потом создавать три объекта

потом при вызове метода первого объекта, передавать ему ссылки на два других объекта?

Ведь я заранее не знаю, понадобится ли методу первого объекта заюзать функцию из второго модуля, или нет.

Вообщем я в полном недоумении.
Сижу уже несколько дней, перелопатил кучу литературы в интернет, и до сих пор не понял в чем же смысл ООП и как, и что мне нужно переделывать.


Вообщем прошу вашей помощи дорогие гуру в ООП. smile

Это сообщение отредактировал(а) fedor-semakov - 26.4.2005, 07:02
PM MAIL   Вверх
korob2001
Дата 26.4.2005, 09:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2871
Регистрация: 29.12.2002

Репутация: 13
Всего: 61



Зачем создавать кучу классов??? Достаточно одного, в него помещай конструктор и свои методы или подпрограммы или функции, в Perl они выглядят абсолютно одинаково.
Но если у тебя весь проект разбит по отдельным файлам и ты не хочешь нарушить их групировку, просто реализуй наследование в одном, главном классе и создавай только его объект, а он уже будет наследовать все методы, которые находятся в подклассах. Т.е. ты будешь например обращаться к калассу Class2.pm через Class1.pm .

Как правило дизай каждой страницы не должен сильно отличаться, вот давай и напишем класс на основе которого и будет постоен наш сайт:

Вот каласс, в нём только 4 коротких метода, естественно я не стал вгонять в него все навороты, назовём его HtmlClass.pm
Код

package HtmlClass;

use strict;
use CGI;

my $cgi = new CGI;
# Создаём конструктор
sub new {
    my $check = shift;
    my $class = ref( $check ) || $check;
    my $self  = { };
    bless( $self, $class );
    return $self;
}

# Следующий метод просто выводит верхнюю часть станицы и таблицы
sub get_top {
    my $class = shift;
    my $title = shift || 'With out name';
    my $back_color = shift || 'white';
    print $cgi->start_html( -title   => $title,
                            -bgcolor => $back_color );
}

# Следующий метод выводит нижнюю часть страницы
sub get_bot {
    my $class = shift;
    my $copy = shift || 'My site';
    # Получаем текущий год
    my $year = ( localtime() )[5] + 1900;
    # Прибавляем к текущему году число 5
    my $to_year = $year + 5;
    print $cgi->hr();
    print $cgi->font( { -style => "font-size: +12px" }, "Cpyright © $year-$to_year $copy" );
    print $cgi->end_html();
}

# Сдедующий метод выводит начало таблицы
sub start_block {
    my $class          = shift;
    my $width          = shift || '100%';
    my $border         = shift || 0;
    my $align          = shift || 'left';
    my $title          = shift || 'Без имени';
    my $tit_back_color = shift || '#0000aa';
    my $mes_back_color = shift || '#cccccc';

    print $cgi->start_table( { -width       => $width,
                               -border      => $border,
                               -cellspacing => 0,
                               -cellpadding => 5 } );
             print $cgi->Tr();
                  print $cgi->start_td( { -width   => $width,
                                          -align   => 'left',
                                          -bgcolor => $tit_back_color } );
                       print $cgi->font( { -color => '#ffffff',
                                           -size  => '3' }, $title );
                  print $cgi->end_td();
             print $cgi->end_Tr();
             print $cgi->start_Tr();
                  print $cgi->start_td( { -width   => $width,
                                          -bgcolor => $mes_back_color,
                                          -align   => $align } );
}

# Следующий метод выводит нижнюю часть таблицы
sub stop_block {
    print $cgi->end_td();
    print $cgi->end_Tr();
    print $cgi->end_table();
}

1;

А вот так будет выгладеть код который его юзает:
Код

#!/usr/bin/perl -w
use strict;
use lib qw( . );
use HtmlClass;

# Создаём объект нашего класса
my $hc = HtmlClass->new();

# Выводим заголовок
print "Content-type: text/html\n\n";

# Выводим верхнюю часть страницы
$hc->get_top('Главная страница');

# Оборачиваем наше меню в блок
$hc->start_block( 200, 1, 0, '<b>Крутейшие поисковики сети</b>');
    # здесь вводим HTML ссылки
    print qq(<b><a href='http://yandex.ru'>Поисковик Yandex</a></b><br>);
    print qq(<b><a href='http://rambler.ru'>Поисковик Rambler</a></b><br>);
    print qq(<b><a href='http://aport.ru'>Поисковик Aport</a></b><br>);
    print qq(<b><a href='http://yahoo.com'>Поисковик Yahoo</a></b><br>);
    print qq(<b><a href='http://google.com'>Поисковик Google</a></b><br>);
    print qq(<b><a href='http://astalavista.com'>Поисковик Astalavista</a></b><br>);
    print qq(<b><a href='http://crack.ru'>Поисковик Crack</a></b><br>);
$hc->stop_block();


# Выводим нижнюю часть страницы
$hc->get_bot();

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

Все вроде бы ничего, но какой то у нас код разно-стильный получился из-за HTML тегов, если уж юзаем модуль CGI.mp, то не плохо было бы юзать его на полную катушку.

Но вопрос в том, как мы его подключим. Можно было бы написать в начале основного кода такие строки:
Код

use CGI;
my $cgi = new CGI;

Тем самым подключить CGI.pm и создать его объект, но мы так делать не будем. Так как наш класс уже имеет объект CGI.pm, мы просто унаследуем методы CGI.pm через наш класс. Для этого нам нужно реализовать наследование в нашем классе, нужно добавить в массив @ISA имя нашего наследуемого класса, т.е. добавляем в наш код, сразу после имени пакета такую строку:
Код

push( @ISA, "CGI" );

Другими словами наш класс должен начинаться так:
Код

package HtmlClass;
push( @ISA, "CGI" );
use strict;
use CGI;

my $cgi = new CGI;  # эту строку удаляем

Далее всё как и было раньше, единственное теперь нужно в каждом методе, заменить первую строку:
Код

my $class = shift;

изменить на такую:
Код

my $cgi = shift;

Это мы будем и из нашего класса наследовать методы CGI.pm, что бы и в нём не создавать объект CGI.pm.

Зато теперь мы унаследовали методы CGI.pm через наш класс. Теперь нам можно юзать эти методы в основном коде даже не подключая CGI.pm и не создавая его объекта второй раз. Теперь перепишем основной код:
Код

#!/usr/bin/perl -w
use strict;
use lib qw( . );
use HtmlClass;

# Создаём объект нашего класса
my $hc = HtmlClass->new();

# Выводим заголовок
print $hc->header( -charset => 'Windows-1251' );

# Выводим верхнюю часть страницы
$hc->get_top('Главная страница');

$hc->start_block( 200, 1, 0, $hc->b('Крутейшие поисковики сети') );
    # здесь вводим HTML ссылки
    print $hc->b( $hc->a( { -href => 'http://yandex.ru' }, 'Поисковик Yandex' ) );
    print $hc->br();
    print $hc->b( $hc->a( { -href => 'http://rambler.ru' }, 'Поисковик Rambler' ) );
    print $hc->br();
    print $hc->b( $hc->a( { -href => 'http://aport.ru' }, 'Поисковик Aport' ) );
    print $hc->br();
    print $hc->b( $hc->a( { -href => 'http://yahoo.com' }, 'Поисковик Yahoo' ) );
    print $hc->br();
    print $hc->b( $hc->a( { -href => 'http://google.com' }, 'Поисковик Google' ) );
    print $hc->br();
    print $hc->b( $hc->a( { -href => 'http://astalavista.com' }, 'Поисковик Astalavista' ) );
    print $hc->br();
    print $hc->b( $hc->a( { -href => 'http://crack.ru' }, 'Поисковик Crack' ) );
    print $hc->br();;


$hc->stop_block();


# Выводим нижнюю часть страницы
$hc->get_bot();

Как можно заметить, мы заюзали все нужные нам методы CGI.pm через наш класс и мы не создавали объект CGI дважды.

Ладно пойду я спать, что-то я засиделся сегодня ;))) , у нас уже 9:00.

Удачи.

Это сообщение отредактировал(а) korob2001 - 26.4.2005, 15:38


--------------------
"Время проходит", - привыкли говорить вы по неверному пониманию. 
"Время стоит - проходите вы".
PM MAIL WWW ICQ MSN   Вверх
fedor-semakov
Дата 26.4.2005, 11:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 26.4.2005

Репутация: 2
Всего: 2



korob2001
Спасибо за столь полноценный ответ.
С наследованием вроде бы понял, но есть вопрос.

Если я правильно понял, то строка
push( @ISA, "CGI" );
экспортирует все методы модуля CGI в наш модуль.

Допустим, ну или предположим smile мне это не нужно.

Маленький пример.

у меня к примеру в Test.pm свой метод header есть.

Тогда при вызове в скрипте этого метода, вызываться будет именно он, что мне и нужно, но а если где то потом мне нужен будет именно метод CGI::header, а не Test::header?

И еще.
В скрипте у меня не получится ли мешанина, которая в последствии приведет к трудночитаемости кода?.
Непонятно будет чей это метод.

пример.
my $obj = new Test.pm
print $obj->start_table();

start_table это мой метод, или наследуемый из CGI, потому что у меня такого метода нет, но я ведь склеротик и я не помню уже?




И еще, я опять таки хочу не только научиться ООП, но и научиться это делать максимально эффективно и правильно с точки зрения mod_perl

Не будет ли такое экспортирование методов CGI забирать лишнюю память?

Я подумал, а что если не экспортировать, а сделать например вот так.

Код

package Test;
sub new {    
    my $check = shift;
    my $class = ref( $check ) || $check;
    my $self  = { };
    use CGI;
    $self->{CGI} = new CGI;#создаем объект CGI и сохраняем его в нашем хеше
    bless( $self, $class );
    return $self;
}

sub cgi{#метод для доступа к методам CGI
   my $self=shift;
   return $self->{CGI};
}

sub get_hello{
   my $self=shift;
   return $self->cgi->h1("Hello");

}

1;




Код

#!/usr/bin/perl -w
use strict;
use lib qw( . );
use Test;
my $obj = new Test;

#Теперь нам понятно, чей это метод, наш или заимствованный у CGI 
print $obj->cgi->header();#Это заимствованный
print $obj->get_hello();#Это наш


Я тоже пойду спать, высплюсь обязательно вернусь, ибо есть еще много вопросов.
Рад что тут такие замечательные люди на этом форуме. smile




Добавлено @ 11:05
Цитата(fedor @ 26.4.2005, 11:01)
Не будет ли такое экспортирование методов CGI забирать лишнюю память?


тут я отвечу сам себе, ибо уже понял.
Нет не будет.
Ибо это не совсем экспортирование, это наследование.
Просто Perl если не находит какой то метод в нашем классе, он идет его искать в @ISA, поэтому с памятью будет все ок.


PM MAIL   Вверх
fedor-semakov
Дата 28.4.2005, 03:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 26.4.2005

Репутация: 2
Всего: 2



Господа, столкнулся с маленькой проблемой.

Я покажу, как ее можно увидеть.

Допустим

Код

package SimpleTest;
use strict;
use CGI;

sub new {
    my $this = shift;
    my $class = ref($this) || $this;
    my $self  = {};
    bless($self, $class);
    return $self;
}

sub header{
    my $q=new CGI;
    print $q->header();
}

1;


Код

#!/usr/bin/perl -w
use strict;
use SimpleTest;
my $obj = new SimpleTest;
print $obj->header();





Скрипт выводит
Цитата

Content-Type: text/html; charset=ISO-8859-1

1


Откуда взялась эта единица и как от нее избавиться?
PM MAIL   Вверх
korob2001
Дата 28.4.2005, 09:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2871
Регистрация: 29.12.2002

Репутация: 13
Всего: 61



Код

sub header{
    my $q=new CGI;
    print $q->header();
}

Если ты реализуешь наследование, то не нужно создавать объект внутри класса. Объект твоего класса это и есть объект CGI.pm
Код

sub header{
    my $q = shift;
    print $q->header();
}

Цитата

Скрипт выводит

Цитата 

Content-Type: text/html; charset=ISO-8859-1

1

Откуда взялась эта единица и как от нее избавиться?

Это результат выполнения print, т.е. если написать такое выражение:
Код

print print 'Hello!';

То в итоге мы получим такую строку "Hello!1", где "Hello!" результат выполнения второго print, а единица - результат выполнения первого print. Другими словами Perl выполняет такое выражение с конца, сначала он выводит строку, а потом истенный результат выполнения втоого print, т.е. 1, которая говорит о том, что второй принт был успешно выполнен, это именно он возвращает результ своего выполнения. Так происходит потому, что print можно рассматривать как функцию, которая возвращает истину или ложь, как результат своего выполнения. Вобщем избавься от одного print, либо в классе, либо в основной программе.
Код

package SimpleTest;
use strict;
use CGI;

sub new {
    my $this = shift;
    my $class = ref($this) || $this;
    my $self  = {};
    bless($self, $class);
    return $self;
}

sub header{
    my $q=new CGI;
    print $q->header();
}

1;

Код

#!/usr/bin/perl -w
use strict;
use SimpleTest;
my $obj = new SimpleTest;
$obj->header();

Удачи.

Это сообщение отредактировал(а) korob2001 - 28.4.2005, 09:48


--------------------
"Время проходит", - привыкли говорить вы по неверному пониманию. 
"Время стоит - проходите вы".
PM MAIL WWW ICQ MSN   Вверх
fedor-semakov
Дата 28.4.2005, 16:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 26.4.2005

Репутация: 2
Всего: 2



korob2001

Цитата(korob2001 @ 28.4.2005, 09:38)
Так происходит потому, что print можно рассматривать как функцию, которая возвращает истину или ложь, как результат своего выполнения.


Спасибо.
Надо же было так попасть в просак.
Иногда трудные вещи даются легко, а на мелочах спотыкаюсь. smile

PM MAIL   Вверх
wladk
Дата 15.7.2005, 21:44 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











Решил вот тоже научится ООП, и уперся в то, что не могу понять как реализовать множественное наследование? В смысле хочется отписать свой класс, посредствам которого возможно было бы вызывать не только методы CGI, но и DBI, IniFile, CGI::FastTemplate. Это и есть множественное наследование? Сейчас реализовал наследование от CGI, а для всех остальных метадов отписал вапперы. Не знаю насколько это правльно.
А вообще, с точки зрения парадигмы ООП, насколько обязательно реализовывать доступ к переменной объекта через метод объекта?

  Вверх
korob2001
Дата 16.7.2005, 00:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2871
Регистрация: 29.12.2002

Репутация: 13
Всего: 61



Цитата

Решил вот тоже научится ООП, и уперся в то, что не могу понять как реализовать множественное наследование? В смысле хочется отписать свой класс, посредствам которого возможно было бы вызывать не только методы CGI, но и DBI, IniFile, CGI::FastTemplate.

Класс MyClass.pm
Код

package MyClass;
push( @ISA, 'CGI', 'DBI' );
use strict;
use CGI;
use DBI;

# Конструктор
sub new {
    my $check = shift;
    my $class = ref( $check ) || $check;
    my $self  = { -host  => 'localhost',
                  -base  => 'DefinedBase',
                  -login => 'root',
                  -pass  => '', @_ };
    return bless( $self, $class );
}

# Определяем метод connect
sub connect {
    my $self = shift;
    return DBI->connect("DBI:mysql:host=" . $self->{ -host }
                           . ";database=" . $self->{ -base },
                                            $self->{ -login },
                                            $self->{ -pass } );
}

1;

Теперь в том же каталоге сохрани и файл, которы будет юзать наш класс:
Код

#!/usr/bin/perl -w
use strict;
use lib qw(.);
use MyClass;

# Создаём объект и изменяем базу данных,
# всё остальное: host, login, pass оставляем
# по умолчанию.
my $cls = new MyClass( -base => 'MyNewBase' );

# Через объект нашего класса работаем с CGI.pm
print $cls->header( -charset => 'Windows-1251' );
print $cls->start_html( -title => 'Просто тест' );
print $cls->center( $cls->h2('Привет, мир!') );
print $cls->end_html();

# Через объект нашего класса работаем с DBI
my( $dbh, $sth, $sql ) = ();
$sql = "SQL ЗАПРОС";
$dbh = $cls->connect();
$sth = $dbh->prepare( $sql );
$sth->execute();
$sth->finish();
$dbh->disconnect();

Если нужно указать другой хост или логин или пароль, то просто при создании объекта передай соответствующие аргументы. Допустим твой хост = localhost, база = myTest, логин = sqlkiller, пароль = 123456789. Тогда укажи их при создании объекта:
Код

my $cls = MyClass->new( -base  => 'myTest',
                        -login => 'sqlkiller',
                        -pass  => '123456789' );         

Заметь, что хост мы здесь не указали, так как localhost у нас установлен в качестве значения по умолчанию, в конструкторе класса, но если хочешь, можешь переопределить и его, просто в данном случае это не нужно.
Думаю теперь ситуация прояснилась. smile
Цитата

А вообще, с точки зрения парадигмы ООП, насколько обязательно реализовывать доступ к переменной объекта через метод объекта?

Желательно.
Просто когда ты обращаешься к переменной через метод, у тебя появляется возможность проверить полученные данные, например те, которые ты хочешь инициализировать члены класса. Для примера давай напишим класс:
SecondClass.pm
Код

package SecondClass;

# Глобальные переменные
our $num1 = 1;
our $num2 = 1;

sub new {
    return( bless( {}, ref( $_[0] ) || $_[0] ) );
}

# Метод который возвращает результаты деления переменной $num1 на $num2
sub getResult {
    return( $num1 / $num2 );
}

Теперь с ним в один каталог сохрани этот код, с любым именем:
Код

#!/usr/bin/perl -w
use strict;
use lib qw(.);
use SecondClass;

my $cls = SecondClass->new();

# Допустим юзер напрямую устанавливает переменные
# следующим образом
$SecondClass::num1 = 10;
$SecondClass::num2 = 0;

# Теперь вызываем метод деления
my $result = $cls->getResult();
print $result;

В результате мы получим ошибку, так как произошло деление на 0. Теперь давай подправим наш класс и код:
SecondClass.pm
Код

package SecondClass;

# Закрытые переменные, теперь они не доступны напрямую, только через методы доступа
my $num1 = 1;
my $num2 = 1;

sub new {
    return( bless( {}, ref( $_[0] ) || $_[0] ) );
}

# Метод установки значения первой переменной
sub setFirstNumber($) {
    shift;
    my $newNum = shift;
    # Если указано не число, завершаем выполнение метода
    # и переменная остаётся со значением по умолчанию.
    # На самом деле здесь можно производить и другие действия.
    return unless $newNum =~ /^\d+$/;

    # Если мы оказались здесь, значит мы получили число,
    # так как это первое число, оно может быть и 0.
    $num1 = $newNum;
}

# Метод установки второго числа
sub setSecondNumber($) {
    shift;
    my $newNum = shift || 1; # Избавляемся от нуля
    return unless $newNum =~ /^\d+$/;
    $num2 = $newNum;
}

# Метод который возвращает результаты деления переменной $num1 на $num2
sub getResult {
    return( $num1 / $num2 );
}

А код, который юзает этот класс измени следующим образом:
Код

#!/usr/bin/perl -w
use strict;
use lib qw(.);
use SecondClass;

my $cls = SecondClass->new();

# Устанавливаем значения переменных через методы
$cls->setFirstNumber(10);
$cls->setSecondNumber(0);

# Теперь вызываем метод деления
my $result = $cls->getResult();
print $result;

Теперь мы не получим ошибки, так как в методе определения второго члена класса мы не дали установить значение 0, вместо этого программа производит деление на значение по умолчанию, т.е. на 1.

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

Удачи. smile

Это сообщение отредактировал(а) korob2001 - 16.7.2005, 00:38


--------------------
"Время проходит", - привыкли говорить вы по неверному пониманию. 
"Время стоит - проходите вы".
PM MAIL WWW ICQ MSN   Вверх
wladk
Дата 18.7.2005, 10:48 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











Цитата(korob2001 @ 16.7.2005, 00:26)
Цитата

Решил вот тоже научится ООП, и уперся в то, что не могу понять как реализовать множественное наследование? В смысле хочется отписать свой класс, посредствам которого возможно было бы вызывать не только методы CGI, но и DBI, IniFile, CGI::FastTemplate.

Класс MyClass.pm
Код

# Определяем метод connect
sub connect {
    my $self = shift;
    return DBI->connect("DBI:mysql:host=" . $self->{ -host }
                           . ";database=" . $self->{ -base },
                                            $self->{ -login },
                                            $self->{ -pass } );
}



Вот с этого места подробнейsmile DBI вообще-то немного спецефичен, по моему пониманию тем, что метод connect, для него, является по сути методом new. Правильно ли я понимаю, что для всех классов-родителей, кроме первого в списке, нужно писать свои вапперы для методов new.
И еще, если существуют одноименные методы в разных родительских классах, то таки нужно для них писать свои вапперы. Но как?
Цитата

Цитата

А вообще, с точки зрения парадигмы ООП, насколько обязательно реализовывать доступ к переменной объекта через метод объекта?

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


Мммм, ну во-первых, а не правильнее ли проверять данные при получении от пользователя? Я про ключ "Т" говорю. А во-вторых, я имел в виду доступ на чтение. Вопрос поставлен был мной не корректно, согласен. Так вот как быть с чтением(получение значения переменных) брать значения переменных напрямую или писать методы?
  Вверх
korob2001
Дата 18.7.2005, 14:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2871
Регистрация: 29.12.2002

Репутация: 13
Всего: 61



Цитата

Вот с этого места подробней DBI вообще-то немного спецефичен, по моему пониманию тем, что метод connect, для него, является по сути методом new.

Да, ты всё верно понимаешь. Если нужно было сделать что-то более похожее, то можно было бы написать метод connect так:
Код

sub connect {
    my $self = shift;
    return DBI->connect(@_);
}

А обращаться к нему так:
Код

$cls->connect("DBI:mysql:host=$host;database=$base", $log, $pass);

Что более похоже на оригинальное обращение к connect(), класса DBI.
Цитата

Правильно ли я понимаю, что для всех классов-родителей, кроме первого в списке, нужно писать свои вапперы для методов new.

Нет, если я правильно понимаю слово ваперы. Заметь, что для CGI мы не переопределяли никаких методов. В DBI, connect и есть конструктор ( в Perl конструктор может называться как угодно, не обязательно new), потому мы обращаемся к нему явно через имя класса DBI->connect(). Он подключает нас к базе и возвращает объект.
Вот для примера напишем такой класс:
TestDBI.pm
Код

package TestDBI;
push( @ISA, "DBI" );
use DBI;

sub connect {
    shift;
    my $self  = DBI->connect(@_);
    return $self;
}

Заметь, что наш класс не имеет конструктора, так как нам необходимо только реализовать интерфейс DBI, мы возвращаем объект, который создал нам конструктор класса DBI. Теперь код который его юзает:
Код

#!/usr/bin/perl -w
use strict;
use lib qw(.);
use TestDBI;

# Настройки подключения
my $DBHOST = "localhost";
my $DBBASE = "TestBase";
my $DBLOG  = "root";
my $DBPASS = "";
my $sql    = "SELECT Name, Surname FROM Users WHERE Id='13' LIMIT 1";

my $dbh = TestDBI->connect("DBI:mysql:host=$DBHOST;database=$DBBASE", $DBLOG, $DBPASS);
my $sth = $dbh->prepare($sql);
$sth->execute();
my( $name, $surname ) = $sth->fetchrow_array();
$sth->finish();
$dbh->disconnect();
print "$name $surname\n";

Теперь мы полностью повторили интерфейс модуля DBI, точнее унаследовали. Мы не можем в основном коде написать DBI->connect(), так как мы не подключали его к основному коду, он подключен только в классе use DBI, если же подключать его в осной код, тогда вообще проподает смысл нашего класса, хотя его и так, практически нет, он просто тестовый.
Кстати у CGI.pm тоже есть некоторые заморочки с наследованием, вот для примера попробуй написать класс, который наследует все методы CGI.pm, затем напиши код, который будет получать параметры через метод param() и выводить их в браузер. Для примера создай форму, которая будет передавать методом POST параметры, основной программе, которая будет юзать твой класс.
Цитата

Мммм, ну во-первых, а не правильнее ли проверять данные при получении от пользователя? Я про ключ "Т" говорю. А во-вторых, я имел в виду доступ на чтение. Вопрос поставлен был мной не корректно, согласен. Так вот как быть с чтением(получение значения переменных) брать значения переменных напрямую или писать методы?

Не нужно путать класс и основную программу, класс может быть использован не только пользователем, но и программистом, который пишет свою программу и использует твой класс. ООП, это прежде всего подход при котором не нужны повторения часто используемого кода. Если ты будешь читать переменные напрямую, а устанавливать через методы, тогда теряется закрытость. Посмотри на их объявление, которое я приводил выше. Когда мы хотим обратиться к переменным напрямую, мы объявили их как our, а когда через методы, мы объявили их как my. Что я этим хотел сказать? То, что когда ты хочешь прочитать значение поля напрямую, то это поле должно быть объявлено как глобальное, т.е. our.

Теперь прикиним такую ситуацию:
Переменная в классе объявлена как глобальная. У тебя есть метод, который проверяет значение, которое пользователь хочет уствновить ( ВНИМАНИЕ! Пользователем в данном случае считается не тот, кто юзает конечное приложение, хотя и он тоже, это может быть другой программист, который пишет совершенно другую программу и использует твой класс ). Он устанавливает переменную через метод, который в состоянии проверить, действительно ли значение подходит для этого поля. Если да, то значение устанавливается, если нет то корректируется или просто сообщаем пользователю, каким должно быть значение. Но так переменная объявлена глобально (our), так как мы хотим считывать значение этой переменной через имя пакета, то мы с таким же успехом можем и установить её через имя пакета, так как она объявлена глобально, она в данном случае является членом класса, т.е. доступна через его имя ( напрямую ), что может нарушить работу программы или же могут быть получены не корректные данные.
Вобщем рекомендуется создавать метододы доступа, это свого рода плюс ООП, хотя это и не обязательно.

Это сообщение отредактировал(а) korob2001 - 18.7.2005, 14:27


--------------------
"Время проходит", - привыкли говорить вы по неверному пониманию. 
"Время стоит - проходите вы".
PM MAIL WWW ICQ MSN   Вверх
Guest
Дата 18.7.2005, 20:05 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











На основании всего прочитанного и высказанного написал класс и скрипт которые его использует. В чем я заблуждаюсь?


Код

package WebApp;

@ISA = qw/CGI IniConf CGI::FastTemplate DBI/; 

use strict;
use CGI;
use IniConf;
use CGI::FastTemplate;
use DBI;
###########################################################
# конструктор
###########################################################
sub new {
    my $check = shift;
    my $class = ref( $check ) || $check;
    my $self  = { };
    bless( $self, $class );
    return $self;
}
###########################################################
# Задание рут директории
###########################################################
sub setRootDir{
    my $self  = shift;
$self->{ rootdir } = shift || "$ENV{DOCUMENT_ROOT}";
return (1);

###########################################################
# Вывод рут директории
###########################################################
sub getRootDir{
    my $self  = shift;
    return $self->{ rootdir };    
}   
###########################################################
# работа с ини файлом
###########################################################
sub setIniFile{
    my $self  = shift;
    $self->{ cfg } = new IniConf(-file => $self->{ rootdir }.$_[0] );
    return (1);
}
sub cfg{
    my $self  = shift;
    return $self->{ cfg };
    }
###########################################################
# работа с шаблонами
###########################################################
sub setRootTpl{
    my $self  = shift;
    $self->{ tpl } = new CGI::FastTemplate ( $self->{ rootdir }.$_[0] );
    return (1);
}
sub tpl{
    my $self  = shift;
    return $self->{ tpl };
    }
###########################################################
# работа с CGI
###########################################################
sub InitCGI{
    my $self  = shift;
    $self->{ cgi } = new CGI;
    return (1);
}
sub cgi{
    my $self  = shift;
    return $self->{ cgi };
    }    
###########################################################
# Детект языка
###########################################################
sub LngDetect {
   my $self     = shift;
   $self->{LNG} = shift || "ua";
   return (1);
}
###########################################################
# Работа с БД
###########################################################
sub connectDB{
        my $self  = shift;
        $self->{ dbh } = "DBI:mysql:".$_[0].":".$_[1];
        $self->{ dbh } = DBI->connect($self->{ dbh }, $_[2], $_[3], {PrintError => 1, RaiseError => 1}) or die "Error: $DBI::errstr\n";

        return (1);
    }
sub dbh{
    my $self  = shift;
    return $self->{ dbh };    
    }
1; 


Код

use strict;
use lib qw (.);
use WebApp;

my $webapp=WebApp->new();
$webapp->InitCGI();
my $action = $webapp->cgi->param("action") || "all";
print $webapp->cgi->header( -type => "text/html; charset=windows-1251" );
$webapp->setRootDir();
$webapp->setIniFile("/conf/wlad.ini");
$webapp->setRootTpl("/tpl/new");
$webapp->tpl->define( start => "index.tpl",
                                        main => "main.tpl",
                                        links => "links.tpl"
                                        );
$webapp->tpl->assign( NAME_OF_SITE => "Cайт тесного круга ограниченных людей" );
$webapp->connectDB($webapp->cfg->val('DB', 'db_name'), $webapp->cfg->val('DB', 'db_host'), $webapp->cfg->val('DB', 'db_user'), $webapp->cfg->val('DB', 'db_user_p'));
#############################################################
### Детект языка запроса
#############################################################
$webapp->LngDetect($webapp->cgi->param("lng"));
#
##if($action eq "showcategory"){
##    
##}elsif ($action eq "showautor"){
##
##}elsif ($action eq "show"){    
##
##}else {
#############################################################
### Выборка всех категорий и вывод их в алфавитном порядке
#############################################################
my $sql = "SELECT Id, CategoryName_".$webapp->{LNG}." FROM tbl_category ORDER BY CategoryName_".$webapp->{LNG};
my $sth = $webapp->dbh->prepare($sql);
$sth->execute();
 while ( my $row_ary = $sth->fetchrow_arrayref() ) {
       $webapp->tpl->assign(LNG => $webapp->{LNG},
                                         ACTION =>$action,
                                         NAME => $row_ary->[1],
                                         ID => $row_ary->[0]
                                         ); 
     $webapp->tpl->parse( CENTER => ".links" );
    }

##}
$webapp->tpl->parse( DATA => "start" );
$webapp->tpl->parse( MAIN => "main" );
$webapp->tpl->print();
$sth->finish();

По поводу заморочек с наследование методов от CGI, таки есть.smile Для однообразия доступ ко всем методам всех родительских модулей решил одинаково.

По поводу переменных... Я наверное так и не выучу русский язык!:( Я имел в виду доступ к значениям хеша, К примеру

Код

$webapp->{LNG}


Правильно ли я понял, что такой доступ есть моветон? И лучше отписать метод
Код


sub LngOut{
    my $self  = shift;
    return $self->{ LNG };



Выглядит конечно симпатичней...
  Вверх
korob2001
Дата 19.7.2005, 20:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2871
Регистрация: 29.12.2002

Репутация: 13
Всего: 61



Цитата

По поводу переменных... Я наверное так и не выучу русский язык!:( Я имел в виду доступ к значениям хеша, К примеру

Хеш ты можешь менять так, как тебе удобнее, его метод и есть конструктор, потому проверяй данные перед посвящением объекта.

Это сообщение отредактировал(а) korob2001 - 19.7.2005, 20:31


--------------------
"Время проходит", - привыкли говорить вы по неверному пониманию. 
"Время стоит - проходите вы".
PM MAIL WWW ICQ MSN   Вверх
wladk
Дата 21.7.2005, 14:36 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











Я понял! То что я предложил, на самом деле не есть наследование! Это есть инкапсуляция! О слово придумали!:)
А наследование будет выглядеть таким образом
собственно сам класс
Код

package WebApp1;

use DBI;

@ISA = qw/DBI/;
use strict;


package WebApp1::db;
  use vars qw(@ISA);
  @ISA = qw(DBI::db);

package WebApp1::st;
  use vars qw(@ISA);
  @ISA = qw(DBI::st);
1; 

Ну и пример скрипта его использующий
Код

@ISA = qw/WebApp1/;
use strict;
use lib qw (.);
use WebApp1;

my $webapp = WebApp1->new();

my $dbh=WebApp1->connect("DBI:mysql:$bd:$host", $user, $passwd, { RaiseError => 1, AutoCommit => 0, RootClass => 'WebApp1' })or die "Error: WebApp1->errstr\n";;

my $sql ="bla -bla-bla";
my $sth = $dbh->prepare($sql);
$sth->execute();

bla-bla-bla

$sth->finish();
$dbh->disconnect();



Увы добится такого же эффекта от модуля CGI мне не удалось. Увыsmile((

А вот интересно, а что же такое полимрфизм?
  Вверх
nerezus
Дата 27.7.2005, 19:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Вселенский отказник
****


Профиль
Группа: Участник
Сообщений: 3330
Регистрация: 15.6.2005

Репутация: нет
Всего: 43



Спасибо korob2001, теперь написал модуль Net::IPRange, чуствую, что качество моего кода улучшилось =)

Теперь диапазон пробежать легко:

Код
use Net::IPRange;
my $ip = IPRange->new('253.254.253.252', '254.1.7.252');
while ($ip->next) { print $ip->value, "\n"; }



--------------------
Сообщество художников Artsociety.ru
PM MAIL WWW   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Perl: CGI программирование"
korob2001
sharq
  • В этом разделе обсуждаются вопросы относящиеся только к CGI программированию
  • Если ваш вопрос не относится к системному или CGI программированию, задавайте его в общем разделе
  • Если ваш вопрос относится к системному программированию, задавайте его здесь
  • Интерпретатор Perl можно скачать здесь ActiveState, O'REILLY, The source for Perl
  • Справочное руководство "Установка perl-модулей", качать здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, korob2001, sharq.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Perl: разработка для Web | Следующая тема »


 




[ Время генерации скрипта: 0.1062 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.