Здравствуйте, barn_czn, Вы писали:
_>А самая удобная модель структуры — дерево.
А самая естественная — даг (directed acyclic graph), т.е. граф без циклов но, возможно, с контурами (т.е. как дерево, но у каждого узла может быть несколько родителей). Все эти ваши теги, грани или фасеты какие-то — суть завуалированные даги.
_>А самая удобная модель структуры — дерево.
Рассмотрим другой пример. Как организовать структуру файлов проекта?
Первый вариант:
И то, и другое — дерево, но какое предпочесть? Второй вариант — это транспонированный первый, вывернутый наизнанку. Такая ситуация возникает (как и у топик-стартера), когда есть независимые критерии (аспекты), по которым стоит разделять сущности (антивир/антиспам, скачивалка/интероп). Эти признаки образуют n-мерный параллелепипед. Чтобы зафиксировать иерархическую структуру, надо умостить параллелепипед в плоскость, т.е. упорядочить критерии, произвольно выбрать порядок в котором расслаиваем параллелепипед. Таким образом, попытка умостить набор сущностей в прокрустово ложе дерева, на самом деле, приводит к потере информации об исходной структуре.
Здравствуйте, barn_czn, Вы писали:
_>Даже в примеры не хочу вникать . Ересь эта фасеточная ваша модель. _>Программист всегда будет конструировать структруры (фасет ваш тоже структура в конечном итоге). _>А самая удобная модель структуры — дерево. Не произвольный граф, а именно дерево. _>Вы можете построить любую структуру поверх дерева (заюзав тэги, идентификаторы).
Не ересь, а весьма перспективное направление. Парадигма АОП кстати в некотором роде близка этой модели, только там речь идет о сквозной функциональности внутри функций, а здесь — о сквозной группировке единиц кода более высокого уровня.
Я лично думаю над тем, как можно такую фасеточную модель внедрить в язык программирования.
Дерево конечно удобно, но задача — сделать удобным использование таких моделей. Очевидно, что пользоваться произвольными графами менее удобно, чем деревьями, но вот теги по уровню удобства сравнимы с деревьями, так что думать надо в направлении "как использовать теги вместо namespaces".
Здравствуйте, x-code, Вы писали:
XC>Не ересь, а весьма перспективное направление. Парадигма АОП кстати в некотором роде близка этой модели, только там речь идет о сквозной функциональности внутри функций, а здесь — о сквозной группировке единиц кода более высокого уровня. XC>Я лично думаю над тем, как можно такую фасеточную модель внедрить в язык программирования. XC>Дерево конечно удобно, но задача — сделать удобным использование таких моделей. Очевидно, что пользоваться произвольными графами менее удобно, чем деревьями, но вот теги по уровню удобства сравнимы с деревьями, так что думать надо в направлении "как использовать теги вместо namespaces".
Вижу два аспекта:
1. Объявления. Как объяснить компилятору, в каких фасетах присутствует описываемый класс.
2. Доступ. Как объяснить компилятору, из какого фасета брать классы. Тем более, что не очень понятно, то ли имеется в виду "всё из antivirus, плюс всё из db", или "всё из пересечения antivirus и bd".
3. Как избежать взрыва мозга.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Вижу два аспекта: S>1. Объявления. Как объяснить компилятору, в каких фасетах присутствует описываемый класс.
В C++ для этого используется техника traits.
S>2. Доступ. Как объяснить компилятору, из какого фасета брать классы. Тем более, что не очень понятно, то ли имеется в виду "всё из antivirus, плюс всё из db", или "всё из пересечения antivirus и bd".
Что значит — брать? Опять же, это можно сделать через шаблоны и traits, благо в классах могут быть члены-типы (те самые классы, которые нужно "брать").
S>3. Как избежать взрыва мозга.
Здравствуйте, 0x7be, Вы писали:
0>Сейчас для логической организации во "мэйнстримовых" языках применяется иерархическая организация — пространства имен, пакеты и т.п. Однако жизнь штука сложная и не всегда хорошо вписывается в иерархическую схему. У меня часто возникали ситуации, когда более естественным способом организовать типы и функции в программе была бы фасеточная классификация, где гранями являются функциональные аспекты системы. Но фасеточная классификация неоднозначно отображается в иерархическую, что порождает некоторые проблемы, особенно если над проектом работает несколько людей.
Неймспейсы и так почти что теги. Вы можете взять свой класс и поместить его в любой неймспейс без "переписывания неймспейса", и существующего кода. Это то же самое, что пометить его произвольным тегом. Тегов может быть много, а неймспейс один, это правда, но контрактный тег (то, что вы называете "гранью") и должен быть один. Класс не должен выполнять несколько контрактов, это азбука ООП. Каждый контракт должен быть четко определен и относиться к одной области-грани (иначе, у вас декомпозиция корявая). То есть, остальные теги — это уточнение деталей и лучше их реализовать отдельно (через атрибуты).
Как правильно написали выше, без примеров это обсуждать бессмысленно. Нужен класс, который с равным основанием можно отнести к двум неймспейсам сразу. Не обязательно ваш пример, любой другой пойдет. Берусь показать, что это в каждом случае плохая декомпозиция.
Здравствуйте, Qbit86, Вы писали:
SV.>>Класс не должен выполнять несколько контрактов, это азбука ООП. Q>Это не азбука ООП, это ваши досужие заключения.
Беру это утверждение назад: некорректно сформулировал. Если каждый интерфейс считать за контракт, то, конечно, это глупость. Я имел в виду, что класс должен решать одну четко определенную функциональную задачу, в противном случае его надо разбить. Если и с этм несогласны, то сразу приводите контрпример.
Здравствуйте, SV., Вы писали:
SV.>Как правильно написали выше, без примеров это обсуждать бессмысленно. Нужен класс, который с равным основанием можно отнести к двум неймспейсам сразу. Не обязательно ваш пример, любой другой пойдет. Берусь показать, что это в каждом случае плохая декомпозиция.
Посмотрите здесь
Здравствуйте, 0x7be, Вы писали:
S>>Пока что не увидел — почему. 0>Ок, второй заход. Опишу минимальный набор компонентов, пригодный для иллюстрации идеи: 0>Есть следующие компоненты: 0>Updater — реализует в себе общую для всех часть функционала обновления. 0>AntivirusScanner — антивирусный сканер, использует Updater. 0>AntispamScanner — антиспамовый сканер, использует Updater. 0>AntivirusUpdater — антивирусный обновитель. 0>AntispamUpdater — антиспамовый обновитель.
0>Тут мы наблюдаем 4 фасета: Updater, Antivirus, Antispam, Scanner. 0>Фишка в том, что, например, AntivirusUpdater может быть расположен пространстве имен двумя РАВНОПРАВНЫМИ способами: 0>1. Antivirus.Updater.AntivirusUpdater. 0>2. Updater.Antivirus.AntivirusUpdater. 0>(ПРИМЕЧАНИЕ: порядок слов в самом идентификаторе AntivirusUpdater следует из правил английского языка ) 0>Сейчас волевым решением выбран первый вариант, но объективно он ничем не лучше. 0>Это исключительно соглашение, что бы было "безобразно, но единообразно".
0>Далее, если мы захотим заиметь ещё один вид сканера, то у нас добавляется ещё один фасет. 0>Пусть это будет какая-нибудь порнорезалка. Естественным образом у нас появляется фасет Pornbuster, и компоненты PornbusterScanner и PornbusterUpdater. 0>Никаких "криминальных" зависимостей между компонентами, которые бы зарубили нам расширяемость, тут не наблюдается.
0>Так лучше понятно?
Я правильно понимаю, что все это — исключительно сахар для разгребания кучи классов, не связанных между собой отношениями наследования и т.п?
Да, а язык какой?
Если это С++, в котором можно иметь члены-типы, то можно писать так:
// пусть мы стартуем от более-менее "главных" вещейclass Antivirus {
class Scanner;
class Updater;
};
class Antispam {
class Scanner;
class Updater;
};
class Pornbuster {
class Scanner;
class Updater;
};
// теперь граниstruct Updater {
typedef Antivirus::Updater Antivirus;
typedef Antispam::Updater Antispam;
typedef Pornbuster::Updater Pornbuster;
};
struct Scanner {
typedef Antivirus::Scanner Antivirus;
typedef Antispam::Scanner Antispam;
typedef Pornbuster::Scanner Pornbuster;
};
так как в С++ typedef — это не новый тип, а алиас на уже существующий тип, то ты теперь можешь писать как угодно, хоть Antivirus::Updater, хоть Updater::Antivirus — это будет один и тот же класс.
Из этого можно извлечь немалый профит, кстати, так как Updater — это сам по себе тип, которым можно, например, параметризовать какой-нть шаблон, и он автоматом подхватит именно то, что нужно.
Здравствуйте, 0x7be, Вы писали:
SV.>>Как правильно написали выше, без примеров это обсуждать бессмысленно. Нужен класс, который с равным основанием можно отнести к двум неймспейсам сразу. Не обязательно ваш пример, любой другой пойдет. Берусь показать, что это в каждом случае плохая декомпозиция. 0>Посмотрите здесь
Тут мы наблюдаем 4 фасета: Updater, Antivirus, Antispam, Scanner.
Фишка в том, что, например, AntivirusUpdater может быть расположен пространстве имен двумя РАВНОПРАВНЫМИ способами:
1. Antivirus.Updater.AntivirusUpdater.
2. Updater.Antivirus.AntivirusUpdater.
(ПРИМЕЧАНИЕ: порядок слов в самом идентификаторе AntivirusUpdater следует из правил английского языка )
Сейчас волевым решением выбран первый вариант, но объективно он ничем не лучше.
Это исключительно соглашение, что бы было "безобразно, но единообразно".
Это? AntivirusUpdater — класс? Я вижу два варианта:
1. На самом деле класс НЕ AntivirusUpdater. То есть, он может использоваться для обновления чего угодно, может взаимозаменяться с BITS, а написан просто потому, чтоб не привязываться к конкретным платформам типа COM. В этом случае класс надо переименовать в SoftwareUpdater, а неймспейс под него завести типа ServerIO, Updating или Steam
2. Это в самом деле AntivirusUpdater. Ваше приложение-антивирус должно обновляться не так, как другой софт. Есть какие-то очень важные нюансы. (Допустим даже, что AntivirusUpdater использует SoftwareUpdater). В этом случае Antivirus.Updater.AntivirusUpdater не равноправен Updater.Antivirus.AntivirusUpdater, поскольку Updater — компонент антивируса, а не наоборот.
Здравствуйте, SV., Вы писали:
SV.>2. Это в самом деле AntivirusUpdater. Ваше приложение-антивирус должно обновляться не так, как другой софт. Есть какие-то очень важные нюансы. (Допустим даже, что AntivirusUpdater использует SoftwareUpdater). В этом случае Antivirus.Updater.AntivirusUpdater не равноправен Updater.Antivirus.AntivirusUpdater, поскольку Updater — компонент антивируса, а не наоборот.
А как на счет сфокусированности (cohesion) кода? Например, если антивирус — это независимый компонент, который вообще ничего не знает об апдейтере? При этом есть апдейтер антивируса, который умеет апдейтить именно антивирус (специфичная логика) и тесно связан с общим кодом обновления вместе с другими апдейтерами. То, что ты предлагаешь — это по сути когда мы нечто делаем составной частью некоторого другого компонента, при том, что сам этот компонент вообще ничего не знает о своей "составной части". Это нормально? Ну то есть примерно как автомобильную заправку назвать составной частью автомобиля.
Здравствуйте, mrTwister, Вы писали:
SV.>>2. Это в самом деле AntivirusUpdater. Ваше приложение-антивирус должно обновляться не так, как другой софт. Есть какие-то очень важные нюансы. (Допустим даже, что AntivirusUpdater использует SoftwareUpdater). В этом случае Antivirus.Updater.AntivirusUpdater не равноправен Updater.Antivirus.AntivirusUpdater, поскольку Updater — компонент антивируса, а не наоборот.
T>А как на счет сфокусированности (cohesion) кода? Например, если антивирус — это независимый компонент, который вообще ничего не знает об апдейтере? При этом есть апдейтер антивируса, который умеет апдейтить именно антивирус (специфичная логика) и тесно связан с общим кодом обновления вместе с другими апдейтерами. То, что ты предлагаешь — это по сути когда мы нечто делаем составной частью некоторого другого компонента, при том, что сам этот компонент вообще ничего не знает о своей "составной части". Это нормально? Ну то есть примерно как автомобильную заправку назвать составной частью автомобиля.
Вообще-то, я предполагал, что антивирус — это приложение, которое дергает апдейтер по команде из GUI. Если нет, и общение приложения и апдейтера происходит только через конфиги, то это другой случай. Вот такой:
class Antivirus.Updater.AntivirusUpdaterService; // Класс апдейтераclass Antivirus.Core.Application; // Класс "независимого компонента, который вообще ничего не знает об апдейтере".
Еще раз, я полагаю, что в функциональном аспекте иерархии ВСЕГДА годятся. Иерархическая неоднозначность идет уровнем ниже (то есть, на уровне атрибутов). Если кто-то приведет контрпример, это будет весьма интересно.
SV.>class Antivirus.Updater.AntivirusUpdaterService; // Класс апдейтера
SV.>class Antivirus.Core.Application; // Класс "независимого компонента, который вообще ничего не знает об апдейтере".
SV.>
Прекрасно, а теперь посмотрим на все это с несколько другой точки зрения, а именно с кода апдейтера. Допусти есть не только апдейтер антивируса, но и апдейтер чего-то еще, есть базовый функционал для всех апдейтеров, есть планировщик задач апдейтера — то есть куча классов, у которых друг с другом довольно высокие cohesion и coupling. В предложенном тобой решении эти классы оказываются сильно разбросаны по иерархии и находятся друг от друга далеко. В этом и проблема. Если я правильно понял топик стартера, то он бы хотел в одном случае посмотреть на иерархию кода, который так или иначе связан с обновлением (какие есть классы, какие зависимости между ними, у кого какая ответственность и так далее), а в другом случае он бы хотел посмотреть на иерархию кода, который так или иначе связан с антивирусом. И в обоих случаях ему было бы удобно работать с разными иерархиями.
SV.>>class Antivirus.Updater.AntivirusUpdaterService; // Класс апдейтера
SV.>>class Antivirus.Core.Application; // Класс "независимого компонента, который вообще ничего не знает об апдейтере".
SV.>>
T>Прекрасно, а теперь посмотрим на все это с несколько другой точки зрения, а именно с кода апдейтера. Допусти есть не только апдейтер антивируса, но и апдейтер чего-то еще, есть базовый функционал для всех апдейтеров, есть планировщик задач апдейтера — то есть куча классов, у которых друг с другом довольно высокие cohesion и coupling. В предложенном тобой решении эти классы оказываются сильно разбросаны по иерархии и находятся друг от друга далеко. В этом и проблема.
Как раз про это я хотел написать, и даже начал:
T>и тесно связан с общим кодом обновления вместе с другими апдейтерами
Забыл добавить. Вот эта тесная связь для меня не является основанием помещать наш апдейтер в неймспейс "с другими апдейтерами".
В википедии написано, что неймспейс — это контекст для идентификаторов, и я с этим горячо согласен. Контекст, как я понимаю, служит для разрешения конфликтов и для функциональной классификации (апдейтер в контексте антивируса — функциональная часть антивируса). Спрашивается, причем здесь cohesion и coupling? Давайте рассуждать методом доведения до абсурда. string используется во множестве классов. Лежит, однако, в System. В этом тоже какая-то проблема? Допустим, вы делаете свой string, какой-нибудь... ммм... с автопереводом. Он активно использует System.String для хранения и обработки. Будете ли вы помещать его тоже в System? Технических препятствий-то нет. Студия по рукам не надает.
Апдейтер антивируса, не смотря на связность и связанность с другими апдейтерами, имеет антивирусный контекст.
Что касается связности и связанности. Это метрики, которые существуют объективно. Чтобы смотреть на них, можно использовать тул, который строит диаграммы, таблицы и пр. Объективные метрики не должны быть продублированы субъективными маркерами во имя непротиворечивости. Функциональная классификация, напротив — исключительно субъективна. Из кода ее не вывести.
>Если я правильно понял топик стартера, то он бы хотел в одном случае посмотреть на иерархию кода, который так или иначе связан с обновлением (какие есть классы, какие зависимости между ними, у кого какая ответственность и так далее), а в другом случае он бы хотел посмотреть на иерархию кода, который так или иначе связан с антивирусом. И в обоих случаях ему было бы удобно работать с разными иерархиями.
Это решительно невозможно обсуждать без примеров. То, что было приведено — не примеры, а так, заготовка для болтологии. Если у нас три класса — базовый универсальный Updater, специализированный AntivirusUpdater, и Application, я уже написал, как бы я сделал:
class System.Updating.BaseUpdaterService; // Базовый класс апдейтера из какого-то фреймворка.class Antivirus.Updater.AntivirusUpdaterService : System.Updating.BaseUpdaterService; // Специализированный класс апдейтера.class Antivirus.Core.Application : System.Application; // Класс "независимого компонента, который вообще ничего не знает об апдейтере".
Соответственно, если вы не согласны, или покажите перепаковку этих трех классов, или добавьте в пример новые классы, которые покажут не... кузявость единственной неймспейсовской иерархии.
Здравствуйте, jazzer, Вы писали:
S>>2. Доступ. Как объяснить компилятору, из какого фасета брать классы. Тем более, что не очень понятно, то ли имеется в виду "всё из antivirus, плюс всё из db", или "всё из пересечения antivirus и bd". J>Что значит — брать?
Значит выполнять name lookup. Вот я пишу Console.WriteLine(). Какой Console имеется в виду? Из System.*?
S>>3. Как избежать взрыва мозга. J>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Мне неизвестен язык программирования, который бы позволил задать правило "в функциональном неймспейсе могут быть только поднеймспейсы bl, db, и ui". Или хоть как-то гарантировать, что вместо antispam.db не появится какой-нибудь antispam.database, сломав стройную структуру таблички.
В С++ можно вместо неймспейсов пользоваться просто структурами и членами-типами в них. А в структурах как раз можно все это гарантировать.
Здравствуйте, Sinclair, Вы писали:
S>>>2. Доступ. Как объяснить компилятору, из какого фасета брать классы. Тем более, что не очень понятно, то ли имеется в виду "всё из antivirus, плюс всё из db", или "всё из пересечения antivirus и bd". J>>Что значит — брать? S>Значит выполнять name lookup. Вот я пишу Console.WriteLine(). Какой Console имеется в виду? Из System.*?
Ну информация же о фасете/ах откуда-то все же доступна? Или вообще все с потолка?
Здравствуйте, jazzer, Вы писали:
J>Ну информация же о фасете/ах откуда-то все же доступна? Или вообще все с потолка?
Понятно, что доступна. Как ей пользоваться?
В случае плоского дерева я пишу
using Namespace.Subnamespace;
и в область видимости влетает всё, что нужно.
В случае фасетов что будет происходить если я пишу
using Updater;
using Antivirus;
? Войдут ли в область видимости все классы из Updater (включая AntispamUpdater), все классы из Antivirus (включая AntivirusChecker) или только AntivirusUpdater и все остальные из обоих фасетов?
Или надо писать
using Updater & Antivirus;
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, jazzer, Вы писали:
J>>Ну информация же о фасете/ах откуда-то все же доступна? Или вообще все с потолка? S>Понятно, что доступна. Как ей пользоваться? S>В случае плоского дерева я пишу S>
S>using Namespace.Subnamespace;
S>
S>и в область видимости влетает всё, что нужно.
А, так тебе нужно, чтоб именно using работал
Мне вот вполне явного указания фасета хватит.
Здравствуйте, jazzer, Вы писали:
S>>и в область видимости влетает всё, что нужно. J>А, так тебе нужно, чтоб именно using работал J>Мне вот вполне явного указания фасета хватит.
И как именно это будет выглядеть?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.