Реализовав известную модель наследования и дополнив её наследованием методов касса,
он позволяет легко строить иерархии классов, даже не зная о цепочках прототипов.
Но и эта модель может быть улучшена.
Приведенный способ не сможет полностью заменить существующий,
так как он использует свойство
__proto__
, недоступное в некоторых реализациях JS.Но он позволяет значительно расширить возможности наследования, работая при этом
поверх основной модели.
Кроме создания цепочки прототипов конструктров кофе использует следующий код
для наследования свойств класса:
for key of parent child[key] = parent[key] if __hasProp_.call(parent, key)
То есть все свойства просто копируются. При таком наследовании теряется гибкость.
Простейший пример — при изменении метода предка не меняются методы в
наследованных классах. Также не наследуются неперечисляемые свойства.
Было бы гораздо лучше, если свойства класса тоже наследовались по цепочке
прототипов. Всё что нужно — после наследования класса средствами кофе удалить
всё унаследованное 🙂 и установить child.__proto__ = parent
.
При таком наследовании у дочернего класса будут доступны все свойства предка,
которые так же можно переопределить. Но появляется возможность реализовывать
интересную функциональность, основанную на том что свойства принадлежат
прототипу, а не самому объекту.
Один из примеров — переменная экземпляра класса (class instance variable).
Object.defineProperty Parent, 'test', get: -> @_test if @hasOwnProperty '_test' set: (val) -> @_test = val Parent.test = 1 Parent.test # => 1 Child.test # => undefined Child.test = 2 Parent.test # => 1 Child.test # => 2
Этот подход к наследованию лежит в основе пакета coffee_classkit.
В этом пакете также реализованы методы работы с классами, взятые из Ruby:
include
, использующий append_features
, extend
, использующий extend_object
,
хуки inherited, included, extended
. Не стану здесь описывать их подробно:
они идиентичны аналогам из руби, только названия в кэмлкейсе.
Кто не не знаком с Ruby, надеюсь, без труда всё поймёт по исходнику,
тем более, что методы не больше шести строк.
Вся функциональность доступна с использованием обычного синтаксиса объявления класса:
classkit = require 'coffee_classkit' class Child extends Parent classkit.extendsWithProto @ classkit.include @, Mixin
Для удобвства в пакете есть класс, имеющий все эти методы в своем составе.
Унаследовав от него класс, можно использовать их в более явной и привычной форме:
class Example extends classkit.Module @extendsWithProto() @include Mixin
Также в пакет включен аналог ActiveSupport::Concern
:
class Mixin extends classkit.Module @extendsWithProto().concern() @includedBlock: -> # выполняется в контексте базового класса @instanceVariable 'test' class @ClassMethods someClassMethod: -> someInstanceMethod: -> class Base extends classkit.Module @include Mixin @someClassMethod() (new Base).someInstanceMethod()
Больше простых примеров можно найти в тестах в репозитории.
С использованием описанных подходов, становится возможным писать модульный
объектно-ориентированный код, не врываясь в глобальное пространство имён.
Развёрнутый пример можно посмотреть в набросках проекта,
написанного с использованием CoffeeClasskit.
ссылка на оригинал статьи http://habrahabr.ru/post/190494/
Добавить комментарий