Как стать автором
Обновить

Комментарии 27

при множественном наследовании я уж тем более не хочу обращаться к методам предков по порядковому номеру предка. вставил нового предка и всё непредсказуемо поехало нафиг.
Ваше замечание навело на мысль перегрузить и .ancestor. Просто проверяем первый аргумент на принадлежность классу Constr и делаем .ancestor(имя_родительского_класса, 'метод');
ancestor: function(name, level, constr){
  if(name instanceof Constr)
    name[level].apply(this, constr);
  else
  //далее по тексту
где у аргументов будет совершенно другой смысл — constr, например — массив аргументов. Зато получится самодостаточная система вызовов родителей через один метод. Может быть, это решит споры по поводу надобности? :) Я бы таким пользовался, пототму что apply в тексте программы регулярно рвёт шаблоны тем, что this не на месте, а так — нормально.
Точнее, не name[level].apply(this, constr);
а name.prototype[level].apply(this, constr);
вот поэтому Google и придумывает новый язык на замену javascript :). Все таки js, не смотря на свои очевидные преимущества, сложноват для понимания простого программера. Увы
А сторонники JS на этом месте скажут «вот потому не надо стремиться придерживаться парадигмы классов, а работать через прототипы». На что я бы ответил, что если отношения классов с наследованием в природе существуют, то их не избежать, а на чём выражать — на родных классах языка или на прототипах — дело третье.
Фак мой мозг.
Вот из-за таких извращений люди и считают JavaScript недоязыком.
Эмоции понятны, т.к. код сходу не прочитаешь, но поясните мысль: что вы находите извращением. Я предупреждал, что чистая реализация на паре функций из 1-й статьи гораздо проще для понимания, зато этот код лучше для применения. И я такое вижу во всех реализациях наследования.
а) если код плохо читается — он плох, независимо от того, насколько он полезен;
б) все проблемы наследования в js решаются парой функций augment/extend;
Вы хотите сказать, что код, оперирующий сложными понятиями — всегда плох, потому что плохо читается? Здесь написан код, оперирующий именно такими, поэтому его сходу не прочитаешь. Будучи некоторое время в теме, всё становится понятным. Это — примерно как читать код библиотек, не думая категориями библиотечных объектов, только и всего.

б) >парой функций augment/extend
Их очень по-разному иногда называют. Вы очень кратки, чтобы можно было понять, о чём речь :). У меня 3 функции, но ведь можно обойтись и одной!
> Вы хотите сказать, что код, оперирующий сложными понятиями — всегда плох, потому что плохо читается?

Код, который плохо читается — всегда плох.
Оперировать сложными понятиями можно и нужно читабельным и прозрачным образом.

> Их очень по-разному иногда называют. Вы очень кратки, чтобы можно было понять, о чём речь :)

Прототипное наследование и карринг.
… опять очень кратко. То и другое у меня есть. То и другое не способствует понятности.

Один афганец знал несколько английских слов, необходимых для общения. Но никак не мог понять, что такое «exclusive» — хорошо это или плохо?

Так и приведённые Вами термины — они приобретают разный смысл в зависимости от того, к чему относятся. Ну ладно, не буду отвлекать от работы.
Соглашусь с forgotten по первому пункту (как минимум).
При проектировании и кодировании пытаюсь пользоваться одним принципом:

Простые вещи это результат сложного анализа, а сложные — результат простого нагромождения.

Любой плохочитаемый код — сложный.
Согласен, но всё равно не буду ограничивать себя в проверке функций, которые, возможно, понадобятся, на основании того, что они пишутся сложно. Пусть для начала сложно, но отработают своё. Потом можно выкинуть или переписать. Для демонстрации показываю разные примеры и предупреждаю, что идём от простого к сложному и то, что не нравится — можно выбросить. Пока же вижу общие слова «сложно», но ни одного предложения, что выбросить. ;)
Вы можете привести пример, когда нужно обращаться к методу по порядковому номеру предка?
Наиболее реальный пример, исходя из обсуждений, видится для случая, когда мы развернули слияние классов в линию: A -> C; B -> C; превратили в A -> B -> C (потому что иначе instanceof ни в какую не поддерживается, об этом раздел в статье; то, что в B примешается A — это вопрос другой, надо как-то разруливать, не пользоваться B в других узлах). Тогда для экземпляра C -> c02 обращение к одному предку (B) будет co2.ancestor('метод'), а к другому (А) — co2.ancestor('метод', 2), без нарушения принципа Лискоу. Остальные случаи, как обсуждалось, индицируют неправильную архитектуру классов. Ну и возможность обращения не по номеру, а по имени конструктора — комментарий habrahabr.ru/blogs/javascript/130713/#comment_4333908.
Ваша увлеченность идеей сама по себе достойна уважения :)

Сам давно уже подхожу к архитектуре с принципом максимального упрощения, а именно:
1) каждый отдельно взятый объект и его связи должны быть максимально простыми — .ancestor это радикально нарушает;
2) не вводить архитектуру ради архитектуры (как признак — увлечение вставкой имен паттернов в имена объектов/типов, стремление к _максимальной_ гибкости) — здесь это стремление к множественному наследованию от типов с одинаковыми свойствами/методами;
3) если приходится изощряться — значит, что-то надо переделать; не можешь переделать — не изощряйся еще больше, маскируя сложность под простоту — станет только сложнее
4) для простоты — понимать и использовать конкретные принципы: SOLID + закон Деметры — как признаки для обнаружения слабых мест в коде

По-моему, Вы демонстрируете в целом позитивное явление — Javascript становится все более самодостаточной платформой разработки серьезных приложений. Поэтому и Javascript-разработчики проходят через «синдром архитектора» :)

Так что, хотя и в корне не согласен со статьей, признаюсь, что Ваша увлеченность нравится.
Спасибо за положительную оценку процесса :) и отрицательную — всего остального.

.ancestor появился, потому что надо было обращаться к перекрытому методу родителя и потому что намечалось множественное наследование. Возможно, что только первый уровень предка и понадобится реально, что можно было бы получить через .superclass (из исходной функции из начала 1-й статьи) или _anc, который я ей ввёл на замену.

.extend появился, потому что не нравится использовать $.extend() в таких базовых вещах. Все сложности с перегрузкой аргументов — исключительно ради него, чтобы понять в процессе работы, чем он (Constr.prototype.extend) может быть ещё полезен. Без выделения его как именованной функции код был бы раза в 1.5 меньше, но пока он ничему не мешает. Это как в тюнинге — есть возможность форсировать режим — почему бы не попробовать, а потом видно будет, нужно ли это. Для расширения прототипа — однозначно нужен.

Ну а далее — сейчас эту микробиблиотеку применяю к реальной задаче в интеграции с парой других библиотек. Ваши замечания обязательно учту, сам изо всех сил стремлюсь не заболеть архитектурным синдромом :).
Не стоит использовать arguments.callee, мне кажется.
Да, об этом в курсе. А почему?
зависимости нужно передавать через стандартные интерфейсы — например, через параметры метода — их должен обеспечивать вызывающий объект. arguments.callee() это нарушает: вызывающий объект не знает, что к нему могут обратиться и что-то от него ожидать. и всегда можно избежать использования callee()
ой, что-то я перепутал с Function.caller() — рассуждения про интерфейсы можно применить к нему, а вместо callee же можно использовать просто именованные функции.

кстати, интересна рекомендация даже анонимным функциям давать имена — в некоторых случаях облегчает отладку, т.к. вместо в стеке отображается понятное наименование.
На stackoverflow есть довольно подробный ответ.
почитал собственно код:

1. Возможно, я просто не умею понимать такой код, но вроде это не прототипное наследование: прототип базового объекта никак не встраивается в цепочку прототипов наследника, и если изменить прототип базового класса, поведение наследника не изменится

2. Как бы я решал, использовать такой код или нет: поскольку это — «системный», а не прикладной функционал, в нем нужно быть уверенным гораздо сильнее — его баги будут гораздо сложнее. Поэтому: 1) код должен быть понятным, максимально стройным и устойчивым, в т.ч. покрытым тестами; 2) решаемая проблема должна оправдывать вносимую сложность. Код, на мой взгляд, сложен, но это отдельная тема. Самое главное — решаемая проблема для меня не оправдывает увеличение сложности в виде такого кода.
1. Когда я отлаживал этот код для множественного наследования, я превратил прототипное наследование в чисто классическое такими строчками. Вместо
Inherited.prototype = new this(contextArg0);
написал расширение прототипа через встроенную функцию:
if(Inherited.prototype){
	var tmp = Inherited.prototype;
	Inherited.prototype = new this(contextArg0);
	f2.extend(Inherited.prototype, tmp);
}else
	Inherited.prototype = new this(contextArg0);

Она и превращает прототипное в классическое тем, что копирует прототипы на каждом уровне. В результате, множественное всё равно неполноценно, а прототипное — исчезло (в остальном, внешне ничего не изменилось). Видимо, надо будет откатить обратно эти 6 строчек до одной.

2. Оттого, что скрипт системный, я его тестирую в разных случаях, для этого покрыт тестами, которые сейчас на странице примера, но надо ещё — например, в тестах нет пропусков определений; я их тестировал отдельно, работают. Самую главную обкатку хочу провести на паре рабочих задач и надеюсь, что обсуждение при публикации типа этого — поможет.

Решаемую проблему я вижу не в одном доступе к предкам. Мне нужно наследование, поддерживающее выполнение конструкторов классов (и прототипное там, где возможно). Сейчас этот код его делает (код из первой статьи — нет).
Увы и ах, кроме прикладного интереса, код не придставляет практической пользы.
1) Не достаточно понятен. И сложен в поддержке.
2) При первой возможности будет переписан в тот момент, когда Вы покинете фирмую
Мне хватает классов от Джона Ресига, или base2, или Ext.JS, или Backbone.JS.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации