Как стать автором
Обновить

Не статичные статические члены класса

Здравствуйте. Вы думаете, что в заголовке автор решил поумничать? Пошутить? Покаламбурить? Отнюдь. Просто на днях ему пришлось решать одну задачу, в которой он (т.е. я) столкнулся с одним, как оказалось, не совсем удобным свойством статических членов классов. Собственно речь идет о многих языках програмирования, но т.к. проблема возникла при написании приложения на языке php, то и все дальнейшие рассуждения будут приводиться применительно к нему, но всё изложенное можно так же транспонировать и на другие языки програмирования. Итак…

Сижу я вечером дома. Пишу небольшое приложение тесно связанное с базами данных.

Приложение, как я написал, небольшое, но с довольно серьезной бизнес-логикой и сложными данными. Естественное мое решение — использовать ORM для работы с mysql. И, так-как время позволяло, решил воплотить в жизнь давно вынашиваемую идею-создать собствееное очень маленькое и легкое ORM (в результате автор изменил задачу и написал просто надстройку над redbeansPHP, в которой использовал излагаемые ниже соображерия).

Первое, что нужно сделать в любой ORM — сопоставить класс таблице базы данных. Конечно, самое подходящее место для этого-статическое свойство класса. Почему именно статическое? А потому, что все объекты данного класса используют значение этого свойства.

Вроде бы все логично, но не так все просто. Представьте ситуацию, когда объекты наследуются от одного и того же предка (в данном случае в этом предке реализованы общие методы для работы с БД в выражениях создаваемой ORM).

Пусть общий предок всех классов class CProtoObject.

class CProtoObject{
   protected static $_tableName;
   .......
  public static function setClassTable($p_tableName){
   self::$_tableName=$p_tableName;
 }
}

И есть два его потомка: class CUser extends CProtoObject и CFlat extends CProtoObject, которые хранят данные в таблицах tbl_user и tbl_flat, соответственно.

Чтобы задать имена таблиц используем вызов статического метода setClassTable:

CUser::setClassTable("tbl_user");
CFlat::setClassTable("tbl_flat");

Некоторые читатели уже увидели проблему: Оба класса обращаются к одной и той же переменной, описанной в их общем предке CProtoObject. Т.е. объекты обоих классов будут записывать свои данные в tbl_flat, т.к. эта таблица указана для свойства _tableName последней. Суть прблемы: бывают случаи, когда значения статического свойства предка и наследника или двух наследников должны быть разными.

CUser::$_tableName!=CFlat::$_tableName!=CProtoObject::$_tableName

Для решения этой проблемы я пошел следующим путем: создал статическую переменную типа массив в классе предке для всех наших классов CProtoObject:

protected static $_staticValues=array();

И статический метод:

public static setTable($p_tableName){
   static::$_staticValues[static::class]=$p_tableName;
}

Тогда, для сохранения имен таблиц для каждого класса достаточно вызвать этот метод:

CUser::setTable("tbl_user");
CFlat::setTable("tbl_flat");

А вот метод получения имени таблицы для класса:

public static function getTableOfClass(){		return static::$_staticValues[static::class];	
}

Вот и все. Не правда ли, что вышеизложенное немного напоминает работу компилятора при составлении деревьев виртуальных функций? В заключении уточню: для хранения нескольких статических переменных можно использовать в качестве параметра метода setTableName любой агрегатный тип, например, массив. Все вышеизложенные соображения я воплотил в одном классе CStaticPropValues (файл CStaticPropValues.php). Чтобы использовать вышеописааный функционал, объект данного класса достаточно добавить в любой Ваш класс. Пример использования можно увидеть в файле index.php.

Оба файла можно найти на github
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.