Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770
Форумы портала PHP.SU :: Версия для печати :: Тестовое задание
Форумы портала PHP.SU » » Объектно-ориентированное программирование » Тестовое задание

Страниц (2): [1] 2 »
 

1. karamba - 16 Июня, 2016 - 21:47:38 - перейти к сообщению
Мне прислали сделать тестовое задание из одной конторы. Я его сделал, но их не устроил результат. Помогите. Я не понимаю почему....
Вот задание:
Цитата:
Написать класс (или группу классов) для возможности логирования определенных сообщений.
- Возможность писать логи в stdout, mysql, файл по выбору. (Выбор осуществляется для всей системы логирования)
- Предусмотреть настройки mysql, пути до файла логирования
- Логи должны содержать:
* Дату и время события (в формате YYYY-MM-DD HH:MM:SS)
* Сообщение логирования (строка, массив, объект, исключение)
Код должен быть написан на языке PHP без использования фреймворков и сторонних библиотек.
Обязательно использование объектно-ориентированного подхода.
Архитектура программы должна быть масштабируема (возможность расширения функционала с минимальными правками существующего кода).

Я написал класс три класса для каждого типа логирования. Привожу код для MySQL

CODE (htmlphp):
скопировать код в буфер обмена
  1. class LogDB {
  2.  
  3.         private $host = 'localhost';
  4.         private $database = 'log';
  5.         private $user = 'root';
  6.         private $password = '';
  7.  
  8.         function __construct() {
  9.         $a = func_get_args();
  10.         $i = func_num_args();
  11.         if (method_exists($this,$f='__construct'.$i)) {
  12.             call_user_func_array(array($this,$f),$a);
  13.         }
  14.     }
  15.  
  16.     private function __construct4($host,$database,$user,$password) {
  17.         $this->host = $host;
  18.         $this->database = $database;
  19.         $this->user = $user;
  20.         $this->password = $password;
  21.     }
  22.  
  23.  
  24.         public function logAdd($entry){
  25.  
  26.                 $entry = date("Y-m-d H:i:s ").serialize($entry);
  27.  
  28.                 $mysqli = new mysqli($this->host, $this->user, $this->password, $this->database);
  29.                 if ($mysqli->connect_error) {
  30.                 die('Connect Error (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error);
  31.                 }
  32.  
  33.                 $mysqli->set_charset("utf8");
  34.                 $mysqli->query("insert into log (log) values('$entry')");
  35.                 if ($mysqli->errno) {
  36.                         die('INSERT Error (' . $mysqli->errno . ') ' . $mysqli->error);
  37.                 }
  38.  
  39.                 $mysqli->close();
  40.         }
  41. }


Они ответили что нет объекто-ориентированного подхода. Почему? Я не пойму, что они хотят
2. Bio man - 17 Июня, 2016 - 01:53:47 - перейти к сообщению
karamba пишет:
Почему?
потому что его нет.
в задании четко было сказано как надо делать.
во первых нужно выделить сущности.
- Logger: базовый абстрактный класс или интерфейс.
- StreamLogger: производный от Logger для записи логов в поток. В твоем случае это stdout.
- DBLogger: производный от Logger для записи логов в БД.
- FileLogger: производный от Logger для записи логов в файл.

StreamLogger принимает поток как параметр конструктора (в твоем случае php://stdout), открывает его и по завершению программы закрывает (в деструкторе).

DBLogger принимает параметром конструктора либо внешний объект для работы с БД (пусть будет PDO) либо принимает параметры для подключения к БД, и создает объект PDO сам.
Я бы предпочел первый вариант, он более оптимален в плане расширения. Тут вылезает полиморфизм - мы сможем подменить объект PDO на любой другой производный от PDO, а не хардкодим класс в логере.

FileLogger по сути лишний, так как есть StreamLogger, который может работать и с файловыми потоками.

Плюсом было бы написать логгер в соответствии с PSR-3, так как многие помешаны на PSR, и скорее всего посчитают плюсом.
И обязательно почитай PSR-1 и PSR-2, так как твой код ужасен.

Успехов!
3. T1grOK - 17 Июня, 2016 - 11:29:48 - перейти к сообщению
Для подобных вещей очень кстати использование паттерна Chain of responsibility.
4. Bio man - 17 Июня, 2016 - 13:51:41 - перейти к сообщению
T1grOK пишет:
Для подобных вещей очень кстати использование паттерна Chain of responsibility.
имхо это переусложнение. в задаче сказано, что писать логи нужно 1 логгером, что делает паттерн неприменимым в данной ситуации.
5. T1grOK - 17 Июня, 2016 - 16:19:34 - перейти к сообщению
Bio man пишет:
что делает паттерн неприменимым в данной ситуации

Все применимо - цепочка из одного звена. Но в любой момент количество звеньев можно нарастить, в чем вижу плюс - предусмотрительность к расширению возможностей.
6. LIME - 17 Июня, 2016 - 19:06:44 - перейти к сообщению
Bio man пишет:
StreamLogger принимает поток как параметр конструктора
Bio man пишет:
DBLogger принимает параметром конструктора либо внешний объект для работы с БД

тогда уж лучше принимать всем DTO
например объект конфига
чтоб уж интерфейс не плясал в разные стороны
(Добавление)
karamba пишет:
$mysqli = new mysqli
я после этого тут же расхотел отвечать когда первый раз вопрос увидел))
karamba пишет:
Дата рег-ции: Сент. 2010  
karamba попрактикуйся еще ...рано профессионально кодить
7. Bio man - 17 Июня, 2016 - 21:45:16 - перейти к сообщению
интерфейс интерфейсом а конструктор конструктором. одно другому не мешает. пусть конструкторы отличаются, не вижу в этом проблемы. тем более, код станет понятнее, если конструктор будет принимать понятные параметры (поток, PDO, параметры БД), нежели универсальный DTO (если я правильно понял, что ты имел в виду под DTO).
LIME пишет:
например объект конфига
тут не соглашусь. как раз наоборот, компоненты должны объявляться в конфиге и конфигурироваться приложением при запуске, т.е. сам объект (логгер) ничего не должен знать о конфиге.

Например:
PHP:
скопировать код в буфер обмена
  1. // config
  2. return [
  3.   'components' => [
  4.     'logger' => [
  5.       'class' => DBLogger::class,
  6.       'table' => 'log',
  7.       'db' => 'app',
  8.       'user' => 'root',
  9.       'password' => '',
  10.     ],
  11.   ],
  12. ];
  13.  
  14. // DBLogger
  15. class DBLogger extends Logger
  16. {
  17.   public function __construct($user, $pass, $db, $table)
  18.   {
  19.     // magic
  20.   }
  21. }
  22.  
  23. class StreamLogger extends Logger
  24. {
  25.   public function __construct($stream)
  26.   {
  27.     // magic
  28.   }
  29. }

LIME пишет:
karamba попрактикуйся еще ...рано профессионально кодить
это уж точно.
(Добавление)
T1grOK пишет:
Все применимо - цепочка из одного звена. Но в любой момент количество звеньев можно нарастить, в чем вижу плюс - предусмотрительность к расширению возможностей.
YAGNI. Когда будет потребность - будем рефакторить, иначе это лишнее переусложнение. Тем более это 1 компонент, добавил класс, добавил по методу в логгеры и в путь.
8. karamba - 19 Июня, 2016 - 12:49:30 - перейти к сообщению
Bio man пишет:
karamba пишет:
Почему?

во первых нужно выделить сущности.
- Logger: базовый абстрактный класс или интерфейс.
- DBLogger: производный от Logger для записи логов в БД.

DBLogger принимает параметром конструктора либо внешний объект для работы с БД (пусть будет PDO) либо принимает параметры для подключения к БД, и создает объект PDO сам.
Успехов!


Попробовал сделать как вы сказали. Посмотрите пожалуйста, оцените.
Я только не понимаю для чего нужен общий абстрактный класс. Я тоже думал, что нужно замутить что то связанное с наследованием. Но просто если этот супер класс убрать, то ничего не измениться, будет все также работать, значит он никак не влияет на функционал. Для чего он нужен?
Если конструктор класса DBLogger будет принимать внешний PDO объект, то наверное не будет учтено задание, которое гласит, что необходима настройка на БД. А так получится, что об настройке на БД должен будет позаботиться создатель этого внешнего PDO, а не мой класс.
Стоит ли делать перегрузку конструктора или это лишнее? Надо ли сделать деструктор для закрытия PDO или он и так закроется при уничтожении экземпляра класса?

PHP:
скопировать код в буфер обмена
  1. <?PHP
  2.  
  3. abstract class Logger {
  4.         abstract protected function logAdd($entry);
  5. }
  6.  
  7. class DBLogger extends Logger{
  8.  
  9.         private $DBH;
  10.  
  11.         function __construct() {
  12.         $a = func_get_args();
  13.         $i = func_num_args();
  14.         if (method_exists($this,$f='__construct'.$i)) {
  15.             call_user_func_array(array($this,$f),$a);
  16.         }
  17.     }
  18.  
  19.         private function __construct0() {
  20.                 try {
  21.                         $this->DBH = new PDO("mysql:host=localhost;dbname=log", 'root', '');
  22.                 }  
  23.                 catch(PDOException $e) {  
  24.                         echo $e->getMessage();  
  25.                 }
  26.         }
  27.  
  28.         private function __construct4($host,$dbname,$user,$pass) {
  29.                 try {  
  30.                         $this->DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
  31.                 }  
  32.                 catch(PDOException $e) {  
  33.                         echo $e->getMessage();  
  34.                 }
  35.         }
  36.  
  37.         function logAdd($entry) {
  38.                 $entry = date("Y-m-d H:i:s ").serialize($entry);
  39.                 try {
  40.                         $STH = $this->DBH->prepare("INSERT INTO log (log) values (?)");
  41.                         $STH->bindParam(1, $entry);
  42.                         $STH->execute();
  43.                 }
  44.                 catch(PDOException $e) {  
  45.                         echo $e->getMessage();  
  46.                 }
  47.         }
  48. }
  49.  
  50. $dbLog = new DBLogger();
  51. $dbLog->logAdd("I use OOP");
  52.  
  53. ?>
9. OrmaJever - 19 Июня, 2016 - 13:16:01 - перейти к сообщению
я бы наверное вот так сделал
PHP:
скопировать код в буфер обмена
  1. abstract class Logger
  2. {
  3.         protected $text;
  4.  
  5.         public function log( $entry )
  6.         {
  7.                 $this->text = date("[Y-m-d H:i:s] ").serialize($entry);
  8.         }
  9.  
  10.         public function write();
  11. }
  12.  
  13. class FileLoger extends Logger
  14. {
  15.         private $path;
  16.  
  17.         public function __construct( $path )
  18.         {
  19.                 if( !file_exists($path) ) {
  20.                         throw new Exception('File not exists: ' . $path);
  21.                 }
  22.         }
  23.  
  24.         public function write()
  25.         {
  26.                 return file_put_contents($this->path, $this->text . "\n", FILE_APPEND);
  27.         }
  28. }
  29.  
  30. class MysqlLoger extends Logger
  31. {
  32.         private $mysqli;
  33.  
  34.         public function __construct( $user, $pass, $dbname )
  35.         {
  36.                 $this->mysqli = new mysqli($user, $pass, $dbname);
  37.  
  38.                 if( $this->mysqli->connect_errno ) {
  39.                         throw new Exception('Mysqli comment error: ' . $this->mysqli->connect_error);
  40.                 }
  41.         }
  42.  
  43.         public function write()
  44.         {
  45.                 $stmt = $mysqli->prepare('INSERT INTO log (text) values (?)');
  46.                 $stmt->bindValue('s', $this->text);
  47.                 return $stmt->execute();
  48.         }
  49. }
  50.  
  51. class stdoutLogger extends Logger
  52. {
  53.         // ...
  54. }
  55.  
  56. $fileLogger = new FileLoger('./error.log');
  57. $fileLogger->log('случилась беда');
  58. $fileLogger->write();
  59.  
  60. $mysqlLogger = new MysqlLoger('user', 'pass');
  61. $mysqlLogger->log('ойойой');
  62. $mysqlLogger->write();
  63.  
10. karamba - 19 Июня, 2016 - 17:38:38 - перейти к сообщению
Да, вытащить сериализацию в базовый класс наверное можно, чтобы был в нем какой то смысл.
Я так понял, что использовать mysqli это уже не модно, надо через PDO.
И метод log лучше, чтобы вызывался прямо в write, чтобы ради одной записи не использовать два метода.
Почему если такого файла на диске нет, то просто не создать его?
11. Bio man - 19 Июня, 2016 - 17:42:11 - перейти к сообщению
Что это за хрень с __construct0 итд?
karamba пишет:
Я только не понимаю для чего нужен общий абстрактный класс.
для полиморфизма. в таком случае мы работаем с любым логгером, будто он является абстрактным логгером.
Почитай основы ООП, такие вещи ты должен знать прежде чем говорить что твое ООП - ООП
https://www[dot]youtube[dot]com/user/pro[dot][dot][dot]00fox2/playlists
12. Viper - 19 Июня, 2016 - 18:54:31 - перейти к сообщению
karamba пишет:
И метод log лучше, чтобы вызывался прямо в write, чтобы ради одной записи не использовать два метода.
наоборот. write() убрать и делать это в log() да + добавить возможность указания типа логгера. ИМХО

OrmaJever пишет:
$mysqlLogger = new MysqlLoger('user', 'pass');
ой ли? Если имело смысл использовать разные базюльки(в том числе и удаленный вариант), то смысла нет ибо DSN куда передавать?

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

Ну и для полного комплекта syslog ещё можно добавить.
13. karamba - 19 Июня, 2016 - 19:53:50 - перейти к сообщению
Bio man пишет:
Что это за хрень с __construct0 итд?
karamba пишет:
Я только не понимаю для чего нужен общий абстрактный класс.
для полиморфизма. в таком случае мы работаем с любым логгером, будто он является абстрактным логгером.
Почитай основы ООП, такие вещи ты должен знать прежде чем говорить что твое ООП - ООП
https://www.youtube.com/user/pro100fox2/playlists


Всмысле за хрень с __construct0 итд? Это так реализована перегрузка конструктора. Насколько я понял в PHP нет обычной перегрузки конструктора.

Как будет использоваться полиморфизм в данном случае? В моем абстрактом классе нет ни одного поля или неабстрактного метода. Если мы создадим экземпляр каждого типа логирования и запишем в массив. Будем массив перебирать и вызывать метод logAdd(), то он будет одинаково работать и при существовании базового класса и без него.
14. Viper - 19 Июня, 2016 - 21:05:25 - перейти к сообщению
karamba пишет:
Это так реализована перегрузка конструктора.
это не правильно.
15. karamba - 20 Июня, 2016 - 09:17:32 - перейти к сообщению
Viper пишет:
karamba пишет:
Это так реализована перегрузка конструктора.
это не правильно.

Всмысле неправильно? А как еще сделать перегрузку конструктора?

 

Powered by ExBB FM 1.0 RC1