Форум 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)

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 года больше меня, и мне поэтому ничего "серьезного" не доверяют... ну так вот :)). На то, что там происходит без слез / смеха смотреть тяжело... Так что, как "у нас", уж наверняка лучше не делать.
Так, как я бы делал - иерархия, ребенок сообщает наверх, и не его забота кто и как обработает, даже ни на что не подписывается, когда надо будет, ему родитель "скажет" что делать.


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

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