Правильная структура вызова классов

Andreika

"PHP for nubies" reader
теперь дошло... вот только не дошло причем тут именно "конструктор", но в целом мысль очевидна - исключения это все происки западных империалистов, советский гражданин не пользуется исключениями :)
 

whirlwind

TDD infected, paranoid
Чем неудобны исключения? Если я вызвал метод/функцию, я хочу получить результат ее работы. Кому нужны исключения например от неправильного формата отданного в sprintf? Для того, что бы юзать спринтф, мне не нужно знать какие исключения она генерирует, потому что исключение - исключение из правил, исключение из алгоритма. Для юзания спринтфа мне достаточно взглянуть на прототип этой функции. Если я использую что то, что генерирует исключения (пусть это библиотека), значит я обязательно должен предусматривать возможность их обработки. При чем в тех местах, где мой код взаимодействует с кодом библиотеки. Во всех этих местах код утолщается за счет try/catch-ей, которые я просто вынужден там ставить, если хочу получить стабильный код. Этого не видно, пока не напишешь. Это снаружи все превращается в изящный одновариантный отлов, когда вам всего то надо вывести сообщение об ошибке. А чем глубже, тем больше. С исключениями вместо того, что бы обработать только результат функции, приходится отлавливать и все возможные исключение, при чем отлавливать их каждый раз, когда они могут возникнуть, а не один раз где то снаружи, как пытаются представить некоторые.
 

Andreika

"PHP for nubies" reader
если хочу получить стабильный код
такое очучение, что try catch у Вас это как @ перед функцией/error_reporting(0). варнинги какие-то непонятные выдает - отключим и получим стабильный код.
 

whirlwind

TDD infected, paranoid
Немного в другой плоскости. что такое выброс исключения? это делегирование обработки неизвестно кому, но точно известно, что не коду выбросившему исключение. Это значит что программист не захотел реализовать нормальную обработку ошибочной ситуации. Проще говоря, кусок алгоритма вылетает на долю обработчика исключения. Чаще всего это является следствием лени, а не необходимости.

Какая тут связь с конструктором? А самая прямая. Сейчас как раз стоит вспомнить, о том что я говорил ранее:

1. конструктор вызываем не мы
2. конструктор не возвращает значения

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

-~{}~ 13.02.06 18:23:

ЗЫ. кого еще не убедил, попробуйте представить реализацию эктив-роу (CrUD) на основе драйвера-БД выбрасывающего исключения и не выбрасывающего таковые. Элементарный подсчет количество сеток try/catch покажет что удобнее обойтись драйвером без исключений.
 

Andreika

"PHP for nubies" reader
PHP:
class DB {
    protected $conn;

    function __construct() {

       $this->conn = mysql_connect();
       if (!$this->conn) { throw new Exception(mysql_error(),mysql_errno()); }
   }
    function query($q) {
///...
    }
}


try {
   $db = new DB();
   $db->query('SELECT....');
   $db->query....
} catch(Exception $e) {
   log($e->getMessage(),$e->getCode());  
}
напишите пожалуйста свой аналогичный красивый код (нисколько не сомневаюсь, что это возможно - сто лет жили без исключений и еще столько бы прожили) с комментариями почему именно так (идеологически) правильнее
заранее спасибо
 

itprog

Cruftsman
whirlwind
Можно одним catch-ем ловить все возможно выбрасываемые исключения. Но для того чтобы хоть как то их разделить делают несколько классов исключений с "говорящим" названием и использовав type-hinting можно обработать по-разному разные типы исключений
 

zerkms

TDD infected
Команда форума
[offtop]
log($e->getMessage(),$e->getCode())

брать логарифм сообщения ошибки по основанию кода ошибки конечно стильно :Р
[/offtop]
 

whirlwind

TDD infected, paranoid
Вообще не стоит заморачиваться писаниной именно по этому поводу, потому что этот пример абсолютно ничего не демонстрирует. Вы рассмотрите примеры, когда ваш DB экземпляр используется далеко от места его создания. К тому же ошибку подключения к БД можно таки отнести к исключению. Использование класса DB после рефакторинга (надеюсь код писать не заставите) выглядело бы так
PHP:
$db = new DB(); 
if ( !$db->query('SELECT....') ) echo $db->getLastError();
получилось хотя бы короче. А как реализовано все это дело внутри класса, меня, как пользователя класса абсолютно не колышет.

-~{}~ 13.02.06 18:41:

>Можно одним catch-ем ловить все возможно выбрасываемые исключения
фишка в том, практически никогда не приходится ловить исключения в одном месте. Даже при возможности определения типа. Я повторюсь - пропробуйте эктив роу c исключениями, и вы поймете о чем я говорю.
 

zerkms

TDD infected
Команда форума
фишка в том, практически никогда не приходится ловить исключения в одном месте.
смысл в том - что в каждом месте ловится вероятное исключение - после которого можно работу приложения восстановить, либо завершить наиболее корректно....

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

имхо - это вполне нормальная и распространённая практика
 

Andreika

"PHP for nubies" reader
1. мне прийдется угадывать ошибка в запросе или в "подключении" (база конечно может помереть и после вызова конструктора, но.. )
2. ставить во всех методах класса всевозможные проверки
3. таскать в пользовательском скрипте проверки на результат выполнения, которые будут уж никак не короче

К тому же ошибку подключения к БД можно таки отнести к исключению.
угу, это критическая ошибка, после которой Я считаю, что работа МОЕГО объекта (экземпляра) на этом завершена и мило прощаюсь вызовом исключения (в конструкторе). также я считаю, что скрипт, использующий МОЙ объект (класс) как основу должен быстренько свернуть свою работу, форсировать стек вызовов© и выдать в браузер Unhandled exception. ТЫ считаешь, что ТВОЙ скрипт спокойно проживет без моего объекта - лови исключение, игнорь его и работай дальше спокойно. тем более, что этих исключений не по 100 штук на класс.

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

whirlwind

TDD infected, paranoid
> что в каждом месте ловится вероятное исключение

Зачем? И чем это отличается от

PHP:
if ( !$this->myCall() ){

}
Это пятое колесо телеге при чем в более длинной интерпретации. Исключение - деление на ноль ну никак не решается! Остальное - необоснованная попытка сломать структуру работы программы.

-~{}~ 13.02.06 19:13:

Andreika:
Чесслово, утомляет говорить одно и то же. будьте же внимательнее.

>мне прийдется угадывать ошибка в запросе или в "подключении"
> ставить во всех методах класса всевозможные проверки

У вас в try что находится помимо конструкции? Запросы у вас находятся. Могут там быть исключения? Могут. Значит вам все равно придется разбирать в catch-e все возможные ситуации, если для вас это имеет значение. Судя по примеру - не имеет. Я в своем варианте так и написал - значения не имеет.

>таскать в пользовательском скрипте проверки на результат выполнения, которые будут уж никак не короче

А try/catch у вас где сейчас?
 

zerkms

TDD infected
Команда форума
if ( !$this->myCall() ){

}
что этот код значит??
что результат кастуется в (bool)false ???

а если там ноль к примеру. тем более что false - тоже результат....

исключения во всех языках широко искользуются в ООП, они есть - удобный инструмент.

ps: active row (эктив роу (c)) это - The Active Record Pattern?? и какие же у него особенности перед другими методами работы с данными - которые могут помешать использовать исключения??

pps: предыдущий пост андрейка написал хороший, солидарен с ним полностью
 

Mich

Продвинутый новичёк
Зачем? И чем это отличается от
тем что вы можете не обрабатывать исключение на этом уровне и оно вплывет выше. Иначе вам придется делать это вручную. И исключений может быть несколько.
 

whirlwind

TDD infected, paranoid
>а если там ноль к примеру. тем более что false - тоже результат
Не суть важно. Смысл в том, что это то же самое что и
PHP:
try{
   $this->myCall()
}catch(){
 .....
}
только короче.


>The Active Record Pattern?? и какие же у него особенности перед другими методами работы с данными - которые могут помешать использовать исключения??

Это просто пример. Сколько ловушек для исключений у вас там будет, при условии что для работы с БД вы пользуетесь драйвером, генерирующим исключения? Какого уровня будут эти исключения? Это будут исключения поступающие от драйвера БД. Потому что вам наплевать на другие исключения - вы не вызываете код, который может генерировать другие исключения. И если вы пишете давно, в любом юните у вас отлавливаются исключения, генерирующиеся глубже, иначе вы теряете контроль над юнитом.

PS.

> во всех языках широко искользуются в ООП, они есть - удобный инструмент

Где такое вы видели? Рьяное желание заюзать исключения я наблюдал только в дельфях и пхп.

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

Andreika

"PHP for nubies" reader
whirlwind
Могут там быть исключения? Могут.
от это новость.. и откуда ж им там (в данной реализации) взяться? ветром их надует чтоли?

ставить во всех методах класса всевозможные проверки это примерно if (!$this->conn) return false; в сотне методов и еще сотня вызовов этих методов, т.к. ваша программа ж не станет решать почему это $db->query вернула false и продолжет настойчиво выполнять остальные запросы.. а если там еще и будет какой нибудь "посторонний" алгоритм, который не должен выполняться при неработающей базе, но не должен пропускаться в случае неверного запроса

ЗЫ. на всяк случай - я не предлагаю использовать throw вместо return false;
 

whirlwind

TDD infected, paranoid
ГЫ. блин, все, последний пост и я домой пошел :)

Чем "дальше" уплывает исключение от места где его сгенерили, тем сложнее его анализировать. В самом верху как правило дальше ... $e->getMessage() дело не идет. Только в том месте где был сделан выброс исключения проще всего оперировать данными, приведшими к исключению, как следствие - проще обработать ошибку.

Вернемся к драйверу БД с исключениями и эктив роу. Допустим эктив роу не отлавливает исключения. Допустим эктив роу используется другими классами, например контроллером MVC в качестве модели. Все классы нашей иерархии выбрасывают исключения. Все исключения отлавливаются где-то снаружи. Допустим, для объекта эктив роу было установлено некорректное значение определенного поля, запрос сорвался, исключение уплыло наверх. И если сильно постараться то можно разгрести гемморой образованный наверху множеством различных исключений. Допустим что мы наконец то выловили исключение, связанное с неправильной установкой определенного поля. Ну допустим вывели сообщение об этом пользователю. Ну а если мы хотим заменить неправльное значение на правильное и повторно выполнить запрос что тогда? Как нам углубиться в иерархию? Нереально! Что бы решить эту проблему мы вынуждены поставить ловушку на исключения в результате выполнения запросов при CrUD операциях... И такого гемороя нас ожидает по всему проекту... В результате вылизывания и причесывания, все исключения у нас ходят не дальше одного уровня, а следовательно от них можно отказаться - и кода будет по свободнее, и структура попрозрачнее.

Это только один из множества вариантов. Исключения нужны тем, кто не может/не хочет писать надежный код.
 

itprog

Cruftsman
whirlwind
Только в том месте где был сделан выброс исключения проще всего оперировать данными, приведшими к исключению, как следствие - проще обработать ошибку.
каким образом оперировать ? Они дали ошибку значит они неверные, к примеру пришел объект не того типа, как с ним оперировать?

Вернемся к драйверу БД...
А разве с if мы не получаем таких же подобных конструкций и недостатков?
Ловушка (catch) может поменять значение и вызвать обратно исключение, передав ему свою информацию. Только вот ситуация не совсем правильная, исключения не для проверки что от клиента пришла валидная дата и pid в виде числа. Их можно приспособить к подобной валидации, но отсюда и изобилие try/catch.

Теперь про отличие условия от исключения.

PHP:
<?php
function a($int) {
if (!is_integer($int)) { throw new Exception('$int is not integer'); }
if ($int > 100) { throw new Exception('$int > 100 '); }
if ($int < 10) { throw new Exception('$int < 10 '); }
return $int;
}

try {
a(1402);
} catch (Exception $e) {
echo "<h1>" . $e->getMessage() . "</h1>";
}
?>
Теперь попробуй изобразить это без исключений и предположим константы в a() меняются (программистом конечно).

Исключения нужны тем, кто не может/не хочет писать надежный код.
Т.е. на JAVA большинство пишут ненадежный, как и сама JAVA, код?
 

whirlwind

TDD infected, paranoid
itprog я уже говорил, хватит тут показывать на примере бесполезных echo $e->getMessage и отлов в пределах одного уровня. Это не аргумент в данном случае. Приведите примеры где у вас между выбросом и отловом исключения несколько вызовов, т.е. бектрейс боле одного элемента. Я уверен, что все эти примеры будут примером кривого дизайна, рефакторинг которого сводится к 100% переписыванию.

-~{}~ 14.02.06 12:17:

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

да запросто
PHP:
function a($int) { 
   if (!is_integer($int)) return null;
   if ($int > 100) return 1;
   if ($int < 10) return -1;
...
}
вы сейчас начнете говорить, а как же если будут еще варианты? во-первых, еще варианты либо кодируются другими статусными кодами, либо добавляется метод, позволяющий получить детализированую расшифровку (гр.говоря все сводится к validate+getValidationResult), либо алгоритм делится на несколько методов в зависимости от задачи. И все эти варианты будут гораздо удобнее, нежели каждый раз парсить сообщение об ошибке из исключения. Потому что сообщение об ошибке - это та информация, с которой удобно работать человеку, а не алгоритму.
 

Andreika

"PHP for nubies" reader
whirlwind
забыл написать что теперь с результатом функции a() делать? проверять ===null потом ==0 потом уже -1 или 1, что конечно же на 2 порядка удобнее

а вы еще про конструкторы или вообще про исключения?
 
Сверху