Практическое наследование (Оценка: +1)

Печать / RSS-лента
Введение
Раньше мне приходилось довольно часто сталкиваться с проблемой непонимания начинающими программистами основ такого важного механизма ООП, как наследование. Задачи, поставленные мной, часто приводили к созданию такого необычного решения, что приходилось переписывать добрую часть кода, не смотря на то, что частично решение уже было реализовано ранее. Мне удалось решить эту проблему в своем коллективе и сейчас я хочу поделиться с вами секретом "наследования".
Целью данной статьи является демонстрация использования наследования на реальных примерах. Возможно, пример окажется слишком узким, но, как мне кажется, он достаточен для внимательного читателя.

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

<?php
/*
TableName=PeopleTable
*/
class People{
/*
FieldName=firstNameField
*/
private $firstName;

/*
FieldName=nameField
*/
private $name;

/*
FieldName=midleNameField
*/
private $midleName;

/*
FieldName=dateBirthField
*/
private $dateBirth;
...

publid getFirstName(){
return $this->firstName;
}
...

}

Класс, аннотированный данным образом, использует таблицу PeopleTable для записи своих объектов, а его свойства записываются в поля firstNameField, nameField, midleNameField, dateBirthField. Другими словами, аннотация позволяет добавлять метаданные (данные описывающие данные) в код.
Данная задача была поставлена мной одному из моих программистов и дана неделя для разработки, реализации и тестирования.

Неудачное решение
Решение, предложенное моим программистом, меня несколько удивило. Оно выглядело следующим образом: был разработан интерфейс Described который позволял добавлять метаданные к любому, реализующему его классу

<?php
interface Described{
// Метод возвращает все метаданные данного элемента.
public function getAllMetadata();

// Метод возвращает значение конкретных метаданных элемента.
public function getMetadata($metadataName);

// Метод устанавливает значение метаданных.
public function setMetadata($metadataName, $metadataValue);

// Метод проверяет, существуют ли заданные метаданные в вызываемом представлении.
public function isMetadataExists($metadataName);
}

Так же были реализованы представления всех описываемых метаданными элементов класса:
- Type - представление класса;
- Property - представление свойства;
- Method - представление метода.
Все эти классы являлись подклассами класса ClassMember который реализовывал интерфейс Described, следовательно, все эти классы могли быть описаны с помощью метаданных.
Мой программист так же создал интерфейс Reflect, реализация которого позволяла получить соответствующие отображения:

<?php
interface Reflect{
// Метод возвращает представление свойства класса.
static public function &getReflectionProperty($propertyName);

// Метод возвращает представление метода класса.
static public function &getReflectionMethod($methodName);

// Метод возвращает представление класса.
static public function &getReflectionClass();

// Метод возвращает отображения всех свойств класса.
static public function getAllReflectionProperties();

// Метод возвращает отображения всех методов класса.
static public function getAllReflectionMethods();
}

Для ясности приведу пример кода, использующий аннотирование с решением, предложенным моим программистом:

<?php
// Анотируемый класс
class TestMetadata implement Reflect{
use TReflect;

private $var1;
private $var2;

public function method(){}
}

// Получаем представление переменной. Представление хранит только информацию о переменной, но не ее значение, потому инкапсуляция не нарушается.
$var1Reflect = TestMetadata::getReflectionProperty('var1');
// Добавляем аннотацию к свойству
$var1Reflect->setMetadata('FieldName', 'var1Field');
/* Теперь свойство var1 класса TestMetadata имеет аннотацию FieldName со значением var1Field, это позволит нам в будущем записать значение данного свойства в требуемое поле таблицы. Если изменится имя поля таблицы, достаточно будет изменить аннотацию */


Автор статьи: Артур (25.06.12 / 20:52)
Наследование
Рейтинг: +1
Просмотров: 834
Комментарии (1) »