Проблема с дизайном кода

[DAN]

Старожил PHPClub
Проблема с дизайном кода

Всем привет, sorry, что пишу сюда.

Возникла проблема с дизайном кода, надеюсь на вашу помощь, особенно на помощь инфицированных TDD ))

Задача следующая. Есть базовый класс, который несет в себе часть общего функционала, и выступает также в роли интерфейса.
Есть производные классы, расширяющие функционал базового. Код примерно такой:
PHP:
class BaseClass
{
	var	$dbh;

	var $propOne;
	var $propTwo;
	
	function BaseClass( &$dbh ){
		//set database handler here 
	}
	
	function & getProperties(){
		$array[1] = $this->propOne;
		$array[2] = $this->propTwo;
		
		return $array;
	}
	
	function save(){
		$props	=& $this->getProperties();
		//	save data using PEAR::DB autoExecute method.
	}
}

class ExtClass extends BaseClass 
{
	var $propThree;

	function & getProperties(){
		$array	  =& parent::getProperties();
		$array[3] = $this->propThree;
		
		return $array;
	}
	
	function save(){
		parent::save();
		$props	=& $this->getProperties();
		//	save data using PEAR::DB autoExecute method.
	}
}
Проблема в следующем. При вызове ExtClass::save() вызывается явно BaseClass::save(), в котором также явно вызывается getProperties(). Но по правилам полиморфизма в BaseClass идет вызов ExtClass::getPropterties(), что есс-но возвращает массив с тремя элементами вместо необходимых двух. От этого валится работа DB::autoExecute(), так как появляются лишние для базового класса BaseClass данные.

Так вот, как бы мне разделить методы getProperties(), чтоб для базового класса возвращались именно его свойства.
Напомню, что базовый класс также несет в себе интерфейс.

Буду рад услышать предложения по редизайну, а может и другому решению.
Спасибо.
 

pachanga

Новичок
Можешь, пожалуйста все задачу целиком пояснить?

Меня вот сразу насторожил факт присутствия обращения к БД в классах, которые по-моему являются просто контейнерами данных.
 

[DAN]

Старожил PHPClub
Есть несколько сущностей, которые имеют некий общий функционал (зашитый в базовом классе - БК) и свой специфический. Есть фабрика, порождающая эти сущности (поэтому важен интерфейс).
Каждая из сущностей описывается конкретным производным классом (ПК). Можно сказать, ПК выступает в роли модели или даже ORM-класса.
Так вот, хотелось бы сделать так, чтоб можно было безболезненно плодить эти ПК, не затрагивая функционала системы, а лишь править фабрику.
Соответственно, все запросы к БД, добавляющие/изменяющие данные конкретной сщности, я посчитал нужным инкапсулировать в ПК. Да, при изменении свойств какого-либо ПК, также могут меняться свойства БК, которые тоже нужно сохранять в БД.
 

pachanga

Новичок
У тебя там случаем не имплементация ActiveRecord получается?

-~{}~ 04.10.05 13:02:

Почему бы тебе на самый простой случай не сделать пока так:

PHP:
function save()
{
  $this-> _saveProps($this->getProperties());
}

function _saveProps($props)
{
....
}
-~{}~ 04.10.05 13:09:

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

tony2001

TeaM PHPClub
объявить все статические методы - static.
это не даст тебе использовать $this и писать такой код.

P.S. и убери ты эти && везде, ну правда.
 

Screjet

Новичок
Так вот, как бы мне разделить методы getProperties(), чтоб для базового класса возвращались именно его свойства.
Напомню, что базовый класс также несет в себе интерфейс.
В ПХП5 этот метод, в базовом классе, сделал бы приватным. В ПХП4 сделал бы, как pachanga предложил, добавил бы новый метод.
 

pachanga

Новичок
Честно говоря, если речь идет об ActiveRecord, то можно было бы обратить внимание на Propel, PHP Cake в качестве источника вдохновения :)
 

[DAN]

Старожил PHPClub
pachanga, почти что ActiveRecord. Базовый класс помимо plain-данных еще агрегирует парочку объектов.
С методом save() проблем нет. Проблема именно в том, что при вызове getProperties() из BaseClass::save() вызывается метод ExtClass::getProperties()
С метаанными связываться неблагодарное занятие, тогда уж проще на каждую сущность отдельный класс плодить. Хочется общую функциональность вынести в базовый класс, при этом сохранив интерфейс БК в ПК.

tony2001, нету там статических методов.
 

pachanga

Новичок
Автор оригинала: [DAN]
почти что ActiveRecord. Базовый класс помимо plain-данных еще агрегирует парочку объектов.
Ты не думаешь, что это несколько раздувает его? Может выделить отдельный классы на дополнительные ответственности(responsibility)?

С метаанными связываться неблагодарное занятие
Ну почему же, для начала можно попробовать добавлять некоторый префикс ко всем аттрибутам, которые предназначаются к сохранению в БД и, получая get_object_vars анализировать, что сохранять, а что нет. Таким образом тебе хотя бы не придется переписывать getProperties каждый раз.
 

Screjet

Новичок
Хочется общую функциональность вынести в базовый класс, при этом сохранив интерфейс БК в ПК.
имхо, нереально. БК не может сохранять собственное состояние и поведение. Только ПК может это.
 

tony2001

TeaM PHPClub
>tony2001, нету там статических методов.
тогда зачем ты вызываешь их статически?
Код:
parent::save();
$array      =& parent::getProperties();
 

SiMM

Новичок
Если выкинуть всё лишнее, то
PHP:
class BaseClass {
    function getProperties(){
      $this->var = 1;
    }

    function save(){
        BaseClass::getProperties(); // вместо $this->getProperties();
    }
}

class ExtClass extends BaseClass {
    function getProperties(){
        $this->var = 2;
    }

    function save(){
        parent::save();
    }
}

$cl = new ExtClass;
$cl->save();
echo $cl->var;
В PHP 4 - канает. В 5ке - не пробовал.
PS: если я вообще что нибудь понял.
 

[DAN]

Старожил PHPClub
SiMM, оно самое, спасибо. Немного неловко, что приходится явно указывать имя базового класса.
Автор оригинала: pachanga
Ты не думаешь, что это несколько раздувает его? Может выделить отдельный классы на дополнительные ответственности(responsibility)?
а можно поподробнее?

-~{}~ 04.10.05 13:51:

> имхо, нереально. БК не может сохранять собственное состояние и поведение. Только ПК может это.
В С++ приватные методы весьма неплохо позволяют инкапулировать функционал БК.
 

pachanga

Новичок
tony

ты о чем???

function save()
{
...
parent :: save();
}

это не статический вызов, это единственный способ обратится к интерфейсу родителя в данном случае.

-~{}~ 04.10.05 13:58:

Автор оригинала: [DAN]
а можно поподробнее?
Для этого мне надо знать все технические детали.

Ну а как насчет предложенной схемы с get_object_vars?
 

[DAN]

Старожил PHPClub
Screjet, как только в yast`e появится, так сразу :)
pachanga, get_object_vars рулят!
поподробнее выглядит так:
PHP:
	function & getProperties()
	{
		$properties	= get_object_vars($this);

		unset($properties['dbh']);
		unset($properties['table']);
		unset($properties['mappings']);
		
		return $properties;
	}
Грубо говоря, класс картирует в свои свойства строку из БД по заданному ID. Также в классе я описываю методы для вставки/сохранения/удаления записи в БД.
Ну и немного привнес в класс бизнесс-логики, как то проверка целостности данных, ссылочная связанность.

Я и сам рад бы разнести всё это дело (прорефакторить), но в основном коде тогда приходится порождать кучу мелких классов.
 

pachanga

Новичок
а как насчет такого:

PHP:
function & getProperties()
{
        $properties = get_object_vars($this);
        $filtered = array();

        foreach(array_keys($properties) as $index)
        {
           if($this->_isPersistableProperty($index))
            $filtered[$index] =& $properties[$index];
         }

        return $filtered;    
}

function _isPersistableProperty($name)
{
        return strpos('p_', $name) === 0;
}
-~{}~ 04.10.05 14:28:

Кстати, ты против использования метаинформации, но что за аттрибут mappings?
 

[DAN]

Старожил PHPClub
mappings - это соответствие имени атрибута класса и имени колонки в таблице БД.

Хм... у меня всего 3 атрибута, которые я эскейплю при формировании запроса к БД, поэтому не вижу смысла в их префиксировании.

А в чем, по-твоему, наблюдается раздутие класса?
 

pachanga

Новичок
Автор оригинала: [DAN]
mappings - это соответствие имени атрибута класса и имени колонки в таблице БД.
Это и есть метаинформация в простейшем виде :)

Хм... у меня всего 3 атрибута, которые я эскейплю при формировании запроса к БД, поэтому не вижу смысла в их префиксировании.
Просто у тебя в потомках могут появиться различные аттрибуты, которые бы ты не хотел отдавать на сохранение в БД, ведь так? Введя префикс для сохраняемых полей, тебе не потребуется переопределять getProperties() всякий раз.

Ну а если уж у тебя есть mappings, то почему бы не анализировать их и не фильтровать только требуемые аттрибуты? Тогда и префикс не понадобится.

А в чем, по-твоему, наблюдается раздутие класса?
Если это чистой воды ActiveRecord - то все замечательно, если же там у тебя завязана куча дополнительного функционала, то я все же постарался бы разнести ее по отдельным сущностям. Но опять же, я не вижу картины целиком, поэтом сказать точно не могу. Может, и нет там раздутия ;)
 
Сверху