Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Стиль реализации статического класса (http://www.flasher.ru/forum/showthread.php?t=177053)

Alexmody 23.03.2012 15:12

Стиль реализации статического класса
 
Вариант 1:

Есть статик класс:

Код AS3:

public class Profile
{
        public function Profile(){       
        }
 
        public static function setVar(name:String, value:*):void
        {
                var sharedObject:SharedObject = SharedObject.getLocal(Constant.SHARED_OBJECT_NAME);
                        sharedObject.data[name] = value;
                        sharedObject.flush();
        }
 
        public static function getVar(name:String):*
        {
                        var sharedObject:SharedObject = SharedObject.getLocal(Constant.SHARED_OBJECT_NAME);
                        return sharedObject.data[name];
        }
 
        public static function clear():void
        {
                        var sharedObject:SharedObject = SharedObject.getLocal(Constant.SHARED_OBJECT_NAME);
                        sharedObject.clear();
        }
        …
}


Как видно getLocal() вызывается в каждой статической функции. Что, наверное тратит лишние производительные ресурсы.

Вариант 2: можно сделать статическую переменную static var sharedObject:SharedObject;
и один раз получить куки, а далее использовать ее во всех статик функциях.

Код AS3:

public static function init(name:String, value:*):void
{
        sharedObject:SharedObject = SharedObject.getLocal(Constant.SHARED_OBJECT_NAME);
 
}

Но при этом обязательно сначала надо будет вызвать init, а только потом уже остальные функции.


Вариант 3:
при объявлении статической переменной, сразу же ее инициализировать

Код AS3:

public class Profile
{
        sharedObject:SharedObject = SharedObject.getLocal(Constant.SHARED_OBJECT_NAME);
 
        public function Profile(){       
        }
        ...
}


Какой вариант правильнее всего выбрать?

maxkar 23.03.2012 15:53

Цитата:

Какой вариант правильнее всего выбрать?
Правильнее всего сделать методы в Profile не статическими, создавать один его экземпляр при старте приложения (в главном классе) и передавать его всем тем, кому он нужен. Соответственно, sharedObject передавать к конструктор.

dsQuadro 23.03.2012 16:33

может проще синглтон использовать?

КорДум 23.03.2012 16:34

Синглтон?
Я бы сделал еще статический метод init(), в который передавал SharedObject.getLocal(Constant.SHARED_OBJECT_NAME).

carrotoff 23.03.2012 17:41

Терпеть не могу статику. Мне кажется лучший вариант от maxkar.
Статику можно использовать исключительно в качестве хэлперов.

dsQuadro 23.03.2012 17:45

Код AS3:

package
{
        public class Profile
        {
                private static var instance:Profile;
                private var sharedObject:SharedObject;
 
 
                public function Profile(privateClass:ProfileSingletone)
                {
                        sharedObject = SharedObject.getLocal(Constant.SHARED_OBJECT_NAME);
                }
 
                public static function getInstance():Profile{
                        if (instance == null) {
                                instance = new Settings(new ProfileSingletone);
                        }
                        return instance;
                }
 
                public function getVar(name:String):*
                {
                        return sharedObject.data[name];
                }
                ...
        }
}
 
class ProfileSingletone { };

в любом месте приложения вызываете
Код AS3:

Profile.getInstance()

и работаете как с обычным экземпляром класса

Alexmody 23.03.2012 17:47

maxkar
>>правильнее всего сделать методы в Profile не статическими, создавать один его экземпляр при старте >>приложения

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

КорДум

Я бы сделал еще статический метод init(), в который передавал SharedObject.getLocal(Constant.SHARED_OBJECT_NAME).

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

dsQuadro
Замечательное решение к данной задаче:
- глобал доступ
- создание экземпляра идет не при запуске программы, а в том месте, где в первый раз воспользовались функцией, это супер!
- минимум статик функций
- появилась возможность только один раз вызвать ф-ю getLocal в конструкторе и передать SharedObject приватной переменной, которая далее используется в функциях класса.
- решилась проблема с первоочередностью вызова init - такую функцию вообще вырезаем.

Ага спасибо за пример , реализация заготовки синглтона у меня была:

Код AS3:

public class Profile
        {
                private static var _inst:Profile;
                private static var _allowInstantiation:Boolean = false;
 
                private var sharedObject:SharedObject;
 
                public function Profile()
                {
                        if(!_allowInstantiation)
                            throw new Error("You can not created instance directly. This class uses Singelton pattern");
 
                        sharedObject = SharedObject.getLocal(Constant.SHARED_OBJECT_NAME);
                }
 
                public static function get inst():Profile
                {
                        if (!_inst)
                        {
                                _allowInstantiation = true;
                                _inst = new Profile();
                                _allowInstantiation = false;
                        }
                        return _inst;
                }
 
                public function setVar(name:String, value:*):void
                {
                        sharedObject.data[name] = value;
                        sharedObject.flush();
                }
 
                public function getVar(name:String):*
                {
                        return sharedObject.data[name];
                }
 
                public function clear():void
                {
                        sharedObject.clear();
                }
 
                //...
}


maxkar 23.03.2012 18:00

Цитата:

Согласен, к этому и стараюсь идти, чтобы не плодить глобал переменные, но порой очень не удобно передавать экземпляр внутрь иерархической структуры классов.
По иерархии наследования (в конструктор родителя) или по экземплярам (через конструкторы "дочерних"/вложенных) объектов перечада идет? В обоих случаях это может свидетельствовать о том, что где-то не до конца вынесены зависимости. Т.е. если есть класс A, который в конструкторе получает ссылку на Profile только для того, чтобы передать ее конструктору классу Б, значит, вероятно, конструктор класса А должен получать экземпляр класса Б, а не создавать его. Вся программа при инициализации собирается "снизу вверх", т.е. "примитивные" классы, классы "более высокого" уровня и т.п. Это устраняет большую часть проблемы, так как не нужно передавать параметры своим зависимостям.

Исключением является "крупномодульное" взаимодействие (плагины и т.п., получающие набор классов среды выполнения). Но там подобная передача ограничивается тем же одним уровнем, код внутри плагина точно так же собирается "от меньшего к большему". Так что "далеких" передач объектов нигде нет, есть только последовательность создания модуля/приложения. При использовании inversion-of-control контейнера там не линейная, а древовидная схема получается, но это не принципиально.

КорДум 23.03.2012 18:04

Цитата:

Не очень хорошо, когда в статик классе нельзя пользоваться другими статик функциями, пока не вызвать статик ф-ю init. В этом и изначальная загвоздка была.
Смотря где.
У flashdynamix есть в утилитах SWFProfiler со статическим методом init(), который принимает ссылку на stage.
Ну и я делал как-то похожее.

Синглтон, в общем, нужен.

Alexmody 23.03.2012 22:38

maxkar
>>что где-то не до конца вынесены зависимости
Для снижения связей и зависимостей между классами в древовидной иерархии, я стараюсь максимально применить композицию построенную на ее принципе и интерфейсах. Тем не менее, никогда не задумывался и не придерживался правилу "Вся программа при инициализации собирается "снизу вверх"". Как только узнаешь это правило, тут же всплывают все приемущества такого подхода, при том что я очень люблю композицию. Спасибо за совет, буду больше думать об этом!
Действительно, если упрощенно говорить, то передача класса по дочерним классам, может быть выстроена почти на одном уровне вложенности.

Кстати, посоветуйте какие книжки можно почитать по архетиктуре ПО? Т.е. к примеру по паттернам проектирования материал читаю, также стараюсь использовать правила SOLID и пр.

maxkar 24.03.2012 12:58

Это не правило, это наблюдение того, что получается на практике :)

По архитектуре нужно скорее не читать, а писать. Основное, что определяет архитектура - набор абстракций. Абстракции лучше всего записывать человеческим языком, так как "названия класса" мало для описания абстракции. Отсюда вывод - нужно писать документацию (asdoc). Во время написания такой документации как раз могут всплывать ошибки архитектуры. Так, если параметр метода или конструктора плохо согласутеся с абcтракцией, выражаемой классом, значит, вероятно, нужно вынести дополнительный уровень абстракции (вместо параметра передавать уже созданный класс, например). Большой объем документации может свидетельствовать о том, что нарушен SRP. Обычно достаточно нескольких (1-5) предложений для хорошего описания. Конечно, встречаются и случаи, когда документации нужно действительно больше.

Далее. Нужно освоить полиморфизм и инкапсуляцию. И то, и другое ортогонально ООП (например, прекрасно выражается в ФП). Вообще, и то, и другое, связано с абстракцией. Полиморфизм - возможность описывать одной абстракцией различные детали поведения. Инкапсуляция - отсутствие "не соответствующих абстракции" деталей реализации. По данным аспектам книг не посоветую - не знаю.

Еще очень полезно освоить понятия coupling и cohesion. а также то, какие типы cohesion лучше и хуже. Здесь можно почитать "Совершенный код" Стива Макконела ("Code Complete"), глава 5 "Design In Construction". Там как раз описан coupling. Еще стоит посмотреть на главы 6.2 ("Good Class Interfaces") и 7.2 ("Design at the Routine Level"). Первая - про абстракции, вторая - про cohesion.

Желательно попробовать что-нибудь функциональное (lisp (не углубляясь в метапрограммирование), scheme, ml (ocaml/sml), haskell (хотя там уже уклон в сторону типов)). Практика написания на них закрепляет навыки функциональной декомпозиции (именно функциональной!) да и инкапсуляцию/полиморфизм демонстрирует в новом свете.

Еще могу порекомендовать "Practical API Design Confessions of a Java Framework Architect". Автор пишет о немного специфичной области, но много и общеприменимых идей. Для архитектуры особое внимание стоит обратить на различие "provides" и "requires". Отличие принципиальное и очень помогает в разработке. Обычно любой класс пишется либо для удовлетворения "requires" (и проектируется от требований в конкретном месте), либо "provides" - библиотечный класс, который предоставляет функции с широкой обсластью применимости. Двум сразу сценариям один и тот же класс удовлетворять не должен. На практике обычно большинство классов реализуются путем уточнения и написания "requires" (дизайн сверху вниз, пишется высокоуровневый код, затем реализуются его зависимости, для них реализуются их зависимости и т.п.). Потом уже выделяются библиотеки ("provides") которые с помощью адаптеров (разработанных от "requires") используются при решении задачи.

Еще у того же Макконела в "Совершенном коде" можно прочитать про метафоры программирования. Особое внимание стоит обратить "toolbox metaphor", это нужно для того, чтобы критично относиться к рекламе различных методологий.

Ну а после этого нужно писать дальше. Заниматься декомпозицией. На уровне кода. На старте приводит к большому количеству интерфейсов и классов, но дает достаточно много возможностей замены реализации. На уровне библиотек. У вас одна большая "стандартная библиотека" в компании или много маленьких? Если одна большая - распилить на много маленьких, но с хорошей функциональной cohesion. Например, библиотеки UI, функции работы с коллекциями, функции для работы с функциями, несколько библиотек работы с сетью (на разных уровнях абстракции). Освоить бранчевание кода (в version control)) и не бояться экспериментировать. С большим количеством небольших библиотек экспериментировать проще, чем с одной монолитной - гораздо точнее ясны требования к библиотеке и сама библиотека для переделки изолирована. Также нужно следить и за своим ощущением о "качестве" текущей архитектуры. Это удобство ее использования в самом широком смысле (необходимость писать лишний код только для удволетворения нужд фреймворка, неудачные абстракции, сложность модификации под другие задачи и т.п.).

А практически все остальные принципы следуют из уже сказанного выше. Тот же SOLID. SRP - это чистота абстракции и удобство полиморфизма. Open/Closed principle автоматически работает при разделении на дизайн от "requires" и "provides". Там же и его ограничения всплывают. Очевино, что "provides" для введения новой функциональности менять не стоит. А вот классы, созданные от "requires" как раз вполне можно (такие классы без их пользователя обычно имеют мало смысла). LSP - хорошее правило для работы с абстракциями. ISP - частный случай SRP на самом деле. Ну и плюс следствие "requires" и полиморфизма по каждому независимому аспекту (именно так и реализуется хорошее разбиение). Dependency Injection - еще одна реализация SRP, где разделяются ответственности за создание объектов и выполнение его функций.

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

wvxvw 24.03.2012 14:43

Есть такой способ принятия решений известный как Five Whys или Root Cause. Про него писал менеджер компании Toyota Шоичи Оно. Это способ как принятия решений, так и анализа случившихся событий, как правило, негативно повлиявших на исход.
Собственно, ничего нового в этом нет, но как и с любыми простыми вещами, когда они случаются в комбинации с другими простыми вещами иногда можно потеряться :) Смысл анализа заключается в том, чтобы последовательно спросить "почему" пять или больше раз (как правило, больше уже не нужно).

В вашей ситуации, ход размышлений мог бы быть следующим:
- Почему я делаю одну и ту же операцию много раз?
- Потому, что я не хочу создавать экземпляр SharedObject до того, как будет вызван один из методов, но я не знаю, какой из методов будет вызван первым.

- Почему существует неопределенность, какой из методов будет вызван первым?
- Потому что все методы статические, и я не могу прямо повлиять на порядок вызова - Я не могу обязать какой-то из методов быть вызванным раньше остальных, т.как контекст в котором создаются методы мной не управляется.

- Почему я не могу управлять контекстом, в котором существуют нужные мне методы?
- Потому, что методы статические, и неуправляемость присуща именно таким методам.

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

Alexmody 24.03.2012 14:50

maxkar
Спасибо, за столь интересный и развернутый ответ! Я недавно почти прочитал, как мне кажется очень полезную книжку ActionScript 3.0: Design Patterns Она доставила мне уйму удовольствия, стараюсь применять шаблоны проектирования на практике, где это требуется.

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

wvxvw 24.03.2012 14:57

Кстати, интересный момент. Шаблоны проектирования, это как раз противоположность анализа. Т.е. шаблон, это способ избежать анализа, и, основываясь на интуитивном предположении о желаемой структуре проекта, реализовать ее по уже готовому образцу. С практической точки зрения, это более выгодный способ, но он чем-то сродни методу Монте Карло. Т.е. он допускает, что будет принято ошибочное решение, т.как "инуиция подвела". Зато, он дает результат гораздо быстрее, чем детерминистский анализ.

Ваш пример тому подтверждение. Вы интуитивно решили, что шаблон "одиночка" является решением вашей проблемы, в то время как нет никакой практической необходимости в том, чтобы SharedObject был создан в единственном экземпляре, или, по крайней мере, это не следует из вашей постановки задачи.

Alexmody 24.03.2012 15:45

wvxvw
Интересно пишете, а так да, обычно решения принимаются или списком "за и против" или интуитивно на базе шаблонов. В данном случае класс Profile является надстройкой над SharedObject, и подохдт больше под сервисный класс, экземпляр которого создается один раз.

>>в то время как нет никакой практической необходимости в том, чтобы SharedObject был создан в >>единственном экземпляре
Можно использовать SharedObject вызвав его в любом месте программы, но как мне кажется это внесет разрозненность в логическую структуру, т.е. я стараюсь каждый отдельный функционал ( в данном случае одиночка Profile инструмент сохранения/загрузки данных игры из куки ), как можно больше локализировать, чтобы была более читаемая и узнаваемая структура кода (системы). Когда проект разростается, очень важно, чтобы он разбивался на кучу логических частей (за каждой закреплена своя узкая задача), при том взаимозаменяемых и свободно расширяемых (модульность).

wvxvw 24.03.2012 16:18

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

dimarik 24.03.2012 17:39

По сути so[name].data это константа.

Код AS3:

package {
 
public const SHARED_OBJECT:SharedObject = SharedObject.getLocal(Constant.SHARED_OBJECT_NAME).data;
 
}

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

Сообщение от maxkar (Сообщение 1070851)
вместо параметра передавать уже созданный класс, например

Вероятно, параметром вы называете примитивное значение. Это не так. Параметром функции вполне может являться и ссылка. Так что тут "вместо" совершенно непотребно. Я даже понимаю, что вы передаете ссылку на класс, а не на объект класса, верно?

anmelegov 24.03.2012 17:51

объясните пожалуйста доступно чем плохо использование статики... спасибо

dimarik 24.03.2012 18:07

Вроде ничем не плохо. Скорость доступа, правда, страдает. Если у вас цикл из 100к итераций, то скопируйте поле в локальную переменную.

wvxvw 24.03.2012 21:27

Статики это может быть даже очень хорошо - зависит от того, что вам нужно. В большинстве случаев, статическая функция это такая функция, которая не нуждается в "состоянии" т.е. на нее не влияет текущее состояние приложения и она сама не влияет на текущее состояние приложения. Еще такие функции называют "чистыми". Только не потому, что они часто моются, а потому, что они сильно упрощают тестирование приложения, т.как не нужно проверять работу функции в разных ситуациях. Например, сложение - чистая функция, т.как не зависимо от никаких внешних факторов, вы получите результат основанных только на параметрах переданных в функцию.
Есть языки, которые стараются использовать только чистые функции, но зачастую это оказывается очень непрактичным, например, вы не захотите устанавливать соединение с сервером баз данных всякий раз, как вам нужно будет послать запрос - напротив, вы захотите хранить подключенное состояние сервера.
Функции которые специализируются на объекте какого-то типа зависят от состояния этого объекта, это может приводить к разным негативным последствиям, например, невозможности математически описать систему в которой работает функция (а следовательно, невозможности сделать вывод о ее полезности). Это так же может привести к ситуации, в которой функция неадекватно отреагирует на состояние объекта на котором она специализируется. Но как правило, с точки зрения эффективности, такие функции имеют преимущество т.как им не нужно вычислять все операции, которые приведут объект с которым они работают в нужное состояние.
Я бы считал хорошей практикой состояние кода когда в нем все, что только возможно сделать статически, без ущерба для программы, делается статически, и только в местах, где производительность обязывает использовать методы объектов их использовать. К сожалению, такой подход оказывается непрактичным в языках типа AS потому, что очень часто программисту нужно наперед планировать совместимость. Это становится возможным если использовать интерфейсы т.как они не предписывают конкретную реализацию. Но отдельные (статические) функции не могут реализовывать интерфейсы, и поэтому часто есть смысл использовать метод объекта там, где хотелось бы использовать статическую функцию.

maxkar 25.03.2012 20:58

Цитата:

Кстати, что касается одиночки, жаль что нельзя создать базовый класс и от него наследовать функционал одиночки: переменные и ф-ю getInstance(), т.к. она статическая и ее унаследовать нельзя.
Ну и правильно. Потому что наследование от "Abstract Singleton" очень плохо соотносится с LSP (Liskov substitution Principle). Наследование - это реализация отношения is-a. Представить себе что-то, работающее с Singleton - практически невозможно. А вот фабрику для упрощения вполне можно сделать. Она даже и методом может быть public static const getInstance : Function = createLazy(MySingleton.class). В AS3 правда при этом типизация потеряется. Если язык поддерживает макросы (или что-то высокоуровневое), можно и генерировать аксессоры по аннтоациям на классе, например. Но на самом деле там проще аннотации для контейнера развесить вместо singleton'а (см. google guice, например, spring тоже вроде бы так умеет, но это все java-библиотеки).

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

Вероятно, параметром вы называете примитивное значение. Это не так. Параметром функции вполне может являться и ссылка. Так что тут "вместо" совершенно непотребно. Я даже понимаю, что вы передаете ссылку на класс, а не на объект класса, верно?
Не, не верно. Согласен, формулировка у меня плохая получилась. Имелась в виду транзитивная передача. Вместо
Код AS3:

public function SomeClass(arg : String) {
  this.someField = new SomeOtherClass(arg);
}

стоит получать сразу зависимость:
Код AS3:

public function SomeClass(arg : SomeOtherClass) {
  this.someField = arg;
}

Так можно не всегда. Иногда в качестве SomeOtherClass выступает деталь реализации (класс с internal обслатью видимости, например). Но в таких случаях имеет смысл вместо конструктора рассматривать "creation method" (он же - фабричный метод), который получает параметры и создает требуемый экземпляр. Для сложных экземпляров все их зависимости так же будут внутри этого метода создаваться "снизу вверх". Причина для создания creation method здесь не столько в правильном построении зависимостей (сам класс в конструкторе это тоже может сделать) а разделение ответственностей: сам класс отвечает за свою функцию среди нескольких взаимодействующих классов, а за правильную настроку отвечает статический метод. В подобных случаях обычно еще достаточно велика вероятность, что потребуются создание того же класса с другими настройками, от других типов аргументов и т.п. С "creation method" это делается проще и однообразно для всех созданий.

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

Сообщение от wvxvw
Это становится возможным если использовать интерфейсы т.как они не предписывают конкретную реализацию. Но отдельные (статические) функции не могут реализовывать интерфейсы, и поэтому часто есть смысл использовать метод объекта там, где хотелось бы использовать статическую функцию.

Так можно же передать и саму функцию. Да, потеряем типизацию, но не будет лишнего интерфейса. На самом деле даже если в языке у нас будет строгая типизация функций, это не сильно поможет делать все чистыми функциями. И проблема здесь будет уже в "чистоте интерфейса". Большинству потенциально "грязных" функций для работы нужен контекст. Для базы данных - адрес соединения с базой данных. Для логгера - настройки логирования, файл для записи и т.п. Необходимость передачи этих параметров явно влияет на интерфейс. Допустим, мы пишем доступ к каким-то данным. Интерфейс в терминах объектов (getSettings...). Объекты только читаются, и мы хотим добавить кэширование и следить за его эффективностью. Чтобы сделать это на чистых функциях, везде нужно передавать контекст - контекст кэша, контекст базы данных, контекст статистики кэша. Соответственно, все пользователи должны иметь этот контекст только для того, чтобы вызывать наш метод. Инкапсуляция состояния позволяет сделать метод с "чистым" интерфейсом вроде getData(параметры). Попытки сделать композицию статических функций чтобы получить требуемый интерфейс приведут к появлению "грязных" замыканий. Проблема инкапсуляции на чистых функциях не решается никак и нигде. Либо появляются "грязные" функции, либо везде ходит нужный тип. В том же haskell'е функции доступа будут работать с "полиморфным" типом а в собранном приложении у передаваемого параметра будет жуткий-жуткий тип (несколько модандных трансформеров вокруг друг-друга).


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

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