...

воскресенье, 18 августа 2013 г.

Улучшенное наследование в CoffeeScript

CoffeeScript принёс в JS неплохую абстракцию классов, основанную на прототипах.

Реализовав известную модель наследования и дополнив её наследованием методов касса,

он позволяет легко строить иерархии классов, даже не зная о цепочках прототипов.

Но и эта модель может быть улучшена.



Приведенный способ не сможет полностью заменить существующий,

так как он использует свойство __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.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends: 'You Say What You Like, Because They Like What You Say' - http://www.medialens.org/index.php/alerts/alert-archive/alerts-2013/731-you-say-what-you-like-because-they-like-what-you-say.html


Комментариев нет:

Отправить комментарий