Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   final или не final (http://www.flasher.ru/forum/showthread.php?t=155656)

Lyso 07.05.2011 19:45

final или не final
 
Использовать ли final при объявлении чего-либо. В одних источниках я читал, что это загружает код, в других - что делает его быстрее, так как его нельзя продолжить. Стоит ли его использовать постоянно? А не только тогда, когда необходимы специальные классы и функции, которые нельзя наследовать.

NikolyA 07.05.2011 19:50

я бы предпочел вообще обходится без final, так работать интересней

Lyso 07.05.2011 19:51

Иногда необходимо, чтобы классы нельзя было расширить. Так часто делает и сама Adobe почти со всеми классами.

expl 07.05.2011 19:56

Можно поприменять при использовании "Шаблонного метода", чтобы не спутать какие методы предназначены для перегрузки а какие нет.

А целый класс финалить, чтобы нельзя было расширить - зачем это может понадобиться?
Только если вы хотите намекнуть коллегам:
"Не ребят, даже не пытайтесь, этот класс безнадежен, он чудом вообще работает и если вы хотите на нём что-то построить - то это будет генератор багов"

А, более адекватная причина:
Класс A используется в классе B хитрым образом с завязкой на реализацию именно этого класса А
и если в класс B вы попытаетесь подставить наследника класса А - он развалится нафиг. Так что не пытайтесь.

Короче, final-изация класса применяется, когда есть проблемы с дизайном.

Цитата:

что делает его быстрее
Что-то у меня не получилось засечь разницу между скоростью обращения к финализированному и нефинализированному методу
Может потому, что тестировал на дебажной версии флешплеера (сборка swf-ки была релизная)
Целиком финализированный класс не тестил.

honest_man 07.05.2011 20:12

Lyso, это вы автор топика This или не this?
Вопрос риторический...

Знаете, не в обиду конечно, но складывается впечатление что вы там где-то открываете для себя новый оператор, ранее незнакомый, и решаете поспрашивать о нем на форуме.

Так вот, ТАМ где вы узнали про этот оператор должно было рассказываться для каких целей он применяется. Если хотите чтобы наследовался - не пишите final, хотите обратного - пишите. Или вы пытаетесь найти в операторе final подвох или спасение? Там нет ни того ни другого, просто конкретная функциональность.

P.s. Adobe НИКОГДА без причины не закрывают возможность наследования того или иного предопределенного класса.

Psycho Tiger 07.05.2011 20:49

Цитата:

Так часто делает и сама Adobe почти со всеми классами.
Вообще-то с единичными.

Например, представьте себе следующую ситуацию: некоторый класс A является DataProvider`ом для класса B. Класс B честно ждёт загрузки данных (события Event.COMPLETE), после чего мирно начинает работать. Но злые гномы подсунули вместо класса A его наследника, ExtendedA, который шмаляет этот самый Event.COMPLETE каждую секунду. Мирного экземпляра класса B увозят в психушку. Занавес.

Примерно для таких ситуаций и нужен final. Но я сторонник того, что бензопила круче лобзика, и то что к первой надо читать инструкцию и думать меня не останавливает. Поэтому final я не пишу никогда.

P.S. final на скорость никак не влияет. Это даже наивно думать, что компилятор делает какие-то "преобразования", закрывая "отросток" (таблицу виртуальных функций, видимо), который позволяет получить прирост в скорости. Даже если бы такое закрытие существовало - компилятор бы находил сам финальных наследников и перед компиляцией помечал бы их как final. final — это самозавязка рук, и не более.

wvxvw 07.05.2011 20:51

Типичая ситуация, когда нужен final - тогда же, когда бы вы, например, использовали константу. Объекту очень редко нужны такие методы (мне ни разу не понадобились), как и не-статические константы (такими, как ни странно, часто пользуюсь, но в целом это не общепринятая практика). А статические / функции объявленные вне класса и так по факту final.

Я объявляю как final приватные классы (т.е. классы объявленные в том же файле, но вне пакета) не чтобы запретить наследование, а чтобы констатировать факт, что наследовать их уже никак не получится (для документации).

Crazy 07.05.2011 20:58

Цитата:

Сообщение от Lyso (Сообщение 994643)
Использовать ли final при объявлении чего-либо

Случай "я написал *****код и помечу его final, чтобы не было еще хуже" рассматривать не будем -- оно само отсыхает со временем.

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

Если же в одиночку за неделю пишется проект на два десятка классов, то словом "final" можно не заморачиваться.

expl 07.05.2011 23:41

Классная парадигма, однако. Т.е. запрещаем цепочки наследования больше 1-го яруса на уровне соглашения по кодированию :) Но что-то глядя на flex-фреймворк кажется что такое невозможно. Да и в Qt говорят длиннющие цепочки наследования.
Неужели заставить кодеров не выбиваться за 1 ярус наследования реально?

Цитата:

P.S. final на скорость никак не влияет. Это даже наивно думать, что компилятор делает какие-то "преобразования", закрывая "отросток" (таблицу виртуальных функций, видимо), который позволяет получить прирост в скорости. Даже если бы такое закрытие существовало - компилятор бы находил сам финальных наследников и перед компиляцией помечал бы их как final. final — это самозавязка рук, и не более.
Я бы так уверен не был, наш FlexSDK-компилятор даже выражение
Код AS3:

var a:int = 2 + 3 * 10;

_не_ шибко старается привести к
Код AS3:

var a:int = 32;

http://gskinner.com/talks/quick/#43

да и такие оптимизации в идеале должен компилятор делать - у него для этого достаточно информации:
http://gskinner.com/talks/quick/#47

carrotoff 07.05.2011 23:45

Цитата:

Есть парадигма проектирования (запамятовал название), согласно которой любой класс должен быть либо абстрактным, либо финальным
Хм, не слышал. А в чем смысл такого подхода?

wvxvw 07.05.2011 23:46

Можно вообще все только статическими методами написать :)
EDIT: а, не, не получится стейдж получить, а так бы можно было :)

honest_man 08.05.2011 00:11

Конечно можно, а еще можно просто на джаваСкрипте фигачить, чего уж там, нам ооп вообще ненужно! =)

expl 08.05.2011 00:29

Вот так вот, java-script уже не ООП-язык

honest_man 08.05.2011 00:34

expl, ну что вы. Просто я осветил стадии деградации. Сначала переходим на жава скрипт, а потом и вовсе бросаем ооп.

i.o. 08.05.2011 00:35

Цитата:

Сначала переходим на экшн скрипт, а потом и вовсе бросаем ооп.
эээм... забываетесь, сэр-с? :)

Psycho Tiger 08.05.2011 00:39

Цитата:

Я бы так уверен не был, наш FlexSDK-компилятор даже выражение
Я знаю. Я просто как бы намекаю, что во всех вселенных это не даст прироста к скорости)

honest_man 08.05.2011 00:39

*** honest_man в страхе быть забитым любителями JS. =)

Цитата:

Сообщение от i.o. (Сообщение 994706)
эээм... забываетесь, сэр-с? :)

К стати да, попутал рамсы... Но вовремя вспомнил на каком форуме нахожусь и откорректировался! ;))

Crazy 08.05.2011 00:41

Цитата:

Сообщение от expl (Сообщение 994694)
Классная парадигма, однако. Т.е. запрещаем цепочки наследования больше 1-го яруса на уровне соглашения по кодированию :)

Hint: никто не запрещает наследовать один абстрактный класс от другого абстрактного класса.

Добавлено через 11 минут
Цитата:

Сообщение от carrotoff (Сообщение 994697)
А в чем смысл такого подхода?

Смысл такого подхода в отказе от бесконтрольного наследования. Де-факто человек, проектирующий обычный класс, как правило не задается вопросом "что будет, если от этого класса будут наследоваться". Пример: программист A пишет класс C1 для своих текущих целей. Через неделю программист B пишет класс C2, расширяющий C1 и переопределяющий пару методов. Спустя полгода программист A переделывает класс C1, вследствие чего в поведении C2 начинают проявляться жестокие глюки. Поскольку при переделке C1 автору и в голову не придет найти все производные классы и проверить, как на них повлияет правка. Просто жизненный факт.

Разделение abstract-final позволяет это упорядочить. Если пишется abstract-класс -- автор заранее пишет его под наследование. Соответственно, при внесении изменений в абстрактный класс нельзя тупо забыть, что от него наследуются.

P.S. Вообще, следует понимать, что наследование реализации -- довольно опасная операция.

honest_man 08.05.2011 01:05

Цитата:

Сообщение от Crazy (Сообщение 994710)
P.S. Вообще, следует понимать, что наследование реализации -- довольно опасная операция.

Наследование реализаций - это частые и вынужденные меры. Зачем повторять код? Если программисты A и B сидят на одной трубе, не стоит им в тихую (по шухерски :)) наследовать классы друг друга.

Как по мне, в данной ситуации, это рефакторинг - опасная операция. А может просто программист А поссорился с программистом В?

expl 08.05.2011 01:32

Цитата:

Hint: никто не запрещает наследовать один абстрактный класс от другого абстрактного класса.
А, ну тогда здесь нет ничего сверъестественного.
Когда надо получить класс с похожим функционалом обычно от него никто не наследуется, а выносят базовый класс для обоих. Так просто проще.
А тут их просто еще финалят для верности.

Цитата:

Наследование реализаций - это частые и вынужденные меры. Зачем повторять код? Если программисты A и B сидят на одной трубе, не стоит им в тихую (по шухерски ) наследовать классы друг друга.
При выносе общего класса кода может быть даже меньше из-за того, что не потребуется перегружать в коде B специфичные для А методы и мусора в коде B будет меньше.
Цитата:

Как по мне, в данной ситуации, это рефакторинг - опасная операция.
Рефакторинг всегда опасная операция, а НЕрефакторинг - всегда рост дифектов в коде.
Все зависит от количества кода, использующего класс А, который собираешься рефакторить (будет печально, если ты отломал что-то в общей
либе и упало приложение, разрабатываемое вообще другой коммандой)
и от количества тестов для этого класса А, которыми сможешь проверить что ничего не отломал.
А если этот класс А использует 2-10 классов только в этом приложении, которые можно быстро протестить - грешно не отрефакторить
Цитата:

А может просто программист А поссорился с программистом В?
А что это меняет?

honest_man 08.05.2011 01:55

Цитата:

Сообщение от expl (Сообщение 994723)
А что это меняет?

Как это что. Программист А специально реорганизовал класс, чтоб программисту В был кукиш. =) Пусть с нуля создает, а не спиногрызит тут.

Хотя с другой стороны программист А тоже хорош, пусть не мешает личное с делами...

Уволить обоих! И нанять программистов C & D!

wvxvw 08.05.2011 02:08

Цитата:

Сообщение от Crazy (Сообщение 994710)
Де-факто человек, проектирующий обычный класс, как правило не задается вопросом "что будет, если от этого класса будут наследоваться".

Такие люди хорошо лечатся написанием юнит тестов. Вообще, у юнит тестов оказалось много позитивных эффектов не связаных напрямую с тестированием. Так, например, удалось донести до молодого сотрудника вредность синглтонов. Ну и тот факт, что для написания юнит теста очень часто нужно наследоваться от тестируемого класса просто заставляет писать так, чтобы наследование работало, ну, или по крайней мере заставляет задуматься о такой возможности.

Есть еще такой момент - я, (все еще) почему-то предполагаю, что человека, который будет использовать мой код возможно заинтересует реализация, и при наследовании он не будет делать каких-то явных глупостей. Увы, так не работает. И если необходимо получить результат не взирая на "идиотов" сотрудников, которым жалко лишний раз посмотреть чужой исходник... да, иногда наверное не помешает и final...

Crazy 08.05.2011 02:55

Цитата:

Сообщение от wvxvw (Сообщение 994728)
Такие люди хорошо лечатся написанием юнит тестов.

К сожалению, это помогает не всегда. Качественные юнит-тесты встречаются реже, чем многие думают. Думаю, не ошибусь, если предположу, что три четверти здешнего контингента их вообще не пишет.

Где-то с год назад мне довелось поработать с кодом большого сторонника модульных тестов. Покрытие -- свыше 95%. При этом "работа над ошибками" показала, что в оставшиеся 5% аккуратно вошли разные аномальные состояния. Тесты -- работают. А если вдруг из стороннего модуля исключение прилетит -- вся система в алмазный дым обращается. А юнит-тестов при этом -- процентов на 20 больше, чем "полезного" кода.

Цитата:

для написания юнит теста очень часто нужно наследоваться от тестируемого класса
Поступаю так временами. Во всех случаях -- в лично моей практике -- это оказывается следствием огрехов проектирования.

Цитата:

Есть еще такой момент - я, (все еще) почему-то предполагаю, что человека, который будет использовать мой код возможно заинтересует реализация, и при наследовании он не будет делать каких-то явных глупостей. Увы, так не работает. И если необходимо получить результат не взирая на "идиотов" сотрудников, которым жалко лишний раз посмотреть чужой исходник... да, иногда наверное не помешает и final...
Здесь, как мне кажется, спутано теплое с мягким. Если некто наследует от чужого класса и тщательно изучает при этом его код -- это никак не страхует от правок, позднее внесенных автором родительского класса. А автор родительского класса, если специально не предназначал свой класс для наследования, на практике редко находит время для инспекции всех внезапных потомков его класса. И идиоты здесь совершенно параллельны.

wvxvw 08.05.2011 11:23

Да, я о другом :)
Вот, непридуманная ситуация, буквально пару дней назад. Был у меня класс, в котором была переменная хранившая XML. Этот класс должен был использоваться другим человеком. Если занулить / не инициализировать переменную, то были бы ошибки, и очевидно поэтому человек использовавший мой класс решил сделать следующее: _xml = new XML() (и заработало, но не совсем...). Я не предполагал, что XML в этом месте может быть не элементом, а текстом, например. Я когда увидел, чуть не заплакал. Переделал переменную в константу - стало менее удобно, но без ошибок.

Ну а если я что-то поменял в моем классе, что потенциально приведет в нерабочее состояние чужой код - ну так я, чисто по-человечески должен хотя-бы в коммит-лог об этом написать. Опять же, юнит тесты с большой вероятностью покажут, что я что-то сломал, если сломал. Ну и я делаю так: public и protected, если я меняю, я дописываю [Deprecated] и оставляю так на несколько версий, пока все не переделают под новый вариант. То, что я не предполагаю, что другие будут использовать - private / internal. Если кто-то использовал internal - на свой страх и риск, и если не работает / перестало - не мои проблемы.

expl 08.05.2011 12:23

Цитата:

Так, например, удалось донести до молодого сотрудника вредность синглтонов.
Меня не вылечило :( Ситуация обратная - если тянуть ссылку на класс - надо перелопатить 10 тестов, если обращаться в тестируемом классе к синглтону (в тесте, конечно, аккуратно снаружи подменить синглик моком) - то 1 тест. Чего-то идет не так...

Хотя с другой стороны, эти 10 тестов, в которых ни слухом - ни духом о реальном, не замененном моком синглике - могут однажды повести себя неадекватно.

Crazy 08.05.2011 12:45

Цитата:

Сообщение от expl (Сообщение 994784)
Меня не вылечило :( Ситуация обратная - если тянуть ссылку на класс


...то синглетон для этого не нужен. Применяемая для этого конструкция похожа, но не имеет к синглетону никакого отношения.

expl 08.05.2011 13:58

Имеете в виду статическую фабрику?
Код AS3:

public function ServiceOwner
{
    private var _service:IService;
    public static function get service():IService
    {
          if (_service == null)
          {
              _service = new MyService();
          }
          return _service;
    }
}

Ну да, преимуществ по сравнению с сингилком 2: Нет ограничения на количество сервисов, код сервиса свободен от выполнения обязанностей по контролю за своим инштанцированием. Но на них и забить можно - тупо _не_ запрещать синглику инштанцироваться вне getInstance() - кому второй синглик понадобится - фабрику се сделает.

wvxvw 08.05.2011 17:57

expl.
ОК, пример :)
Код AS3:

package tests
{
        import com.***.net.tcp.NinjaEvent;
        import com.***.net.tcp.NinjaMethods;
        import com.***.net.tcp.NinjaService;
 
        import flash.events.Event;
 
        import flexunit.framework.Assert;
 
        import org.flexunit.async.Async;
 
        public class TestNinjaKillActivity
        {
                private var _activityDead:Boolean;
                // Падаван хотел это сделать синглотоном :) А тут внезапно их два - облом :)
                private var _sender:NinjaService =
                        new NinjaService("123456", "127.0.0.1");
                private var _receiver:NinjaService =
                        new NinjaService("654321", "127.0.0.1");
 
                public function TestNinjaKillActivity() { super(); }
 
                [Before(async)]
                public function before():void
                {
                        this._sender.addEventListener(Event.CONNECT, this.sender_connectHandler);
                        this._receiver.addEventListener(
                                Event.CONNECT, this.receiver_connectHandler);
                        this._receiver.addEventListener(
                                NinjaEvent.GET_ACTION, this.getActionHandler);
                        this._receiver.addEventListener(
                                NinjaEvent.CALL_RECEIVED, this.receiver_callReceivedHandler);
                        this._sender.addEventListener(
                                NinjaEvent.CALL_STARTED, this.sender_callStartedHandler);
                        Async.proceedOnEvent(this, this._receiver, "test", 10000);
                        this._sender.connect();
                        this._receiver.connect();
                }
 
                private function sender_connectHandler(event:Event):void
                {
                        if (this._receiver.connected) this.bothConnected();
                        trace("sender_connectHandler");
                }
 
                private function receiver_connectHandler(event:Event):void
                {
                        if (this._sender.connected) this.bothConnected();
                        trace("receiver_connectHandler");
                }
 
                private function sender_callStartedHandler(event:NinjaEvent):void
                {
                        this.killActivity();
                        trace("sender_callStartedHandler");
                }
 
                private function receiver_callReceivedHandler(event:NinjaEvent):void
                {
                        (this._receiver.methods as NinjaMethods)
                        .acceptCall(this._sender.clientId);
                        trace("receiver_callReceivedHandler");
                }
 
                public function bothConnected():void
                {
                        (this._sender.methods as NinjaMethods)
                        .startCall(this._receiver.clientId, "externalId", "a", "b", "c", "d");
                        trace("bothConnected");
                }
 
                private function killActivity():void
                {
                        (this._sender.methods as NinjaMethods)
                                .killActivity(this._receiver.clientId);
                }
 
                private function getActionHandler(event:NinjaEvent):void
                {
                        this._activityDead = true;
                        this._receiver.dispatchEvent(new Event("test"));
                        this.testReceive();
                        trace("getActionHandler");
                }
 
                [Test(async, timeout="10000")]
                public function testReceive():void
                {
                        Assert.assertTrue(this._activityDead);
                }
        }
}

(Названия не я придумывал, это тяжелое наследие царского режима).

expl 08.05.2011 20:02

Ага, как обломали подавана, понятно (ох, как я часто от коллег слышу "А давай этот класс сделаем синглтоном" - хочется увесистой книжкой по пальцам сразу).
А вот как вы ссылки тянете на эти сервисы в клиентские классы не очень.

Например есть модель:
- Юзер
--деревня
---грядка
Главный контроллер (его я даже не пытаюсь покрыть unit-тестами - он завязан на все и вся, но сложной логики там мало, большинство делегируется его детям и модели) инштанцирует СинхронизаторВремениССервером.
Если передавать его всем детям в стиле "push" получается:
- надо протащить ссылки через Юзера и деревню
- надо каждый раз передавать грядке синхронизатор при ее добавлении

Получается лезем в тесты Юзера и деревни, чтобы добавить в конструктор этот сервис (допустим, он нужен только грядке)
То что в тестах деревни может что-то зависеть от синхронизатора, который находится в Грядке - это другой вопрос.

wvxvw 08.05.2011 21:30

К сожалению, в реальном проекте взаимодействие между частями - это вотчина падавана (он там работает на 2 года больше меня, и мне поэтому ничего "серьезного" не доверяют... ну так вот :)). На то, что там происходит без слез / смеха смотреть тяжело... Так что, как "у нас", уж наверняка лучше не делать.
Так, как я бы делал - иерархия, ребенок сообщает наверх, и не его забота кто и как обработает, даже ни на что не подписывается, когда надо будет, ему родитель "скажет" что делать.

expl 08.05.2011 21:44

По поводу "идеалистического" подхода

Вот есть ребенок, у его родителя есть на него ссылка и родитель может принимать от него события
Если ребенка пнули или изменили - что делать ясно - посылать событие - родитель, или кто повыше - обработает и поменяет других детей, если надо.
А если у ребенка спросили что-то что он не знает (или даже ему самому потребовались внешние данные), что делать?

Один из моих подходов - пропихиваем в ребенка при создании класс-сервис, у которого есть поля с нужными данными (его трудно тянуть, иногда такие сервисы становятся сингликами)

Ваш подход:
-у ребёнка что-то спросили
-он шлет событие
-его обрабатывает родитель
-родитель дергает геттер ребёнка и присваивает нужное значение
Что-то подсказывает, что такая схема будет неудобной/ненадежной. Я правильно хоть суть понял?

Bgg 08.05.2011 21:49

Класс-сервис усложнит только ребенка, и добавятся ещё события о том что ребенок, например, не нашел нужные данные, т.е. в него придется вкладывать логику и опять пойдут события аля "parentHelpMe.IdontKnowWhatToDo"

i.o. 08.05.2011 21:57

Цитата:

А если у ребенка спросили что-то что он не знает [без того, что в скобочках] что делать?
Когда такая надобность может возникнуть вообще?
Давайте обсудим с примерчиками, может я чего-то не знаю )


Часовой пояс GMT +4, время: 09:29.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.