![]() |
|
||||||||||
|
|||||||
|
|
« Предыдущая тема | Следующая тема » |
| Опции темы | Опции просмотра |
|
![]() |
![]() |
|
|||||
|
Регистрация: Nov 2010
Сообщений: 497
|
Это не правило, это наблюдение того, что получается на практике
![]() По архитектуре нужно скорее не читать, а писать. Основное, что определяет архитектура - набор абстракций. Абстракции лучше всего записывать человеческим языком, так как "названия класса" мало для описания абстракции. Отсюда вывод - нужно писать документацию (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, где разделяются ответственности за создание объектов и выполнение его функций. В общем, архитектура - это больше практическая дисциплина. В ней мало теоретических работ. Так что фундаментального читать практически нечего. Но очень полезно читать всевозможные описания существующих проектов. Например, блоги разработчиков. Там описаны проблемы, варианты их решения и результаты примененеия решений. Полезно знать возникающие вопросы, последствия различных решений. При этом совершенно не обязательно соглашаться с самими решениями! Достаточно считать, что автор провел эксперимент и получил определенные результаты. Как их трактовать, решаете уже вы сами. |
|
|||||
|
Modus ponens
|
Есть такой способ принятия решений известный как Five Whys или Root Cause. Про него писал менеджер компании Toyota Шоичи Оно. Это способ как принятия решений, так и анализа случившихся событий, как правило, негативно повлиявших на исход.
Собственно, ничего нового в этом нет, но как и с любыми простыми вещами, когда они случаются в комбинации с другими простыми вещами иногда можно потеряться Смысл анализа заключается в том, чтобы последовательно спросить "почему" пять или больше раз (как правило, больше уже не нужно).В вашей ситуации, ход размышлений мог бы быть следующим: - Почему я делаю одну и ту же операцию много раз? - Потому, что я не хочу создавать экземпляр SharedObject до того, как будет вызван один из методов, но я не знаю, какой из методов будет вызван первым. - Почему существует неопределенность, какой из методов будет вызван первым? - Потому что все методы статические, и я не могу прямо повлиять на порядок вызова - Я не могу обязать какой-то из методов быть вызванным раньше остальных, т.как контекст в котором создаются методы мной не управляется. - Почему я не могу управлять контекстом, в котором существуют нужные мне методы? - Потому, что методы статические, и неуправляемость присуща именно таким методам. На этом этапе уже можно сделать вывод, что делать методы статическими - плохая идея т.как если бы вы сделали их методами класса, то, например, в конструкторе класса вы могли бы создать нужный вам SharedObject и, таким образом, решить начальную проблему.
__________________
Hell is the possibility of sanity |
|
|||||
|
Регистрация: Dec 2010
Сообщений: 177
|
maxkar
Спасибо, за столь интересный и развернутый ответ! Я недавно почти прочитал, как мне кажется очень полезную книжку ActionScript 3.0: Design Patterns Она доставила мне уйму удовольствия, стараюсь применять шаблоны проектирования на практике, где это требуется. Кстати, что касается одиночки, жаль что нельзя создать базовый класс и от него наследовать функционал одиночки: переменные и ф-ю getInstance(), т.к. она статическая и ее унаследовать нельзя. Приходится в каждом новом классе заново создавать копировать код функционала одиночки. |
|
|||||
|
Modus ponens
|
Кстати, интересный момент. Шаблоны проектирования, это как раз противоположность анализа. Т.е. шаблон, это способ избежать анализа, и, основываясь на интуитивном предположении о желаемой структуре проекта, реализовать ее по уже готовому образцу. С практической точки зрения, это более выгодный способ, но он чем-то сродни методу Монте Карло. Т.е. он допускает, что будет принято ошибочное решение, т.как "инуиция подвела". Зато, он дает результат гораздо быстрее, чем детерминистский анализ.
Ваш пример тому подтверждение. Вы интуитивно решили, что шаблон "одиночка" является решением вашей проблемы, в то время как нет никакой практической необходимости в том, чтобы SharedObject был создан в единственном экземпляре, или, по крайней мере, это не следует из вашей постановки задачи.
__________________
Hell is the possibility of sanity |
|
|||||
|
Регистрация: Dec 2010
Сообщений: 177
|
wvxvw
Интересно пишете, а так да, обычно решения принимаются или списком "за и против" или интуитивно на базе шаблонов. В данном случае класс Profile является надстройкой над SharedObject, и подохдт больше под сервисный класс, экземпляр которого создается один раз. >>в то время как нет никакой практической необходимости в том, чтобы SharedObject был создан в >>единственном экземпляре Можно использовать SharedObject вызвав его в любом месте программы, но как мне кажется это внесет разрозненность в логическую структуру, т.е. я стараюсь каждый отдельный функционал ( в данном случае одиночка Profile инструмент сохранения/загрузки данных игры из куки ), как можно больше локализировать, чтобы была более читаемая и узнаваемая структура кода (системы). Когда проект разростается, очень важно, чтобы он разбивался на кучу логических частей (за каждой закреплена своя узкая задача), при том взаимозаменяемых и свободно расширяемых (модульность). |
|
|||||
|
Modus ponens
|
А вы не сможете избежать того, чтобы SharedObject был создан в нескольких экземплярах: например, пользователь открыл вашу страницу в нескольких закладках, в нескольких браузерах, даже разных версиях плеера и т.п. Т.е. то, что вы пытаетесь сделать на уровне приложения будет вас только вводить в заблуждение относительно того, как оно будет по-настоящему работать.
__________________
Hell is the possibility of sanity |
|
|||||
|
.
|
По сути so[name].data это константа.
package { public const SHARED_OBJECT:SharedObject = SharedObject.getLocal(Constant.SHARED_OBJECT_NAME).data; } Вероятно, параметром вы называете примитивное значение. Это не так. Параметром функции вполне может являться и ссылка. Так что тут "вместо" совершенно непотребно. Я даже понимаю, что вы передаете ссылку на класс, а не на объект класса, верно? Последний раз редактировалось dimarik; 24.03.2012 в 17:50. |
|
|||||
|
[+4 07.04.12]
[+1 20.01.12] Регистрация: Nov 2009
Адрес: Украина, Славутич
Сообщений: 263
|
объясните пожалуйста доступно чем плохо использование статики... спасибо
|
|
|||||
|
Modus ponens
|
Статики это может быть даже очень хорошо - зависит от того, что вам нужно. В большинстве случаев, статическая функция это такая функция, которая не нуждается в "состоянии" т.е. на нее не влияет текущее состояние приложения и она сама не влияет на текущее состояние приложения. Еще такие функции называют "чистыми". Только не потому, что они часто моются, а потому, что они сильно упрощают тестирование приложения, т.как не нужно проверять работу функции в разных ситуациях. Например, сложение - чистая функция, т.как не зависимо от никаких внешних факторов, вы получите результат основанных только на параметрах переданных в функцию.
Есть языки, которые стараются использовать только чистые функции, но зачастую это оказывается очень непрактичным, например, вы не захотите устанавливать соединение с сервером баз данных всякий раз, как вам нужно будет послать запрос - напротив, вы захотите хранить подключенное состояние сервера. Функции которые специализируются на объекте какого-то типа зависят от состояния этого объекта, это может приводить к разным негативным последствиям, например, невозможности математически описать систему в которой работает функция (а следовательно, невозможности сделать вывод о ее полезности). Это так же может привести к ситуации, в которой функция неадекватно отреагирует на состояние объекта на котором она специализируется. Но как правило, с точки зрения эффективности, такие функции имеют преимущество т.как им не нужно вычислять все операции, которые приведут объект с которым они работают в нужное состояние. Я бы считал хорошей практикой состояние кода когда в нем все, что только возможно сделать статически, без ущерба для программы, делается статически, и только в местах, где производительность обязывает использовать методы объектов их использовать. К сожалению, такой подход оказывается непрактичным в языках типа AS потому, что очень часто программисту нужно наперед планировать совместимость. Это становится возможным если использовать интерфейсы т.как они не предписывают конкретную реализацию. Но отдельные (статические) функции не могут реализовывать интерфейсы, и поэтому часто есть смысл использовать метод объекта там, где хотелось бы использовать статическую функцию.
__________________
Hell is the possibility of sanity |
![]() |
![]() |
Часовой пояс GMT +4, время: 23:38. |
|
|
« Предыдущая тема | Следующая тема » |
|
|