Свойства в стиле C#

[sid]

Новичок
Свойства в стиле C#

Еще одна идея использования средств Overloading PHP5. Реализация перегружаемых свойств (как в C#)
Не буду приводить сам класс, реализующий перегруз. Я думаю это и не надо. Там все элементарно.

Пример использования (Извините, пишу по памяти в блокноте, забыл исходники дома, но я думаю вы смысл поймете)
PHP:
class test extends property
{
    protected $propertyName = 'Fred';
    protected $propertyAge = 35;

    public function __construct()
    {
        $this->registerProperty('name');
        $this->registerProperty('age');
    }

    public function getName()
    {
        return $this->propertyName;
    }

    public function setAge($value)
    {
        if ( !is_integer($value) || $value <= 0 )
        {
            thrown new Exception("Invaid age");
        }else{
            return $this->propertyAge = $value;
        }
    }

    public function getAge()
    {
        return $this->propertyAge;
    }
}

$test = new test();

echo "Name is: {$test->name}\n";
echo "Age is: {$test->age}\n";

// Name is: Fred
// Age is: 35

// Ошибка. Свойства name только для чтения
$test->name = "Eric";

// Ошибка. Неверный возраст
$test->age = "five"

// Ок
$test->age = 13;

// Name is: Fred
// Age is: 13
 

lucas

Guest
1. Оригинальная, я бы даже сказал новаторская идея... :)

2. Важный недостаток: бессмысленно изменяется/"засоряется" дерево наследования. Лучше использовать аггрегацию.
 

[sid]

Новичок
lucas
С "засорением" согласен, не без этого :). Однако если использовать аггрегацию/делегирование придется переписывать __call/__get/__set методы в каждом дочернем классе, а это, как мне кажется, еще более сильный недостаток. Как вариант можно использовать предложенное мною ранее решение http://phpclub.ru/talk/showthread.php?s=&threadid=68821&rand=7.
 

demongloom

Новичок
почему можно в той функции __call/get/set обращаться к родительской функции, с передачей параметров.
 

[sid]

Новичок
Вы имели ввиду не к родительской, а к делегированной?!Конечно можно, но это как раз и будет переписывание __call/__get/__set в каждом классе (да там всего несколько строчек, но все-таки). По моему, если нет стесняющих обстоятельств, лучше использовать наследование, но никто не мешает использовать делегирование! Вобщем я предложил идею, реализацию можете заточить под свои нужды!

-~{}~ 07.07.05 12:47:

Ну что ж, property - заезд второй :). Учитывая невозможность наследования от property и еще нескольких факторов можно предложить следующее дополненное/измененное решение.
PHP:
class property
{
    private $properties = array();
    
    public function __construct(){}
    
    public function registerProperty($propertyName, $defaultValue, $validator = NULL)
    {
        if ( isset($this->properties[$propertyName]) )
        {
            throw new Exception("Property {$propertyName} already registered");
            return false;
        }else{
            if ( !$validator instanceof IValidator )
            {
                $validator = NULL;
            }
            $this->properties[$propertyName] = array(
                'value' => $defaultValue,
                'validator' => $validator
            );
            return true;
        }
    }
    
    public function __set($propertyName, $propertyValue)
    {
        if ( !isset($this->properties[$propertyName]) )
        {
            throw new Exception("Invalid property {$propertyName}");
            return false;
        }elseif ( !$this->properties[$propertyName]['validator'] instanceof IValidator )
        {
            throw new Exception("Property {$propertyName} is readonly");
            return false;
        }else{
            if ( !$this->properties[$propertyName]['validator']->isValid($propertyValue) )
            {
                throw new Exception("Invalid value for property {$propertyName}");
            }else{
                $this->properties[$propertyName]['value'] = $propertyValue;
            }
        }
    }
    
    public function __get($propertyName)
    {
        if ( !isset($this->properties[$propertyName]) )
        {
            throw new Exception("Invalid property {$propertyName}");
            return false;
        }else{
            return $this->properties[$propertyName]['value'];
        }
    }
}
Имеется интерфейса валидатора (взят для примера, можно усложнить конечно)
PHP:
interface IValidator
{
    public function isValid($request);
}
И валидатор возраста
PHP:
class ageValidator implements IValidator
{
    public function isValid($request)
    {
        // Проверка возраста
        return  is_integer($request) &&
                $request > 0 &&
                $request < 150; // Чисто импирическое предположение :)
    }
}
И собственно говря сам класс использующий property
PHP:
class test
{
    private $property;
    public function __construct()
    {
        $this->property = new property();
        $this->property->registerProperty('name', 'Bob');
        $this->property->registerProperty('age', 15, new ageValidator());
    }
    
    function __set($name, $value)
    {
        return $this->property->__set($name, $value);
    }
    
    function __get($name)
    {
        return $this->property->__get($name);
    }
}
Использование при это никак не отличается
PHP:
$test = new test();
echo "Name is: {$test->name}\n";
echo "Age is: {$test->age}\n";

// Ошибка. Свойство name доступно только для записи
$test->name  = "a";

// Ошибка. Данное свойство может принимать
// только целое значение больше 0 и меньше 150
$test->age = "five";

// Ok
$test->age = 5;

echo "Name is: {$test->name}\n";
echo "Age is: {$test->age}\n";
Выводы:

Плюсы:
Не засоряем дерево наследования;
Удобный механизм валидации значений при помощи валидаторов и поддержание целостности данных в классе;

Минусы:
Пока что объект использующий данное решение не может написать свой обработчик __set/__get (это, в принципе, легко поправимо, но я не стал пока за это браться).
 
Сверху