parser

Написать ответ на текущее сообщение

 

 
   команды управления поиском

Недавно обсуждали подобную задачу...

Sumo 08.10.2015 13:24 / 08.10.2015 13:31

http://www.parser.ru/forum/?id=81689

Миксины очень хорошо помогают расширять функционал без множественного наследования и дублирования кода. Пишем функции и подмешиваем их в обекты, когда нам нужно. Я тут поэкспериментировал и боевая реализация получилась такой:
@CLASS
pfMixin

## Базовый класс миксина.
## Объект донор доступен через переменную this миксина.

@static:mixin[aContainer;aOptions][obj]
## aContainer[$caller.self] — если передали миксиним в объект, иначе берем self из caller'а.
## aOptions — параметры, которые передаются инициалищатору миксина
  $result[]
  $obj[^reflection:create[$CLASS_NAME;__init__;^if(def $aContainer){$aContainer}{$caller.self};$aOptions]]

@__init__[aThis;aOptions][locals]
## Инициализатор миксина. Используется вместо конструктора.
## aOptions.export[$.method1[] $.method2[]]
  $aOptions[^hash::create[$aOptions]]
  $self.this[$aThis]
  $self.mixinName[__${CLASS_NAME}__]
  $self.this.[$mixinName][$self]
  $lMethods[^if($aOptions.export){$aOptions.export}{^reflection:methods[$CLASS_NAME]}]
  $lForeach[^reflection:method[$lMethods;foreach]]
  ^lForeach[m;_]{
    ^if(!def $aOptions.export && (^m.left(1) eq "_" || $m eq "mixin")){^continue[]}
    ^if(!($this.[$m] is junction)){$this.[$m][^reflection:method[$self;$m]]}
  }

#--------------------------------------------------------------------------------------------------

@CLASS
pfHashMixin

## Добавляет объекту интерфейс хеша

@BASE
pfMixin

@__init__[aThis;aOptions]
## aOptions.includeJunctionFields.bool(false) — включать junction-поля.
  ^BASE:__init__[$aThis;$aOptions]
  $self._includeJunctionFields(^aOptions.includeJunctionFields.bool(false))

@contains[aName][locals]
## Проверяет есть ли у объекта поле с именем aName.
  $lFields[^reflection:fields[$this]]
  $result(^lFields.contains[$aName])
  ^if($result && !$_includeJunctionFields && $lFields.[$aName] is junction){
    $result(false)
  }

@foreach[aKeyName;aValueName;aCode;aSeparator][locals]
## Обходит все поля объекта.
  $lFields[^reflection:fields[$this]]
  $lForeach[^reflection:method[$lFields;foreach]]
  $result[^lForeach[lKey;lValue]{^if(!$_includeJunctionFields && $lValue is junction){^continue[]}$caller.[$aKeyName][$lKey]$caller.[$aValueName][$lValue]$aCode}[$aSeparator]]
Мне очень понравилось, что в миксинах еще и свое пространство имен существует, которое не смешивается с базовым объектом. Т.е. можно хорошо сокрытие данных делать, а доступ через функции.

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

Тестик:
@USE
../lib/common.p

@main[][locals]
  ^test_hash_mixin[]

  Finish tests.^#0A

@print_fields[aObj][locals]
  $f[^reflection:fields[$aObj]]
  ^f.foreach[k;v]{
    $k - $v.CLASS_NAME ^if($v is string){$v}
  }

@test_hash_mixin[][locals]
  Hash mixin test:
  $o[^hash_mixin_class::create[]]
#  ^print_fields[$o]
  ^o.foreach[k;v]{
    $k -> $v.CLASS_NAME
  }

@CLASS
hash_mixin_class

@BASE
pfClass

@create[]
  ^BASE:create[]
  ^pfHashMixin:mixin[$self;$.includeJunctionFields(true)]
  $field[value]