Вопрос по теории ООП программирования

Bambino

Новичок
Вопрос по теории ООП программирования

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

PHP:
$app = new Application();
$app->run()
Именно этот главный класс "приложения" и занимается дальнейшим созданием других классов и собственно управляет приложением. Дальше любая другая функциональность, по идее, должна "храниться" в других классах, т.е. если нужна работа с БД, то пишем класс управления БД, если нужна работа с протоколированием, то пишем класс протокола и т.д. Тогда с определенной уверенностью я могу сказать, что для хранения и работы с конфигурацией приложения должен быть класс, который будет считывать (например, из файла) и хранить конфигурацию приложения, а также обновлять ее. И вот тут меня определенного рода затык.

Имею примерно такой код:

PHP:
class Application {
  private $_config = null;

  public function run () {
    self::loadCore();
    self::_loadConfig(); // вот здесь загружается конфигурация
    self::loadPlugins
  }

  private function _loadConfig () {
    $this->_config = new Config ();  // создается объект конфигурации, через который я могу получать определенные параметры приложения
  }
}
Я не знаю как правильно делать, т.е. нужно ли создавать объект $_config как свойство класса Application или же это должен быть какой-то "глобальный" класс. Но в ООП понятие глобальности как такового нет (?). Т.е. в данном случае это должен быть класс, где параметры конфигурации могут быть получены через какой-нибудь статический метод.

Если все еще не понятен мой вопрос, то дополняю следующим. В методе self::loadPlugins подключаются и создаются объекты плагинов, классы которых находятся в неком каталоге. Каждый класс плагина использует определенную часть конфигурации приложения. Мой конфигурационный файл представлен в виде ini-файла, который я загружаю через parse_ini_file, где для каждого плагина есть своя секция. Вопрос - как плагину достучаться до своей конфигурации, если конфигурацией управляет отдельный класс (а следовательно объект). Правильнее будет создать какой-то "глобальный" объект Config, к которому я смогу обращаться как из кода плагинов, так из кода любого другого класса, например, в классах ядра мне понадобиться обратиться к конфигурации? Или же объект конфигурации создается в классе приложения (см. выше) и потом просто передается по ссылке в любой другой класс, которому необходимо использовать конфигурацию? Очень надеюсь, что выразил свои мысли правильно.

Т.е. вот примеры кода:

PHP:
class Plugin1 {
  public function __construct () {
    echo Config::PluginName["SomeProperty"]; // глобальный доступ к свойству
  }
}

class Plugin2 {
  public function __construct (&$configObject) {
    echo $configObject->PluginName["SomeProperty"]; // получили доступ к свойству через ссылку на объект конфигурации
  }
}
Что более правильно?
 

Dovg

Продвинутый новичок
self::loadCore();
self::_loadConfig(); // вот здесь загружается конфигурация
self::loadPlugins
Почему они статичные? и зачем их в run, а не в конструктор?
Вопрос - как плагину достучаться до своей конфигурации, если конфигурацией управляет отдельный класс (а следовательно объект)
singleton?
 

Bambino

Новичок
Автор оригинала: Dovg
Почему они статичные? и зачем их в run, а не в конструктор?
Кто они? методы не статичные.. Ну можно и в конструктор... наверное в данном случае это не важно.

Да, объект "приложение" - синглтон.
 

Духовность™

Продвинутый новичок
скажу по своему опыту, это не более чем теже GLOBALS, только вид сбоку. С точки зрения ООП - да, правильно, а фактически удобств и отличий от обычного массива - нет.

как плагину достучаться до своей конфигурации
ИМХО, загрузить её тогда, когда надо.

-~{}~ 03.08.09 17:27:

я бы наверно так сделал

PHP:
class Plugin {
  public function __construct () {
    $conf = Config::getInstance(); // или делать это в базовом классе Base_Plugin

    $conf->loadPluginCfg('news_plugin');
  }
}
 

флоппик

promotor fidei
Команда форума
Партнер клуба
скажу по своему опыту, это не более чем теже GLOBALS, только вид сбоку. С точки зрения ООП - да, правильно, а фактически удобств и отличий от обычного массива - нет.
лол, а что паттерны реализуются какими-то особыми паттернывыми средствами ЯП ?
 

stillwaiting

Новичок
Ну раз Вы ООП изучаете, то вот вам :) :

Код:
<?php
class AppConfig
{
    public $myParam = 0;
    
}



class Application
{
    protected static $config = NULL;

    public static function &getConfig()
    {
        if (empty(Application::$config)) {             
            Application::$config = new AppConfig();            
            Application::$config->myParam = 5;
        }
        return Application::$config;
    }
}


abstract class AppPlugin
{
    protected $config;
    public function __construct() 
    {
        $this->config =& Application::getConfig();
    }
    abstract public function foo();
}



class MyPlugin1 extends AppPlugin
{
    public function foo()
    {
        var_dump($this->config);
    }    
}



class MyPlugin2 extends AppPlugin
{
    public function foo()
    {
        var_dump($this->config);
    }    
}


$a1 = new MyPlugin1();
$a2 = new MyPlugin2();

$a1->foo();
$a2->foo();

Application::getConfig()->myParam = 6;

$a1->foo();
$a2->foo();



?>
Output:
Код:
object(AppConfig)#2 (1) {
  ["myParam"]=>
  int(5)
}
object(AppConfig)#2 (1) {
  ["myParam"]=>
  int(5)
}
object(AppConfig)#2 (1) {
  ["myParam"]=>
  int(6)
}
object(AppConfig)#2 (1) {
  ["myParam"]=>
  int(6)
}
 

korchasa

LIMB infected
stillwaiting
Делаешь "Application::getConfig()->myParam = 6;", а у тебя вывод "$a1->foo();" меняется. Удобно так, магичненько. :D
 

Alexandre

PHPПенсионер
Для модульности, независимости и высокой доли абстракции мне не открыли глаза на Phemto
 

Bambino

Новичок
Автор оригинала: stillwaiting
Ну раз Вы ООП изучаете, то вот вам :) :
Интересно.. кажется даже то, что нужно.. :)
Один момент. Конфигурация каждого плагина уникальна и берется из ini-файла. Причем возможен вариант, когда плагину нужна как своя настройка, так и настройка предка т.е.
структура такая:

PHP:
class DefaultPlugin {
  // Здесь какие-то общие свойства
}

class PluginApi extends DefaultPlugin {
}

class Plugin1 extends PluginApi {
}

class Plugin2 extends DefaultPlugin {
}
Так вот Plugin1 должен получить общую конфигурацию разделов [PluginApi] и [Plugin1], а плагин Plugin2 только [Plugin2]

Пример ини-файла:

PHP:
[Application]
ядро_настройка =

[PluginApi]
api_настройка =

[Plugin1]
параметр1 = 

[Plugin2]
параметр1 =
Вообще, если поближе к реалиям... Создается продукт, который работает с АПИ магазина, который предоставляет партнерскую программу. Есть класс PluginApi, который инкапсулирует апи магазина, т.е. в итоге его методы создают определенные запросы к БД магазина. На основе PluginApi создаются потомки-плагины, которые вызывают выполняют определенное действие, т.е. плагин 1 - "получить список товаров", плагин 2 - "получить список популярных товаров". Каждый плагин "дергает" свою функцию АПИ для формирования запроса к БД плюс к запросу добавляется список предопредленных (неизменяемых) параметров. Но есть и простые плагины, когда полное АПИ реализовывать не нужно, потому как из него берется только одна функция. Вот почему в примере выше есть плагины, которые наследуют от класса PluginApi, а есть те, кто просто наследует от DefaultPlugin. В конечном итоге мне нужна вот такой метод:

PHP:
public function getUrl ( array $params ) {
  // Здесь массив $params - это массив параметров конфигурации плагина или массив общей конфигурации - плагина и его апи
  $query_string = ''
  foreach ( $params as $paramName => $paramValue )  {
    $query_string .= '&' . $paramName . "=" . urlencode ( $paramValue );
  }
}
Сильно ли видоизменится ваш код? Или всю описанную функциональность я должен буду реализовать в методе getConfig?

P.S. Т.е. если я правильно понял, то ответ на мой вопрос - создаем объект конфигурации в классе Application и через статический метод обращаемся в любом месте к необходимому разделу конфигурации?

@флоппик: насчет Registry паттерна туговато... :( Прочитал, правда бегло, ни за что не зацепился. Можно как-то в двух-трех словах? :) Или то, что предложил stillwaiting как раз то, что нужно?
 

stillwaiting

Новичок
Так вот Plugin1 должен получить общую конфигурацию разделов [PluginApi] и [Plugin1], а плагин Plugin2 только [Plugin2]
Можно регистрировать конфиги плагинов в application, и вызывать не getConfig() а getLoadedPluginConfigs() например.

Код:
var_dump(Application::getLoadedPluginConfigs());
/*
   Outputs something like array{
   'myplug1' => obj1,
   'myplug2' => obj2
   ....
*/
Тогда конфиги всех плагинов будут всегда под рукой.

Ниже не читал, сори, конец рабочего дня, моск отключился.
 

Bambino

Новичок
Автор оригинала: stillwaiting
Можно регистрировать конфиги плагинов в application, и вызывать не getConfig() а getLoadedPluginConfigs() например.

Код:
var_dump(Application::getLoadedPluginConfigs());
/*
   Outputs something like array{
   'myplug1' => obj1,
   'myplug2' => obj2
   ....
*/
Тогда конфиги всех плагинов будут всегда под рукой.
Нет, немного не то. Проблема в том, что один плагин может быть "сыном", т.е.
PHP:
class Father {
}
class Son extends Father {
}
, тогда как другой "внуком"
PHP:
class GrandFather {
}
class Father extends GrandFather{
}
class Son extends Father {
}
Причем, если в первом случае "сын" использует только свой конфиг, то во втором случаем "сын" должен использовать свою конфигурацию и конфигурацию "отца". Т.е. нужно каким-то образом реализовать иерархическое чтение конфигурации.
 

stillwaiting

Новичок
Ну сделайте что-нибудь типа такого:
PHP:
abstract class PluginBase
{
    public $config;
}

class Plugin1 extends PluginBase
{
    public function __construct()
    {
        $this->config[__CLASS__] = $this->getPluginData();
    }
    static private function getPluginData()
    {
        return "Plugin1Data";
    }
}

class Plugin2 extends Plugin1
{
    public function __construct()
    {
        parent::__construct();
        $this->config[__CLASS__] = $this->getPluginData();
    }
    static private function getPluginData()
    {
        return "Plugin2Data";
    }
}

class Plugin3 extends Plugin2
{
    public function __construct()
    {
        parent::__construct();
        $this->config[__CLASS__] = $this->getPluginData();
    }
    static private function getPluginData()
    {
        return "Plugin3Data";
    }
}

$p3 = new Plugin3();
var_dump($p3);

$p2 = new Plugin2();
var_dump($p2);
Output:
Код:
object(Plugin3)#1 (1) {
  ["config"]=>
  array(3) {
    ["Plugin1"]=>
    string(11) "Plugin1Data"
    ["Plugin2"]=>
    string(11) "Plugin2Data"
    ["Plugin3"]=>
    string(11) "Plugin3Data"
  }
}
object(Plugin2)#2 (1) {
  ["config"]=>
  array(2) {
    ["Plugin1"]=>
    string(11) "Plugin1Data"
    ["Plugin2"]=>
    string(11) "Plugin2Data"
  }
}
Добавьте кэширование конфигураций, регистрирование плагинов в Application, блэкджек и распутных девиц - и будет щастье :)
 

cDLEON

Онанист РНРСlub
Так вот Plugin1 должен получить общую конфигурацию разделов [PluginApi] и [Plugin1], а плагин Plugin2 только [Plugin2]
А можно воспользоваться методом parent::get_config();
 

weregod

unserializer
Автор оригинала: triumvirat
скажу по своему опыту, это не более чем теже GLOBALS, только вид сбоку. С точки зрения ООП - да, правильно, а фактически удобств и отличий от обычного массива - нет.
не так, например, как два пальца реализовать readonly-GLOBALS или доступ к данным с проверкой уровня доступа/инициатора доступа
например, приложение предполагает расширяемость сторонними разработчиками, но дать возможность сломать само приложение расширениями критично
 
Сверху