Модераторы: skyboy, MoLeX, Aliance, ksnk
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Как правильно агрегировать ошибки бизнес логики ? 
:(
    Опции темы
awebtech
  Дата 27.11.2012, 18:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



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

Просто складывается ощущение, что куча addErrors(), getErrors() на каждом уровне не есть правильно.

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

Заранее спасибо за любые ответы.

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

Код

    class ErrorContainer {
        private $errors = array();
        function addError($error) { if (!is_array($error)) { $this->errors[] = $error;} else { $this->errors = array_merge($this->errors, $error); } }
        function getErrors() { return $this->errors[]; }
        function hasErrors() { return !empty($this->errors); }
    }
    
    class Processor extends ErrorContainer {
        function process($account_id, $orders) {
            $account = new Account();
            if (!$account->loadById($account_id)) { $this->addErrors($account->getErrors);}
            
            foreach ($orders as $order_id) {
                $order = new Order();
                if (!$order->loadById($order_id)) { $this->addErrors($order->getErrors);}
            }
        }
        
        return $this->hasErrors();
    }
    
    class Account extends ErrorContainer {
        function loadById($account_id) {
            $account = select_from_database($account_id);
            if (!$account) { $this->addError("Account is missing"); }
            if (!$account['active']) { $this->addError("Account is inactive"); }
            // and so on, some checks may add errors in a cycle
            
            return $this->hasErrors();
        }
    }
    
    class Order extends ErrorContainer {} // very similar to Account, but has its own checks
    
    //Usage:
    
    $errors = array();
    $items = load_items_from_xml($xml);
    foreach ($items as $item) {
        $processor = new Processor();
        if (!$processor->process($item['account_id'], $item['orders'])) {
            $errors = array_merge($errors, $processor->getErrors());
        }

    }

PM MAIL WWW   Вверх
skyboy
Дата 27.11.2012, 19:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

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



Цитата(awebtech @  27.11.2012,  17:50 Найти цитируемый пост)
Ошибка бизнес логики не предполагает критичесого завершения приложения, такую ошибку надо просто отметить и продолжать работу дальше 

тогда глобальный логгер-синглтон. исключения как раз для того, чтоб на текущем этапе прервать выполнение
PM MAIL   Вверх
Arantir
Дата 27.11.2012, 19:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


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

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



awebtech, вне что-то кажется, что у вас одни и те же ошибки много раз отмечаются.
Код

if (!$account) { $this->addError("Account is missing"); }
if (!$account->loadById($account_id)) { $this->addErrors($account->getErrors);}

Вы вызываете 2 раза addError для ошибки "Account is missing". Если бы у вас был лог-файл, в нем бы получилось 2 дублирующихся записи.

Как посоветовал skyboy, используйте класс-логгер. Отлавливайте им ошибки  только неполсредственно в момент их возникновения. А там, где ошибка может возникнуть выше, просто делайте return (завершайте выполнение текущей функции) с указанием ошибки, но без логирования.


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
awebtech
Дата 27.11.2012, 19:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Цитата(skyboy @  27.11.2012,  19:07 Найти цитируемый пост)
тогда глобальный логгер-синглтон. исключения как раз для того, чтоб на текущем этапе прервать выполнение 

Вот тоже думал в эту сторону, спасибо за мнение.

Добавлено через 6 минут и 53 секунды
Цитата(Arantir @  27.11.2012,  19:22 Найти цитируемый пост)
Вы вызываете 2 раза addError для ошибки "Account is missing". Если бы у вас был лог-файл, в нем бы получилось 2 дублирующихся записи.

Как посоветовал skyboy, используйте класс-логгер. Отлавливайте им ошибки  только неполсредственно в момент их возникновения. А там, где ошибка может возникнуть выше, просто делайте return (завершайте выполнение текущей функции) с указанием ошибки, но без логирования. 

Ага, забыл в примере return добавить. Отдельный логгер пока выигрывает.

Есть еще идея унаследоваться всем от одного класса и использовать уже эту базу в качестве логгера ? Как думаете, стоит ?
PM MAIL WWW   Вверх
Arantir
Дата 27.11.2012, 19:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


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

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



Вот пример. Это только пример, на ошибки не тыкать - не проверял, написал на скорую руку. Для полевых условий он не предназначен =)
Код

class Logger {

    private static $_instance;
    private $logfile = "/logs/proccess.log";
    public $last_error;

    public function getInstance()
    {
        if (!isset($_instance))
            $_instance = new Logger;
        return $_instance;
    }
    
    public function error($message) 
    {
        $last_error = $message;
        $file = fopen("/logs/proccess.log", "a");
        fwrite(date("d.m.y H:i:s")." [ERROR] ".$message, $file);
        fclose($file);
    }
}

class Processor {
    function process($account_id, $orders) {
        $account = new Account();
        if (!$account) return FALSE;
        
        foreach ($orders as $order_id) {
            $order = new Order();
            if (!$order) continue;
        }
    }
    return TRUE;
}

class Account {
    function loadById($account_id) {
        $account = select_from_database($account_id);
        if (!$account) { Logger::getInstance()->error("Account is missing"); return FALSE; }
        if (!$account['active']) { Logger::getInstance()->error("Account is inactive"); return FALSE; }
        // and so on, some checks may add errors in a cycle
        
        return TRUE;
    }
}

class Order {} // very similar to Account, but has its own checks

//Usage:

$items = load_items_from_xml($xml);
foreach ($items as $item) {
    $processor = new Processor();
    if (!$processor->process($item['account_id'], $item['orders'])) {
        Logger::getInstance()->error("Could not do proccess for {$item['account_id']}, {$item['orders']}"); 
        continue;
    }
}

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

Это сообщение отредактировал(а) Arantir - 27.11.2012, 19:41


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
ksnk
Дата 27.11.2012, 19:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


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

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



Цитата(awebtech @  27.11.2012,  19:25 Найти цитируемый пост)
Есть еще идея унаследоваться всем от одного класса и использовать уже эту базу в качестве логгера ? Как думаете, стоит ? 

imho - нет. Можно посмотреть на достаточно навороченные cms-ки. У Yii, например есть скелетный статический класс (Yii), который может добраться до остальных частей смс, также он хранит связующие элементы, а есть "мясо" - классы типа СWebApplication и так далее, которые уже делают то, что надо. Все общие части - дебагеры, логеры и фабрики объектов находятся в скелете.

Отнаследоваться не имеет смысла, так как логер обязан быть единственным на все приложение, а так получится по логеру в каждом объекте. В Javascript это можно было бы сделать  smile, а в php - нет.


--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "PHP"
Aliance
IZ@TOP
skyboy
SamDark
MoLeX

Новичкам:

  • PHP редакторы собираются и обсуждаются здесь
  • Электронные книги по PHP, документацию можно найти здесь
  • Интерпретатор PHP, полную документацию можно скачать на PHP.NET

Важно:

  • Не брезгуйте пользоваться тегами [code=php]КОД[/code] для повышения читабельности текста/кода.
  • Перед созданием новой темы воспользуйтесь поиском и загляните в FAQ
  • Действия модераторов можно обсудить здесь

Внимание:

  • Темы "ищу скрипт", "подскажите скрипт" и т.п. будут переноситься в форум "Web-технологии"
  • Темы с именами: "Срочно", "помогите", "не знаю как делать" будут УДАЛЯТЬСЯ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, IZ@TOP, skyboy, SamDark, MoLeX, awers.

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


 




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


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

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