![]() |
Модераторы: korob2001, ginnie |
![]() ![]() ![]() |
|
fedor-semakov |
|
||||||||||
Новичок Профиль Группа: Участник Сообщений: 11 Регистрация: 26.4.2005 Репутация: 2 Всего: 2 |
Всем привет.
Походил по форуму и прочитал тут все, что касается ООП. Начал с этой темы http://forum.vingrad.ru/index.php?showtopic=43392 Спасибо korob2001 за такие понятные и поучительные сообщения. У меня вопрос, как мне перейти на ООП. Но сначало опишу ситуацию. Есть у меня большой и работающий проект. Когда писал его, ничего не знал о ООП. Вернее знал, потому что активно использую сторонние модули со CPAN но вот писать сам все на модулях не умел, да и не видел смысла, ибо разделять свой код ни с кем не собирался, а значит и не видел смысла оформлять все в модули. Все было хорошо, пока я не решил перейти на mod_perl Необходимость такого перехода объективно назрела, ибо в связи с растущей посещаемостью сайта нужно поднять производительность моих скриптов. Ну так вот. Пошел изучать mod_perl и начал пробовать на него переходить. Все доки по mod_perl советуют именно ООП, чтобы экономить память. Дело в том, что разница большая писать вот так например
или вот так
В первом случае первая строка экспортирует все подпограммы в скрипт. А во втором случае, ничего не экспортируется, создается объект, и используется метод header Так в чем же разница? А разница в том, что mod_perl для первого случая будет раздувать каждый процесс, ибо экспортирует туда функции модуля CGI Допустим сто клиентов ходят по сайту, будет сто процессов и в каждом функции CGI Во втором же случае, во первых никакого экспорта нет, а во вторых мы в startup.pl пишем use CGI(); и этот модуль расшаривается между процессами! Экономия памяти просто огромная. Теперь процессам не выделяется память под свой модуль CGI для каждого, они все пользуются расшаренным CGI. Вопрос номер один. Я правильно понимаю это? Далее.. Ну так вот. У меня все на функциях. Функции сгруппированы и разбиты по файлам. Но я тут опишу все попроще. Допустим вот так. (я специально пишу по простому, не ищите тут ошибок, я только пытаюсь объяснить в общих чертах как у меня сейчас все сделано) есть файл mylib.lib в нем функции
А теперь скрипт, который при запросе script.cgi?id=1 пишет Привет "Вася", а при script.cgi?id=2 Привет "Петя"
Ну и тд. принцип думаю понятен. Проект большой, функций огромное кол-во, разбиты по разным файлам. Вызывают друг друга, итд итп. Там где нужно вызвать какую то функцию пишу require "file.lib" Вообщем такая схема была удобна для меня, и она меня устраивала. Надо написать новый скрипт? Нет проблем. У меня уже куча готовых функций, я в скрипте подключаю необходимые библиотеки, и все. Надо добавить функциональность? Нет проблем, пишу дополнительную функцию. Вообщем все красиво, но только не под mod_perl Кто знает как работает mod_perl меня поймет. Сейчас что происходит? Каждый апачев процесс хранит в себе огромную кучу кода, все эти функции. При запуске нового процесса, опять компилит всю эту махину. Мало того, что это потеря времени, так память просто съедается катастрофически!!!! Вот если бы оформить все это дело в ООП и расшарить эти библиотеки между процессами, вот было бы то что надо. Но я никогда не писал ООП я не знаю как и что мне сейчас делать. Допустим я могу просто все свои библиотеки передалать вот так.
а в скрипте писать так. 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; потом создавать три объекта потом при вызове метода первого объекта, передавать ему ссылки на два других объекта? Ведь я заранее не знаю, понадобится ли методу первого объекта заюзать функцию из второго модуля, или нет. Вообщем я в полном недоумении. Сижу уже несколько дней, перелопатил кучу литературы в интернет, и до сих пор не понял в чем же смысл ООП и как, и что мне нужно переделывать. Вообщем прошу вашей помощи дорогие гуру в ООП. ![]() Это сообщение отредактировал(а) fedor-semakov - 26.4.2005, 07:02 |
||||||||||
|
|||||||||||
korob2001 |
|
||||||||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2871 Регистрация: 29.12.2002 Репутация: 13 Всего: 61 |
Зачем создавать кучу классов??? Достаточно одного, в него помещай конструктор и свои методы или подпрограммы или функции, в Perl они выглядят абсолютно одинаково.
Но если у тебя весь проект разбит по отдельным файлам и ты не хочешь нарушить их групировку, просто реализуй наследование в одном, главном классе и создавай только его объект, а он уже будет наследовать все методы, которые находятся в подклассах. Т.е. ты будешь например обращаться к калассу Class2.pm через Class1.pm . Как правило дизай каждой страницы не должен сильно отличаться, вот давай и напишем класс на основе которого и будет постоен наш сайт: Вот каласс, в нём только 4 коротких метода, естественно я не стал вгонять в него все навороты, назовём его HtmlClass.pm
А вот так будет выгладеть код который его юзает:
И так можно в классе описать весь дизайн страницы, а потом подключать этот класс к каждой странице сайта. В итоге у нас получается одинаковый дизайн на каждой странице, без повтороного кода, так же в этом классе можно реализовать методы, которые работают с базой, метод счетчик посещений, да и вообще что угодно. Все вроде бы ничего, но какой то у нас код разно-стильный получился из-за HTML тегов, если уж юзаем модуль CGI.mp, то не плохо было бы юзать его на полную катушку. Но вопрос в том, как мы его подключим. Можно было бы написать в начале основного кода такие строки:
Тем самым подключить CGI.pm и создать его объект, но мы так делать не будем. Так как наш класс уже имеет объект CGI.pm, мы просто унаследуем методы CGI.pm через наш класс. Для этого нам нужно реализовать наследование в нашем классе, нужно добавить в массив @ISA имя нашего наследуемого класса, т.е. добавляем в наш код, сразу после имени пакета такую строку:
Другими словами наш класс должен начинаться так:
Далее всё как и было раньше, единственное теперь нужно в каждом методе, заменить первую строку:
изменить на такую:
Это мы будем и из нашего класса наследовать методы CGI.pm, что бы и в нём не создавать объект CGI.pm. Зато теперь мы унаследовали методы CGI.pm через наш класс. Теперь нам можно юзать эти методы в основном коде даже не подключая CGI.pm и не создавая его объекта второй раз. Теперь перепишем основной код:
Как можно заметить, мы заюзали все нужные нам методы CGI.pm через наш класс и мы не создавали объект CGI дважды. Ладно пойду я спать, что-то я засиделся сегодня ;))) , у нас уже 9:00. Удачи. Это сообщение отредактировал(а) korob2001 - 26.4.2005, 15:38 -------------------- "Время проходит", - привыкли говорить вы по неверному пониманию. "Время стоит - проходите вы". |
||||||||||||||||
|
|||||||||||||||||
fedor-semakov |
|
||||||
Новичок Профиль Группа: Участник Сообщений: 11 Регистрация: 26.4.2005 Репутация: 2 Всего: 2 |
korob2001
Спасибо за столь полноценный ответ. С наследованием вроде бы понял, но есть вопрос. Если я правильно понял, то строка push( @ISA, "CGI" ); экспортирует все методы модуля CGI в наш модуль. Допустим, ну или предположим ![]() Маленький пример. у меня к примеру в Test.pm свой метод header есть. Тогда при вызове в скрипте этого метода, вызываться будет именно он, что мне и нужно, но а если где то потом мне нужен будет именно метод CGI::header, а не Test::header? И еще. В скрипте у меня не получится ли мешанина, которая в последствии приведет к трудночитаемости кода?. Непонятно будет чей это метод. пример. my $obj = new Test.pm print $obj->start_table(); start_table это мой метод, или наследуемый из CGI, потому что у меня такого метода нет, но я ведь склеротик и я не помню уже? И еще, я опять таки хочу не только научиться ООП, но и научиться это делать максимально эффективно и правильно с точки зрения mod_perl Не будет ли такое экспортирование методов CGI забирать лишнюю память? Я подумал, а что если не экспортировать, а сделать например вот так.
Я тоже пойду спать, высплюсь обязательно вернусь, ибо есть еще много вопросов. Рад что тут такие замечательные люди на этом форуме. ![]() Добавлено @ 11:05
тут я отвечу сам себе, ибо уже понял. Нет не будет. Ибо это не совсем экспортирование, это наследование. Просто Perl если не находит какой то метод в нашем классе, он идет его искать в @ISA, поэтому с памятью будет все ок. |
||||||
|
|||||||
fedor-semakov |
|
||||||
Новичок Профиль Группа: Участник Сообщений: 11 Регистрация: 26.4.2005 Репутация: 2 Всего: 2 |
Господа, столкнулся с маленькой проблемой.
Я покажу, как ее можно увидеть. Допустим
Скрипт выводит
Откуда взялась эта единица и как от нее избавиться? |
||||||
|
|||||||
korob2001 |
|
||||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2871 Регистрация: 29.12.2002 Репутация: 13 Всего: 61 |
Если ты реализуешь наследование, то не нужно создавать объект внутри класса. Объект твоего класса это и есть объект CGI.pm
Это результат выполнения print, т.е. если написать такое выражение:
То в итоге мы получим такую строку "Hello!1", где "Hello!" результат выполнения второго print, а единица - результат выполнения первого print. Другими словами Perl выполняет такое выражение с конца, сначала он выводит строку, а потом истенный результат выполнения втоого print, т.е. 1, которая говорит о том, что второй принт был успешно выполнен, это именно он возвращает результ своего выполнения. Так происходит потому, что print можно рассматривать как функцию, которая возвращает истину или ложь, как результат своего выполнения. Вобщем избавься от одного print, либо в классе, либо в основной программе.
Удачи. Это сообщение отредактировал(а) korob2001 - 28.4.2005, 09:48 -------------------- "Время проходит", - привыкли говорить вы по неверному пониманию. "Время стоит - проходите вы". |
||||||||||||
|
|||||||||||||
fedor-semakov |
|
|||
Новичок Профиль Группа: Участник Сообщений: 11 Регистрация: 26.4.2005 Репутация: 2 Всего: 2 |
korob2001
Спасибо. Надо же было так попасть в просак. Иногда трудные вещи даются легко, а на мелочах спотыкаюсь. ![]() |
|||
|
||||
wladk |
|
|||
Unregistered |
Решил вот тоже научится ООП, и уперся в то, что не могу понять как реализовать множественное наследование? В смысле хочется отписать свой класс, посредствам которого возможно было бы вызывать не только методы CGI, но и DBI, IniFile, CGI::FastTemplate. Это и есть множественное наследование? Сейчас реализовал наследование от CGI, а для всех остальных метадов отписал вапперы. Не знаю насколько это правльно.
А вообще, с точки зрения парадигмы ООП, насколько обязательно реализовывать доступ к переменной объекта через метод объекта? |
|||
|
||||
korob2001 |
|
||||||||||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2871 Регистрация: 29.12.2002 Репутация: 13 Всего: 61 |
Класс MyClass.pm
Теперь в том же каталоге сохрани и файл, которы будет юзать наш класс:
Если нужно указать другой хост или логин или пароль, то просто при создании объекта передай соответствующие аргументы. Допустим твой хост = localhost, база = myTest, логин = sqlkiller, пароль = 123456789. Тогда укажи их при создании объекта:
Заметь, что хост мы здесь не указали, так как localhost у нас установлен в качестве значения по умолчанию, в конструкторе класса, но если хочешь, можешь переопределить и его, просто в данном случае это не нужно. Думаю теперь ситуация прояснилась. ![]()
Желательно. Просто когда ты обращаешься к переменной через метод, у тебя появляется возможность проверить полученные данные, например те, которые ты хочешь инициализировать члены класса. Для примера давай напишим класс: SecondClass.pm
Теперь с ним в один каталог сохрани этот код, с любым именем:
В результате мы получим ошибку, так как произошло деление на 0. Теперь давай подправим наш класс и код: SecondClass.pm
А код, который юзает этот класс измени следующим образом:
Теперь мы не получим ошибки, так как в методе определения второго члена класса мы не дали установить значение 0, вместо этого программа производит деление на значение по умолчанию, т.е. на 1. Можно было конечно перехватить эту ошибку с помощью eval, но я просто хотел показать пример того, что дают методы доступа. Если пояснить кратко, то они просто тебе дают возможность обработать данные перед их установкой. Удачи. ![]() Это сообщение отредактировал(а) korob2001 - 16.7.2005, 00:38 -------------------- "Время проходит", - привыкли говорить вы по неверному пониманию. "Время стоит - проходите вы". |
||||||||||||||||||
|
|||||||||||||||||||
wladk |
|
||||||||||
Unregistered |
Вот с этого места подробней ![]() И еще, если существуют одноименные методы в разных родительских классах, то таки нужно для них писать свои вапперы. Но как?
Мммм, ну во-первых, а не правильнее ли проверять данные при получении от пользователя? Я про ключ "Т" говорю. А во-вторых, я имел в виду доступ на чтение. Вопрос поставлен был мной не корректно, согласен. Так вот как быть с чтением(получение значения переменных) брать значения переменных напрямую или писать методы? |
||||||||||
|
|||||||||||
korob2001 |
|
||||||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2871 Регистрация: 29.12.2002 Репутация: 13 Всего: 61 |
Да, ты всё верно понимаешь. Если нужно было сделать что-то более похожее, то можно было бы написать метод connect так:
А обращаться к нему так:
Что более похоже на оригинальное обращение к connect(), класса DBI.
Нет, если я правильно понимаю слово ваперы. Заметь, что для CGI мы не переопределяли никаких методов. В DBI, connect и есть конструктор ( в Perl конструктор может называться как угодно, не обязательно new), потому мы обращаемся к нему явно через имя класса DBI->connect(). Он подключает нас к базе и возвращает объект. Вот для примера напишем такой класс: TestDBI.pm
Заметь, что наш класс не имеет конструктора, так как нам необходимо только реализовать интерфейс DBI, мы возвращаем объект, который создал нам конструктор класса DBI. Теперь код который его юзает:
Теперь мы полностью повторили интерфейс модуля DBI, точнее унаследовали. Мы не можем в основном коде написать DBI->connect(), так как мы не подключали его к основному коду, он подключен только в классе use DBI, если же подключать его в осной код, тогда вообще проподает смысл нашего класса, хотя его и так, практически нет, он просто тестовый. Кстати у CGI.pm тоже есть некоторые заморочки с наследованием, вот для примера попробуй написать класс, который наследует все методы CGI.pm, затем напиши код, который будет получать параметры через метод param() и выводить их в браузер. Для примера создай форму, которая будет передавать методом POST параметры, основной программе, которая будет юзать твой класс.
Не нужно путать класс и основную программу, класс может быть использован не только пользователем, но и программистом, который пишет свою программу и использует твой класс. ООП, это прежде всего подход при котором не нужны повторения часто используемого кода. Если ты будешь читать переменные напрямую, а устанавливать через методы, тогда теряется закрытость. Посмотри на их объявление, которое я приводил выше. Когда мы хотим обратиться к переменным напрямую, мы объявили их как our, а когда через методы, мы объявили их как my. Что я этим хотел сказать? То, что когда ты хочешь прочитать значение поля напрямую, то это поле должно быть объявлено как глобальное, т.е. our. Теперь прикиним такую ситуацию: Переменная в классе объявлена как глобальная. У тебя есть метод, который проверяет значение, которое пользователь хочет уствновить ( ВНИМАНИЕ! Пользователем в данном случае считается не тот, кто юзает конечное приложение, хотя и он тоже, это может быть другой программист, который пишет совершенно другую программу и использует твой класс ). Он устанавливает переменную через метод, который в состоянии проверить, действительно ли значение подходит для этого поля. Если да, то значение устанавливается, если нет то корректируется или просто сообщаем пользователю, каким должно быть значение. Но так переменная объявлена глобально (our), так как мы хотим считывать значение этой переменной через имя пакета, то мы с таким же успехом можем и установить её через имя пакета, так как она объявлена глобально, она в данном случае является членом класса, т.е. доступна через его имя ( напрямую ), что может нарушить работу программы или же могут быть получены не корректные данные. Вобщем рекомендуется создавать метододы доступа, это свого рода плюс ООП, хотя это и не обязательно. Это сообщение отредактировал(а) korob2001 - 18.7.2005, 14:27 -------------------- "Время проходит", - привыкли говорить вы по неверному пониманию. "Время стоит - проходите вы". |
||||||||||||||
|
|||||||||||||||
Guest |
|
||||||||
Unregistered |
На основании всего прочитанного и высказанного написал класс и скрипт которые его использует. В чем я заблуждаюсь?
По поводу заморочек с наследование методов от CGI, таки есть. ![]() По поводу переменных... Я наверное так и не выучу русский язык!:( Я имел в виду доступ к значениям хеша, К примеру
Правильно ли я понял, что такой доступ есть моветон? И лучше отписать метод
Выглядит конечно симпатичней... |
||||||||
|
|||||||||
korob2001 |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2871 Регистрация: 29.12.2002 Репутация: 13 Всего: 61 |
Хеш ты можешь менять так, как тебе удобнее, его метод и есть конструктор, потому проверяй данные перед посвящением объекта. Это сообщение отредактировал(а) korob2001 - 19.7.2005, 20:31 -------------------- "Время проходит", - привыкли говорить вы по неверному пониманию. "Время стоит - проходите вы". |
|||
|
||||
wladk |
|
||||
Unregistered |
Я понял! То что я предложил, на самом деле не есть наследование! Это есть инкапсуляция! О слово придумали!:)
А наследование будет выглядеть таким образом собственно сам класс
Ну и пример скрипта его использующий
Увы добится такого же эффекта от модуля CGI мне не удалось. Увы ![]() А вот интересно, а что же такое полимрфизм? |
||||
|
|||||
nerezus |
|
|||
![]() Вселенский отказник ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 3330 Регистрация: 15.6.2005 Репутация: нет Всего: 43 |
Спасибо korob2001, теперь написал модуль Net::IPRange, чуствую, что качество моего кода улучшилось =)
Теперь диапазон пробежать легко:
|
|||
|
||||
![]() ![]() ![]() |
Правила форума "Perl: CGI программирование" | |
|
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, korob2001, sharq. |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Perl: разработка для Web | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |