ООП: использование экземпляра класса в другом классе

6epcepk

Новичок
ООП: использование экземпляра класса в другом классе

Сегодня осуществил вторую попытку написания логики ядра на ООП.
Пример:
PHP:
class Kernel {
	
	function __construct($config) {
		
		// класс для работы с БД от Дмитрия Котерова
		$db = DbSimple_Generic::connect(...);
		
		get preferences

		$this->loadTemplate();
				
	}
	
	private function loadTemplate() {
	
		echo 'html';
	
	}

	private function loadModule() {
	
		require ...
		call subClass
	
	}

}

class News extends Kernel {

	function __construct($config) {
		
		sql_query
		access kernel preferences
		return result;
				
	}


}

$kernel = new Kernel($config);
Ну так вот. Все работает отлично, кроме как обращения к стороннему классу для работы с БД.
Неужели надо в каждой функции выполнять создание экземпляра класса БД?
Конечно, возможен вариант, написать функции в главном классе:
PHP:
protected function query($query) {
    return $db->query($query);
}
После обращаться parent::query(); Но это не лучший выход.
Как поступить?
 

whirlwind

TDD infected, paranoid
А передать экземпляр класса БД в другой экземпляр религия не позволяет? Или Вы считаете, что возможность хранить экземпляр в переменной придумали исключительно в декоративных целях?
 

AmdY

Пью пиво
Команда форума
6epcepk
а тебе не кажется, что ты пишешь всёже используя процедурный подход, у тебя каждый метод сам по себе.
у тебя при $kernel = new Kernel($config); должно выполняться процентов 80 действий, а уж после отробатывать экшин.
 

6epcepk

Новичок
Mr_Max
Понял, исправился.

whirlwind
Стыдно, про самое простое и забыл :)
Но допустим ситуация: я подключаю сторонний класс к своей системе и тогда мне приходится переделывать аргументы всех методов?

AmdY
Конструктор как раз использует все нижеописанные методы (я их подсократил), просто вынес по отдельности все.

Главное ядро:
PHP:
class Kernel {

    private static $instance = null;
    
    public static function getInstance($config) {
	 
        if (is_null(self::$instance)) {
		
            self::$instance = new Kernel($config);
			
        }
        
        return self::$instance;
    }
    
    private function __construct($config) {
	
		$db = DbSimple_Generic::connect();

		$this->loadComponent($db);
		
    }

    private function __clone() {}
    
	private function loadComponent($db) {
	
		require module file (see file contents below)
		$module = Module::getInstance($db); // А если я подключаю еще класс ... переписывать все аргументы методов?
		
	}

}
Демо-модуль, который вызывается в методе loadComponent:
PHP:
class Module extends Kernel {

    private static $instance = null;
    
    public static function getInstance($db) {
	 
        if (is_null(self::$instance)) {
		
            self::$instance = new Module($db);
			
        }
        
        return self::$instance;
    }
	
	private function __construct($db) {
	
		...get data...
		
		parent::encode($results);
	
	}
	
	 private function __clone() {}

}
Собственно инициализация ядра:
PHP:
$kernel = Kernel::getInstance($config);
Сильно безграмотно выглядит?
 

AmdY

Пью пиво
Команда форума
как ты думаешь твой код стал проще в расширении и сопровождении, он облегчает тебе работу?
 

6epcepk

Новичок
AmdY
Нет, я еще не готов к переходу на ООП.
Пару попыток было, сейчас порыв вызван тем, что взял новую библиотеку для работу с СУБД, а вся система написана на функциях, поэтому придется в каждую функцию добавлять global $db, чтобы экземпляр класса стал виден в функциях.
Поэтому решил один раз отучаться и написать хотя бы предварительную версию системы на ООП.
Ваши мысли..?
 

AmdY

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

whirlwind

TDD infected, paranoid
Но допустим ситуация: я подключаю сторонний класс к своей системе и тогда мне приходится переделывать аргументы всех методов?
Используйте паттерн Adaptor

PS. Пора переходить на ООП.
 

6epcepk

Новичок
AmdY
Сейчас вникаю в Symfony, пока разобрал как взаимодействует класс ядра, класс конфигураций и класс утилит, никак не могу найти как обрабатываются модули.

whirlwind
А если сделать гибрид между Adaptor и Singleton?
То есть в модуль будет передаваться ссылка на экземпляр ядра, но каждый класс в свою очередь вызывается единожды, поэтому по методу getInstance в ядро и модуль.

джамшут
Ядро получается параметры конфигурации системы (настройки, языки, сайты), подгружает модули, следит за правами доступа и т.п.
 

whirlwind

TDD infected, paranoid
То есть в модуль будет передаваться ссылка на экземпляр ядра, но каждый класс в свою очередь вызывается единожды, поэтому по методу getInstance в ядро и модуль.
Вот честно пытался понять, но не осилил.

Класс не вызывается, вызываются методы класса, в контексте экземпляра класса. Вообще есть понимание что такое класс? Класс - это тип. Например число, строка - это типы. Чем отличаются эти типы? Например, тем что у строки есть длина, а у числам присущи такие характеристики как четное/нечетное, позитивное/негативное, целое/дробное и т.п. По этому, соотв наборы методов обработки типа число и типа строка будут разными.

То же самое и классы. Классы - это типы. Только число это одно значение, а объект - это совокупность значений всех атрибутов.

Прежде всего, если класс используется 1 раз (Вашим языком) - это плохой класс. Только представьте, сколько кода получится в итоге, если на каждый чих придется писать отдельный класс. Чем чаще класс используется, тем он удачнее и полезнее.

Теперь Вы понимаете как нелепо звучит "каждый класс в свою очередь вызывается единожды"? Это то же самое, что один раз на всю программу использовать одну строку, один раз число и т.п. Такие классы ничего не улучшают и не облегчают, они только усложняют и запутывают.
 

6epcepk

Новичок
whirlwind
Да конечно, это была игра слов, извиняюсь.

Попытаюсь описать скелет моей логики, что-то взял из Symfony, что-то сам домыслил:

Есть класс Config, его методы (первоначальная загрузка параметров конфигурации из БД, получение значение конфигурационного параметра по ключу) используются в других методах других классов многократно (Kernel, Module).

Есть класс Kernel, в нем определен конструктор, отвечающий за загрузку модулей.
При загрузке системы идет обращение к методу-конструктору однократное.

Есть класс Module, в нем определен конструктор, возвращающий данные модуля, предназначенные для вывода на экран.
При загрузке класса идет обращение к методу-конструктору однократное.

После определения классов:
PHP:
Kernel::getInstance(); // Согласно Singleton
exit;
Но опять для меня загадка с классом для работы с СУБД: передавать экземпляр класса как Kernel::getInstance($db), но если у меня добавляется новый сторонний классВ, мне придется переписывать аргументы всех методов классов модулей? - Не универсально. Объясните.

Я не особо понял чем лучше использование передачу ссылки на экземпляр класса в другой класс (Adaptor), чем просто использовать базовый класс и производный. (Хотя последнее видимо есть какой-то другой паттерн.)

Спасибо.
 

whirlwind

TDD infected, paranoid
PHP:
Kernel::getInstance(); // Согласно Singleton
Вот скажите, с какогу перепугу Вы сами послезавтра или кто-то другой с отличной от Вашей логикой, может догадаться что в результате Kernel::getInstance() будет сформирована страница? Где тут это написано? Вместо легко понятного ну хотя бы
PHP:
Kernel::getInstance()->showMeThePage();
Вы будете долго рыться в коде или писать, а затем читать килограмы документации по проекту... Но это так, присказка.

Идем далее. Конструкторы не предназачены для того, что бы служить точкой входа. Конструкторы предназначены для конструкции объекта, то есть его предварительной инициализации. Теперь ответьте на вопрос

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

Ваши классы должны быть спроектированны таким образом, что бы пользователь класса имел возможность ПОЛНОСТЬ влиять на поведение экземпляра. У Вас все с точностью до наоброт. Вы предусмотрели только один вариант использования класса - результат, который будет получен после отработки конструктора. То есть Вы даже не сможете повторно получить тот же самый результат без повторной конструкции экземпляра.

Если бы у Вас было хотя бы так
PHP:
// Здесь мы просто указываем конкретные объекты
$k = Kernel(new MySpecificDb($config),new MyOtherSpecific());
// и мы можем в любое время сделать все что угодно и с этими
// объектами и с ядром, изменив его поведение путем
// подмены этих объектов
$myDb = $k->getDatabase();
$k->setDatabase(new LogQueries($myDb));

// другой код инициализации

// После всего этого мы заставляем ядро выполнить свою задачу
$k->runPage();
То Вы просто бы добавили новую пару set/get в ядро. При этом старые классы, которые эту фишку не используют, не подвергались бы риску сломаться.

PS. А адаптор используется именно для адаптации одного интерфейса под другой. Я сначала не понял в чем проблема с аргументами.

-~{}~ 13.05.08 14:07:

В догонку

Есть класс Config, его методы (первоначальная загрузка параметров конфигурации из БД, получение значение конфигурационного параметра по ключу) используются в других методах других классов многократно (Kernel, Module).
Может быть единственный экземпляр класса Config используется многократно? Это разные вещи:

1. я создал 50 экземпляров класса Config, методы каждого из которых вызывались по 1 разу - я 50 раз использовал тип Config.

2. я создал 1 экземпляр класса Config, методы которого вызывались 50 раз - я 1 раз использовал тип Config.

Какая разница, сколько раз Вы сложите два числа, если практическая ценность типа число в программе от этого не меняется? Насколько выше ценность типа число в программе, которая использует 500 различных числовых значений, то есть использует тип число 500 раз?
 

6epcepk

Новичок
whirlwind
Большое спасибо, Вы дали мне толчек в нужном направлении. Буду изучать, практиковать, после − вопросы, если таковые будут.
 

Long

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

whirlwind

TDD infected, paranoid
Long ну, по сути, какая разница как оно называется Facade, Kernel или FrontController. Тут Kernel в нагрузку ко всему просто как транзитное хранилище звездных объектов типа БД, шаблонизатора. Эдакий гибрид реестра и фронт-контроллера. Лично я не вижу ничего особо плохого в таком подходе.
 

Long

Новичок
whirlwind при таком подходе будет создаваться куча потенциально не нужных объектов. зачем нам БД, если на странице нет никаких запросов к ней? да еще наверняка присутствует дорогой коннект к базе в конструкторе. ядро должно создавать окружение, в рамках которого работают и взаимодействуют все остальные модули системы.
 

Long

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