Принцип единственной ответственности: глубокое погружение

    Про принцип единственной ответственности (The Single Responsibility Principle, SRP) уже было написано множество статей. В большинстве из них даётся лишь поверхностное его описание мало чем отличающееся от информации в википедии. А те немногие статьи что затрагивают ключевые особенности SRP делают это вскользь, не акцентируя на них внимания и не развивая тему дальше.

    Эта статья — попытка дать более глубокое объяснение принципу единственной ответственности, а также показать как его всё таки можно применять на практике. Кому интересно — добро пожаловать под кат.

    Уровень 0


    Первоначально, автор принципа — Дядя Боб, дал ему такую формулировку:
    A class should have one, and only one, reason to change.
    Класс должен иметь лишь одну причину для изменений.
    Это наиболее часто цитируемое определение принципа и оно крайне туманно. Проблема в том, что никто точно не понимает, что есть причина для изменений. Отсюда возникают различные субъективные трактовки «причины», а также стойкое ощущение того, что применение принципа единственной ответственности в разработке ПО это удел матёрых архитекторов и вообще что-то из области искусства. А ведь применение принципов проектирования на практике это именно то, что хочется в первую очередь освоить в процессе их изучения.

    Рассмотрим следующий пример класса и попробуем оценить (субъективно конечно же) соответствует ли он SRP согласно определению выше:

    Примечание: весь код в статье не высосан из пальца, а взят из реальных проектов автора.

    class SelectQuery
    {
        public function __construct(QueryExecutor $executor) {...}
    
        public function select($column, $alias = null): self {...}
        public function from($table, $alias = null): self {...}
        public function join(string $type, $table, $conditions = null): self {...}
        public function where($column, $operator = null, $value = null): self {...}
        public function orderBy($column, $order = null): self {...}
        public function groupBy($column, $order = null): self {...}
        public function limit(?int $limit): self {...}
        public function offset(?int $offset): self {...}
        public function build(): string {...}
    
        public function rows(): array {...}
        public function row(): array {...}
        public function column(): array {...}
        public function scalar() {...}
        public function count() {...}
    }
    

    Как нетрудно догадаться этот класс умеет строить SELECT запрос и выполнять его. Сразу же бросается в глаза что у класса есть две группы методов: методы для генерации SQL кода и методы для выполнения этого кода. А значит у нас могут появиться две разных причины для изменения этого класса. Например, мы можем добавить новый метод having() для генерации HAVING условий. Это изменение никак не затронет методы выполняющие запрос. С другой стороны, мы можем добавить метод groupByKey() позволяющий группировать извлечённые данные по ключу и это в свою очередь никак не скажется на первой группе методов.

    Налицо нарушение SRP. Решить проблему можно было бы, например, разделением класса на два:

    class SelectQuery
    {
        public function select($column, $alias = null): self {...}
        public function from($table, $alias = null): self {...}
        public function join(string $type, $table, $conditions = null): self {...}
        public function where($column, $operator = null, $value = null): self {...}
        public function orderBy($column, $order = null): self {...}
        public function groupBy($column, $order = null): self {...}
        public function limit(?int $limit): self {...}
        public function offset(?int $offset): self {...}
        public function build(): string {...}
    }
    
    class ExecutableSelectQuery extends SelectQuery
    {
        public function __construct(QueryExecutor $executor) {...}
    
        public function rows(): array {...}
        public function row(): array {...}
        public function column(): array {...}
        public function scalar() {...}
        public function count() {...}
    }
    

    Теперь вроде всё в порядке. Каждый класс делает ровно одну вещь и имеет ровно одну причину для изменений.

    А вот другой пример:

    @Transactional
    class SearchService {
        public List<Athlete> searchAthlete(SearchCriteria criteria)
        public List<User> searchUser(SearchCriteria criteria)
        public List<Association> searchAssociation(SearchCriteria criteria)
        public List<Orgaization> searchOrganization(SearchCriteria criteria)
        public List<Club> searchClub(SearchCriteria criteria)
        public List<Team> searchTeam(SearchCriteria criteria)
        public List<Game> searchGame(SearchCriteria criteria)
    }
    

    Как следует из названия класс используется для поиска различных сущностей и больше ничего не делает, а значит соответствует SRP. Ведь так?

    А как вам такой пример:

    class ActiveRecord
    {
        private $properties = [];
        
        public function __get($property) {...}
        public function __set($property, $value) {...}
    
        public function create() {...}
        public function update() {...}
        public function delete() {...}
        public function read() {...}
    }
    

    Ну ActiveRecord известный антипатерн. Он точно нарушает SRP. Или нет?

    На самом деле во всех этих примерах нарушение или не нарушение SRP зависит от того, что мы будем считать причиной для изменений. При этом, варьируя широту этого понятия мы сможем обосновать как создание классов содержащих ровно по одному методу (из одной строки кода), так и создание «божественных объектов». А значит озвученное выше определение принципа единственной ответственности бесполезно.

    Уровень -1


    По видимому, Дядя Боб в какой-то момент пришёл к тому же выводу и поэтому в своей недавней книге Clean Architecture даёт новое определение этого принципа:
    A module should be responsible to one, and only one, actor.
    Модуль должен отвечать перед одним и только одним актором.
    Под актором здесь понимается группа, состоящая из одного или нескольких лиц, желающих изменения поведения программного модуля.
    Это определение гораздо лучше предыдущего, т.к., во-первых, оно акцентирует внимание не на классе, а на модуле, что явно указывает на то, что SRP применим к любой части системы и не зависит от используемой парадигмы программирования. Во-вторых, из него следует важное практическое следствие:
    Для определения соответствия программного модуля принципу единственной ответственности не всегда достаточно изучения лишь кода этого модуля. Требуется изучить также как этот модуль используется во внешнем по отношению к нему коде.
    Иными словами, каждый актор определяет контекст использования модуля или, что тоже самое, набор сценариев использования модуля. Это может показаться неочевидным сразу, но каждый сценарий использования модуля отражает чьё-либо желание зафиксированное в коде.

    Докажем это
    Каждый актор, или что тоже самое, группа лиц желающих изменения поведения модуля выступает в роли заказчика этого изменения. Чтобы изменение не было бесполезным оно должно как-то и где-то проявляться. Единственный способ это сделать это задействовать это изменение в одном или нескольких сценариях использования модуля. Если же изменение поведения программного модуля никак не проявляет себя в сценариях использования, то это означает одно из двух: либо это изменение не касается поведения модуля (например, рефакторинг приватных методов класса), а значит не относится к SRP вообще, либо оно сделано «на перспективу», что напрямую нарушает принцип YAGNI и следовательно такое изменение бесполезно.

    Оценим с этой точки зрения наш первый пример — класс SelectQuery. Объективно он может использоваться в двух контекстах (т.е. у него есть два актора): генерация SQL кода и выполнение этого кода. Если у нас есть оба этих актора, т.е. если класс в каких-то сценариях используется только для генерации SQL, а в каких-то для выполнения запросов, то первая реализация класса не соответствует SRP. Если же у нас лишь один актор (как в проекте автора) — контекст выполнения запросов, то тогда исходная реализация не противоречит SRP. Действительно, в этом случае с точки зрения применения SelectQuery не будет никакой разницы содержится ли весь нужный функционал для построения и выполнения запросов в одном классе или в двух связанных классах. Сценарий использования будет всегда одним и тем же — строим запрос и потом выполняем:

    $rows = (new SelectQuery(new QueryExecutorImpl()))
        ->select(...)
        ->from(...)
        ->where(...)
        ->rows();
    

    Заметим, что классы из второй реализации SelectQuery не противоречат принципу единственной ответственности как для одного так и для двух акторов. А значит в случае одного актора отвечающего за выполнение запросов обе реализации эквивалентны с точки зрения следования SRP.

    Что на счёт следующего класса SearchService. Одного кода класса недостаточно. Во-первых, неизвестно, что представляют собой сущности Athlete, Association и т.д. А во-вторых, неясен контекст использования класса. Например, может быть такой вариант что сущности это простые DTO, а контекст использования поиск различимых, но почти однородных данных. Тогда этот класс имеет одного актора и следовательно удовлетворяет SRP. Но если возвращаемые сущности это бизнес сущности, а методы класса используются в разных контекстах соответствующих каждой бизнес сущности (как в проекте автора), то тогда класс имеет кучу акторов, по одному на каждый метод, и следовательно жёстко нарушает принцип единственной ответственности.

    В каких контекстах может использоваться наш ActiveRecord? На самом деле только в одном — сохранение записи в источнике данных. Никак иначе использовать этот класс нельзя. Таким образом, ActiveRecord прекрасно согласуется с SRP.

    Для тех кто не верит
    Приведённая выше реализация ActiveRecord действительно не нарушает SRP (кто думает иначе, приведите пример использования отличный от сохранение данных в источнике данных). Однако в оригинальном определении этого патерна заложено нарушение SRP: как минимум два контекста — бизнес логика и взаимодействие с источником данных.


    Постойте! Что мы вообще делаем? На каком основании мы выделяем контексты (акторов) использования модуля. Каков критерий разграничения акторов? Чем это принципиально отличается от «причины для изменений»? Собственно ничем. Да, мы теперь знаем, что нужно изучать то как мы используем программные модули, но в остальном трактовка принципа единственной ответственности в его актуальной формулировке всё также субъективна и находится в прямой зависимости от опыта и интуиции разработчика. Хотелось бы какого-то более формального подхода к проблеме, что-нибудь померить и посчитать в конце концов. Что ж, такой подход есть.

    Уровень -2


    Принцип единственной ответственности возник как попытка объединения двух важных понятий структурного анализа и проектирования: coupling (сопряжение, зацепление, связанность) и cohesion (сплочённость, связность, прочность модуля).

    Как это было


    Оба понятия неформально определяются следующим образом:
    Coupling — степень взаимозависимости между программными модулями.
    Cohesion — степень взаимозависимости между структурными составляющими программного модуля (подмодулями).
    Введём также определение связи и зависимости между программными модулями:
    Модуль A связан с модулем B, или что тоже самое, модуль B связан с модулем А, только тогда когда существует передача информации между ними.
    Модуль A зависит от модуля B, а модуль B не зависит от A, если изменения в спецификации А никогда не приведут к изменениям кода в B, а изменения в спецификации B могут приводить к изменениям кода в A.
    Где под спецификацией модуля понимается некоторая его часть доступная для прямого обращения извне (программный интерфейс модуля). Существует также альтернативная формулировка:
    Модуль A зависит от модуля B, а модуль B не зависит от A, если удаление модуля B приводит к нарушениям в работе A.
    Таким образом программные модули могут быть связанными, но при этом независимыми. При этом несвязанные модули всегда независимы. Чего не скажешь про зависимые модули, они всегда связанны. Заметим, что информация (например, вызов метода, класса, обращение к переменной, отправка сообщения и т.д.) может передаваться как от зависимого модуля к независимому так и наоборот. Обратите также внимание на то, что связность это по сути то же самое что и сопряжение, но применительно к подмодулям рассматриваемых модулей. При этом в хорошо спроектированной системе низкое сопряжение между модулями подразумевает высокую связность внутри этих модулей (смотри, например, GRASP).

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


    В стандарте ISO/IEC/IEEE 24765-2010 определены (неформально) следующие виды связности и сопряжения (от худшего к лучшему):

    • Связность
      1. Случайная (coincidental)
        Подмодули модуля в этом случае никак не взаимодействуют друг с другом и выполняют функционально не связанные задачи. Примером такой связности может быть весь тот код, который часто приводят для демонстрации нарушения принципа единственной ответственности:

        class UserManager
        {
            public function writeToFile(...) {...}
            public function calculatePaymentAmount(...) {...}
            public function authenticateUser(...) {...}
        }
        

      2. Логическая (logical)

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

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

        class ExceptionProcessor
        {
            public function logException(Exception $e) {...}
            public function notifyAdminAboutException(Exception $e) {...}
            public function showFormattedException(Exception $e) {...}
        }
        
        // Далее, где-то в коде
        $exceptionProcessor = new ExceptionProcessor();
        ...
        try {
        ...
        } catch (Exception $e) {
            $exceptionProcessor->logException($e);
            $exceptionProcessor->notifyAdminAboutException($e);
            $exceptionProcessor->showException($e);
        }
        

      4. Процедурная (procedural)

        Тип связности при котором подмодули объединены в модуль по причине их совместного использования в некоторый момент времени выполнения программы. Обращение к модулям происходит в определённом порядке. Подмодули могут быть функционально связаны между собой.

        class Concole
        {
            public static function hasColorSupport(): bool {...}
            public static function highlight(string $text): string {...}
        }
        
        // Далее, где-то в коде
        $consoleText = '...';
        if (Console::hasColorSupport()) {
            $consoleText = Console::highlight($consoleText);
        }
        $this->write($consoleText);
        

      5. Коммуникационная/информационная (communicational/informational)

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

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

        class ExpressionExecutor
        {
            public function execute(string $expression) {
                $ast = $this->parse(
                    $this->lexer(
                        $this->characterIterator($expression)
                    )
                );
                return $ast->evaluate();
            }
        
            protected function parse(Lexer $lexer) {...}
            protected function lexer(Iterator $iterator): Lexer {...}
            protected function characterIterator($expression): Iterator {...} 
        }
        

      7. Функциональная (functional)

        Все подмодули модуля функционально связаны между собой и выполняют одну хорошо определённую задачу. Этот тип связности прямая противоположность случайной связности.

        class Lexer
        {
            public function __construct(CharacterIterator $charIterator) {...}
            public function getTokens(): Generator {...}
        }
        

    • Сопряжение

      1. Патологическое (pathological)
        Программный модуль оказывает влияние или зависит от внутренней реализации другого модуля. Как правило, этот тип зацепления связан с нарушением принципа сокрытия информации (information hiding).

        class Hash:
            ...
            def hashOfText(self, text):
                self.__hashFromCache(text, self.__textHashCache)
        
            def __hashFromCache(text, cache):
                ...
        
        class OrderId:
            ...
            def hash(self):
                return Hash()._Hash__hashFromCache(self.__id, dict()) 
        

      2. По содержимому (content)

        Часть или все содержимое одного программного модуля включены в содержимое другого модуля. Примером такого зацепления могут служить вложенные или анонимные классы.

        public class BitSet implements Cloneable, java.io.Serializable {
            ...
            public IntStream stream() {
                class BitSetSpliterator implements Spliterator.OfInt {
                    ...
                }
                return StreamSupport.intStream(
                    new BitSetSpliterator(0, -1, 0, true), 
                    false
                );
            }
        }
        

        class EventPublisher
        {
            private $subscribers = [];
        
            public function __construct()
            {
                $this->subscribers[] = new EventSubscriberContract {
                    public function handle(Event $event)
                    {
                        ...
                    }
                }
            }
        }
        

      3. По общей области данных (common, common-environment)

        Два или более модулей совместно используют общую область данных (глобальную по отношению к модулям).

        $board = [];
        
        class Bot implements Player
        {
            ...
            public function move()
            {
                global $board;
                ...
                if ($board[$x][$y] === $this->shape['o']) {
                    ...
                }
                ...
                $board[$x][$y] = $this->shape['x'];
                ...
            }
        }
        
        class Game
        {
            private $player1;
            private $player2;
        
            public function __construct(Player $player1, Player $player2)
            {
                $this->player1 = $player1;
                $this->player2 = $player2;
            }
        
            public funciton next(): bool
            {
                $this->player1->move();
                if ($this->isGameOver()) {
                    return true;
                }
                $this->player2->move();
                if ($this->isGameOver()) {
                    return true;
                }
                return false;
            }
        
            public function isGameOver(): bool
            {
                global $board;
                if ($board[$x][$y] === 'x' || ... ) {
                    return true;
                }
                return false;
            }
        }
        

      4. Смешанное (hybrid)

        Различные подмножества значений некоторого элемента данных используются в нескольких программных модулях для разных и несвязанных целей. Тут можно придумать такой искусственный пример: есть некоторый DTO содержащий данные потребляемыми двумя совершенно разными модулями.
      5. По управлению (control coupling)

        Один модуль взаимодействует с другим модулем с целью повлиять на его поведение путём передачи ему управляющей информации.

        class SortedCollection
        {
            private $items;
            private $comparator;
        
            public function __construct(array $items, callable $comparator) {
                $this->items = $items;
                $this->comparator = $comparator;
            }
        
            public function items(): array
            {
                $this->sort();
                return $this->items;
            }
        
            private function sort(): void
            {
                usort($this->items, $this->comparator);
            }
        }
        ...
        $collection = new SortedCollection([3, 5, 2, 1], function (int $a, int $b) {
            return $a <=> $b;
        });
        $sortedItems = $collection->items();
        

      6. По данным (data)

        Данные одного программного модуля поступают на вход другого модуля.

        class UserController
        {
            public function userDetails(string $userId, UserQueryService $service)
            {
                return $service->getUserDetails($userId);
            }
        }
        


    Где-то с середины 70х годов прошлого века существуют и продолжают разрабатываться метрики для измерения как вышеперечисленных видов связности и сопряжения, так и их новых разновидностей.
    Несмотря на обилие метрик, не существует какого-то единственного способа для вычисления сопряжения и связности модулей. Отчасти это связано с отсутствием строгой формализации этих понятий. С другой стороны разновидностей связей между программными модулями великое множество. Какие-то связи считаются «полезными», а какие-то «вредными» (зависимости). И какую связь какой считать во многом зависит от того на каком языке программирования написана программа, какая парадигма программирования использовалась или в каком архитектурном стиле велась разработка.

    Уровень — 3


    Тем не менее не всё так плохо. Существуют интегральные метрики позволяющие объединить всё, что мы считаем «полезным» в отношении связей и рассчитать связность и сопряжение для всей системы. Один из таких подходов описан в Measuring Software Coupling. Хотя этот метод предлагается использовать для вычисления сопряжения, он также подходит и для вычисления связности модулей.

    Суть метода заключается в следующем: необходимо составить список того, что мы считаем зависимостями, далее по формулам рассчитываем связность или сопряжение. Расчёт будет настолько точным насколько адекватным и полным будет наш список видов зависимостей. Из-за диалектической природы сопряжения и связности списки зависимостей для них могут быть противоположными.

    Описание алгоритма расчёта

    1. Составляем матрицу описания связей для m модулей связанных посредством n видов связей:

      $\quad \begin{pmatrix} с_{11} & с_{12} & ... & с_{1n} \\ с_{21} & с_{22} & ... & с_{2n} \\ ... & ... & ... & ... \\ с_{m1} & с_{m2} & ... & с_{mn} \end{pmatrix} \quad$

      где $ с_{ij}$ число характеризующее степень влияния вида связи j на модуль i. Чем больше это число тем сильнее модуль i связан с какими-то другими модулями через связь j. Если $ с_{ij}$ равен нулю, то это означает что вид связи j не относится к модулю i.

      Следует заметить, что матрица описаний применима только к модулям граф зависимостей которых связный, т.е. есть существует путь передачи информации из любого модуля в любой другой модуль. Или другими словами в матрице описаний не должно быть строк или колонок состоящих из одних нулей.
    2. Вычисляем матрицу зависимостей:

      $\begin{pmatrix} d_{11} & d_{12} & ... & d_{1m} \\ d_{21} & d_{22} & ... & d_{2m} \\ ... & ... & ... & ... \\ d_{m1} & d_{m2} & ... & d_{mm} \end{pmatrix}$

      по следующим формулам:

      $d_{ij} = \left( \sum_{k=1}^{n} c_{ik} \times c_{jk} \times \beta_k \right) \Bigg/ \alpha_i$

      $\alpha_i = \sum_{j=1}^n c_{ij}$

      $\beta_k = 1 \Big/ \sum_{i=1}^m c_{ik}$


      где $ \alpha_i $ — сумма всех элементов i-й строки матрицы описаний, а $ \beta_k $ — величина обратная сумме элементов k-го столбца матрицы связей.
    3. Анализируем получившуюся матрицу зависимостей. При этом имеем:
      • $ 1 \ge d_{ij} \ge 0 $, для всех i и j.
      • $ d_{ij} $ — степень зависимости между модулями i и j, если $ i \ne j $.
      • $ d_{ij} $ — степень независимости модуля i от остальных модулей, если $ i = j $.
      • $ d_i = 1 - d_{ii} $ — зависимость модуля от других модулей.
      • $ D = \sum_{i=1}^m d_i $ — общая степень взаимозависимости модулей.
      • $ D_{avg} = D / m $ — средняя степень взаимозависимости модулей.


    Пользуясь вышеописанным алгоритмом попробуем рассчитать сопряжение и связность. Для примера возьмём код на PHP:

    <?php
    
    class User {...}
    
    class Chat {...}
    
    class ChatSession {...}
    
    interface UserRepository
    {
       public function getMostLowLoadedOperator(): ?User;
    }
    
    interface ChatRepository
    {
        public function chatFrom($chatId): Chat;
    }
    
    interface ChatSessionRepository
    {
        public function sessionFrom($sessionId): ChatSession;
    
        public function save(ChatSession $session): void;
    }
    
    interface BotService {...}
    
    class ChatSessionApplicationService extends AbstractApplicationService
    {
        public function __construct(
            ChatSessionRepository $sessionRepository,
    	ChatRepository $chatRepository,
    	UserRepository $userRepository,
    	BotService $botService,
            UserAccessService $accessService
        ) {
            ...
        }
    
        public function startSession(StartSessionCommand $command): string
        {
            // Используются Chat, ChatRepository, ChatSession, ChatSessionRepository,
            // BotService и функционал из AbstractApplicationService
            ...
        }
    
        public function switchChatSessionToFreeOperator($sessionId): void
        {
            // Используются ChatSession, ChatSessionRepository, User и UserRepository 
            // и функционал из AbstractApplicationService
            ...
        }
    
        public function changeName($sessionId, string $name): void
        {
            // Используются ChatSession, ChatSessionRepository и UserAccessService
            ...
        }
    }
    

    Прежде чем рассчитывать сопряжение составим список возможных связей между классами в PHP. Каждому типу связи присвоим вес в диапазоне от 0 до 1 (0 — соответствует отсутствию связи, 1 — соответствует максимально «жёсткой» связи).
    Вид связи Вес связи
    Класс наследует не абстрактный класс или использует каким либо образом не абстрактный класс. 1.0
    Класс использует трейт. 0.8
    Класс наследует абстрактный класс или использует каким либо образом абстрактный класс. 0.6
    Класс реализует интерфейс или использует каким либо образом интерфейс. 0.4
    Класс взаимодействует с другим классом через посредника — функцию или другой класс или используется каким-либо образом другим классом будучи при этом независимым от него. 0.2
    Класс взаимодействует с другим классом через несколько посредников или не взаимодействует вовсе. 0.0
    Приведённый список зависимостей не является исчерпывающим, а скорее показывает качественную картину: какая из зависимостей является более «жёсткой» по отношению к другой. Можно также спорить по поводу весов. Однако наша задача получить не точное значение сопряжения, а качественно оценить на сколько код плох или хорош в этом отношении.

    Итак, описание зависимостей есть, можно приступать к расчёту сопряжения. Всего у нас есть 11 модулей:

    1. $ m_1 $ — User
    2. $ m_2 $ — Chat
    3. $ m_3 $ — ChatSession
    4. $ m_4 $ — UserRepository
    5. $ m_5 $ — ChatRepository
    6. $ m_6 $ — ChatSessionRepository
    7. $ m_7 $ — ChatSessionApplicationService
    8. $ m_8 $ — AbstractApplicationService
    9. $ m_9 $ — BotService
    10. $ m_{10} $ — UserAccessService
    11. $ m_{11} $ — StartSessionCommand

    Составим матрицу описания связей, где $ c_{ij} $ будет определять степень зависимости между $ m_j $ и $ m_i $. Таким образом столбцы в матрице связей это веса рёбер графа зависимостей рассчитанные в прямом и обратном направлении:



    Матрица описания связей:
    $ \small m_7m_{11} $ $ \small m_7m_3 $ $ \small m_7m_6 $ $ \small m_6m_3 $ $ \small m_7m_5 $ $ \small m_5m_2 $ $ \small m_7m_2 $ $ \small m_7m_1 $ $ \small m_7m_4 $ $ \small m_4m_1 $ $ \small m_7m_{10} $ $ \small m_{10}m_1 $ $ \small m_7m_9 $ $ \small m_7m_8 $
    $ \small m_1 $ 0 0 0 0 0 0 0 1 0 1 0 1 0 0
    $ \small m_2 $ 0 0 0 0 0 1 1 0 0 0 0 0 0 0
    $ \small m_3 $ 0 1 0 1 0 0 0 0 0 0 0 0 0 0
    $ \small m_4 $ 0 0 0 0 0 0 0 0 0.4 0.2 0 0 0 0
    $ \small m_5 $ 0 0 0 0 0.4 0.2 0 0 0 0 0 0 0 0
    $ \small m_6 $ 0 0 0.4 0.2 0 0 0 0 0 0 0 0 0 0
    $ \small m_7 $ 0.2 0.2 0.2 0 0.2 0 0.2 0.2 0.2 0 0.2 0 0.2 0.2
    $ \small m_8 $ 0 0 0 0 0 0 0 0 0 0 0 0 0 0.6
    $ \small m_9 $ 0 0 0 0 0 0 0 0 0 0 0 0 1 0
    $ \small m_{10} $ 0 0 0 0 0 0 0 0 0 0 1 0.2 0 0
    $ \small m_{11} $ 1 0 0 0 0 0 0 0 0 0 0 0 0 0

    Выполняем расчёты и получаем матрицу зависимостей:

    $$display$$\begin{pmatrix} 0.833 & 0 & 0 & 0.056 & 0 & 0 & 0.056 & 0 & 0 & 0.056 & 0 \\ 0 & 0.833 & 0 & 0 & 0.083 & 0 & 0.083 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0.833 & 0 & 0 & 0.083 & 0.083 & 0 & 0 & 0 & 0 \\ 0.278 & 0 & 0 & 0.5 & 0 & 0 & 0.222 & 0 & 0 & 0 & 0 \\ 0 & 0.278 & 0 & 0 & 0.5 & 0 & 0.222 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0.278 & 0 & 0 & 0.5 & 0.222 & 0 & 0 & 0 & 0 \\ 0.083 & 0.083 & 0.083 & 0.067 & 0.067 & 0.067 & 0.225 & 0.075 & 0.083 & 0.083 & 0.083 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0.25 & 0.75 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0.167 & 0 & 0.833 & 0 & 0 \\ 0.139 & 0 & 0 & 0 & 0 & 0 & 0.139 & 0 & 0 & 0.722 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0.167 & 0 & 0 & 0 & 0.833 \end{pmatrix}$$display$$


    Общая зависимость классов $ D = 3.636 $, а средняя степень зависимости $ D_{avg} = 0.33 $
    Как и следовало ожидать, самым зависимым модулем оказался ChatSessionApplicationService, за ним следуют репозитории, UserAccessService, AbstractApplicationService и наконец все остальные классы. Средняя степень зависимости между классами небольшая, что говорит нам о том, что в основном классы слабо связанны между собой.

    Попробуем теперь вычислить связность ChatSessionApplicationService и понять на сколько он удовлетворяет SRP. Здесь также можно было бы составить иерархию видов связей внутри модуля (класса) с точки зрения связности его подмодулей (методов). Но поскольку в нашем случае методы внутри ChatSessionApplicationService связаны исключительно через зависимости класса условимся в целях упрощения расчётов считать использование конкретной зависимости методом за 1. Таким образом, строки матрицы описаний связей будут соответствовать методам класса, а столбцы — использованию конкретной зависимости:

    1. Метод использует UserRepository
    2. Метод использует ChatRepository
    3. Метод использует ChatSessionRepository
    4. Метод использует UserAccessService
    5. Метод использует BotService
    6. Метод использует AbstractApplicationService

    Матрица связей:

    $\begin{pmatrix} 0 & 1 & 1 & 0 & 1 & 1 \\ 1 & 0 & 1 & 0 & 0 & 1 \\ 0 & 0 & 1 & 1 & 0 & 0 \end{pmatrix}$


    Матрица зависимостей:

    $\begin{pmatrix} 0.708 & 0.208 & 0.083 \\ 0.278 & 0.611 & 0.111 \\ 0.167 & 0.167 & 0.667 \end{pmatrix}$


    Общая связность класса $ D = 1.014 $, а средняя степень связности методов $ D_{avg} = 0.338 $
    Сразу бросается в глаза низкая степень связности методов друг с другом. Об этом также свидетельствует средняя связность. При этом первый метод почти не связан с третьим, а второй в два раза сильнее связан с первым методом чем с третьим. Третий метод слабо связан с первыми двумя. Всё это означает, что ChatSessionApplicationService плохо соответствует SRP и, следовательно, должен быть разбит на три класса. К тому же такое разбиение только улучшит показатели сопряжения между классами (кто не верит — посчитайте сами) в силу слабой независимости сервиса с остальными классами.

    Дно?


    Едва ли. Исследования в области контроля качества и сложности программного кода ведутся до сих пор и останавливаться не собираются. А значит применение принципов управления сложностью программного кода, к которым в частности относится SRP, будет становиться всё более приближенным к стандартным инженерным практикам.

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 106

      +4

      Вы лучше найдите где дядя Боб берет траву под которой он все эти определения пишет. Или может у него исследования есть или теорема какая-то доказана? Все это игра слов — что такое «модуль», что такое «актор» и так о каждом слове в определении.


      Делать нужно как в нормальной науке. Выводим из эксперимента несколько величин. Даём модель, которая их связь описывает. Не пытаемся определить базовые понятия через друг друга, а говорим абстрактно об объектах, которые обладают измеримыми свойствами и поведением, которое можно показать экспериментально. Вот тогда будут получаться принципы, которые не нужно в каждой новой книге править и по поводу которых никто спорить не будет.

        +4

        Я думаю всё это будет, вопрос времени. Дядя Боб исходит из практического опыта коммерческой разработки. Его практический опыт начинается в 70-ых. Самим технологиям несколько десятков лет, по меркам серьёзных наук это немного. Мне кажется мы в этом вопросе находимся на уровне античных философов, которые эмперически выводили основные наблюдаемые связи в окружающей природе. А вы уже требуете подходов основанных Ньютоном 2000 лет спустя)

          0
          Делать нужно как в нормальной науке

          Делайте. Или приведите пример того, кто делает (в именно в дизайне).


          Если такого нет, то приходится интуитивно передавать знания при помощи того, кто это делает неясными определениями. Если уж другого нет.

            +1
            Делать нужно как в нормальной науке. Выводим из эксперимента несколько величин. Даём модель, которая их связь описывает.

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

              0
              Вот тогда будут получаться принципы, которые не нужно в каждой новой книге править
              SRP во всех книгах имеет одно значение, Бобу не понравилась конкретно формулировка, потому что многие неправильно ее трактовали.
                0

                Подождите, а как понять, что значение одно, если возможны разные трактовки?

                  +1
                  В данном случае, прочитать книгу самого автора (Clean Architecture), который прям так и пишет, что альтернативная трактовка — неверная.
                  Of all the SOLID principles, the Single Responsibility Principle (SRP) might be the least well understood. That’s likely because it has a particularly inappropriate name.
                  It is too easy for programmers to hear the name and then assume that it means that
                  every module should do just one thing.

                  Make no mistake, there is a principle like that. A function should do one, and only
                  one, thing. We use that principle when we are refactoring large functions into
                  smaller functions; we use it at the lowest levels. But it is not one of the SOLID
                  principles—it is not the SRP.

                    0
                    В данном случае, прочитать книгу самого автора (Clean Architecture), который прям так и пишет, что альтернативная трактовка — неверная.

                    конкретная альтернативная трактовка. Но не все альтернативные трактовки. А это, в свою очередь, возвращает нас к вопросу "как же понять, сколько значений у SRP".


                    (И это еще не вдаваясь в тот маленький нюанс, что мнение автора тоже может меняться.)

                      0
                      А это, в свою очередь, возвращает нас к вопросу «как же понять, сколько значений у SRP».
                      Приблизиться к пониманию можно используя примеры из этой же книги или статьи по ссылке в другом комментарии. Само собой, прям гарантированного способа не существует ввиду несовершенства способа передачи информации между людьми, но это не только с SRP так. Наверное есть даже посложнее случаи с пониманием терминов в ИТ, вспомнить тот же MVC.
                        0
                        Приблизиться к пониманию можно используя примеры из этой же книги или статьи по ссылке в другом комментарии.

                        Вот в том-то и дело, что "приблизиться". Мы опять попали в царство субъективных оценок.


                        Наверное есть даже посложнее случаи с пониманием терминов в ИТ, вспомнить тот же MVC.

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

                          +1
                          Почему бы?
                          Возможно программирование пока слишком молодая сфера. А возможно сказывается слишком большая свобода выбора. Одних только языков программирования больше сотни (более менее известных), и каждый предлагает инструменты такими, какими представляют их авторы. Соответственно в двух языках одна и та же по сути вещь может называться по разному, или разные вещи называться одинаково, или в одних могут быть вещи которых вообще нет в других, или вроде есть но с немного другим поведением. Соответственно и способы программирования на этих языках тоже отличаются, а дальше снежным комом. Построить на этом зоопарке однозначную и общую для всех терминологию очень сложно, а ведь это только верхушка айсберга. Сказывается и сложность доказательства формальной корректности программы, и влияние на программу и разработку требований реального мира которые сложно выразить формально. Как следствие, сложно определить даже базовые вещи, на которые должна полагаться все остальная теория. В итоге все принципы подразумевают постоянную необходимость совершения личного выбора разработчиками. Вот они и выбирают каждый по своему.
                            0
                            Построить на этом зоопарке однозначную и общую для всех терминологию очень сложно, а ведь это только верхушка айсберга.

                            Может быть, надо начинать с построения терминологии до открытия зоопарка?


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

                            Что, собственно, и приводит нас к проблеме подтверждаемости этих принципов.

                              0
                              Может быть, надо начинать с построения терминологии до открытия зоопарка?
                              Я только за. Но тогда придется перепилить весь существующщий зоопарк языков и инструментов, чтобы он соответствовал новой стандартизированной универсальной терминологии. Хотя на практике это скорей всего выльется в 101й по счету стандарт. А возможно и в несколько сразу, от каждого гиганта ИТ-индус-трии по одному.

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

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


                                Например, перестать использовать текст как внутреннее представление программного кода при хранении и редактировании.

                                … зачем?

                                  0
                                  Зачем перепиливать, если можно просто составить словарь?
                                  Потому что «каждый предлагает инструменты такими, какими представляют их авторы. Соответственно в двух языках одна и та же по сути вещь может называться по разному, или разные вещи называться одинаково, или в одних могут быть вещи которых вообще нет в других, или вроде есть но с немного другим поведением». В первых трех случах проблем в принципе нет, а вот в последнем появятся.

                                  зачем?
                                  Потому что текст это лишь человекочитаемое представление программы. Использование его в качестве основы для всех инструментов программирования вмешивает проблемы уровня текстового представления во все другие уровни, и на каждом соответствующий инструмент решает их заново. Разделение программы и ее представления позволило бы этого избежать, а заодно добавить новые возможности.
                                    0
                                    Соответственно в двух языках одна и та же по сути вещь может называться по разному

                                    Ну и что? В натуральных языках одна и та же вещь называется по-разному, но однако человечество не занимается перепиливанием всех языков под одну гребенку.


                                    Потому что текст это лишь человекочитаемое представление программы.

                                    Ну то есть лишь самое важное — с моей, конечно, точки зрения — в программе.


                                    вмешивает проблемы уровня текстового представления

                                    Это какие проблемы?


                                    Разделение программы и ее представления

                                    … давно есть — это разделение между исполняемой программой и ее исходным кодом.


                                    заодно добавить новые возможности.

                                    Какие?


                                    И да, вы забыли упомянуть недостатки отказа от текста.

                                      0
                                      Ну и что?
                                      Я же сказал, этот пункт не содержит проблемы. Проблему содержит пункт «вроде есть но с немного другим поведением».
                                      Ну то есть лишь самое важное
                                      Важное для программиста в момент чтения кода, но абсолютно бесполезное для внутренних алгоритмов работы инструментов.
                                      Это какие проблемы?
                                      Тысячи их. Обработка текста — медленно, открытие проекта — медленно, поиск по проекту — медленно, постоянный парсинг — медленно, повторный парсинг разными инструментами — кратно медленно, пересчет подстветки IDE — медленно, потребляемая память — высоко, толкование изменений кода — ужасно, код-стайл — холиварно, отступы — холиварно, окончания строк — холиварно, файловая структура проекта — холиварно, сцепления синтаксиса и ЯП — высоко, хороший синтаксис — сложно, написание парсеров — сложно, однозначность трактовки синтаксиса — плохо, толкование синтаксических ошибок — дно дна, количество языков которые не отличаются почти ничем кроме синтаксиса и имен — миллион. И это только первое что пришло в голову.
                                      давно есть — это разделение между исполняемой программой и ее исходным кодом
                                      Это не то о чем я говорю. Я имею ввиду что код на диске и во внутренних структурах IDE/компиляторов/анализаторов должен быть представлен графом. А текстовое представление должно генерироваться лишь по необходимости, когда программист читает или редактирует конкретные элементы программы.
                                      Какие?
                                      Возможность менять синтаксис, код-стайл, и все остальное на ходу, никак не мешая при этом соседнему программисту, который в это время редактирует этот же самый класс в IDE с поддержкой совместного редактирования. Возможность читать код на любом языке выучив один общий синтаксис и лишь доучивая новые фичи. Возможность легко эмулировать синтаксические фичи которые не поддерживает конкретный язык (например += или тернарный оператор). Возможность легко писать обобщенный код независимо от языка. Возможность автогенерации осмысленной истории изменений, т.е. воспринимающей и отображающей переименование как переименование, а не гадающее на кофейной гуще что есть что. Возможность легкой генерации/модификации программного кода. Возможность динамически упорядочивать код чтобы программист видел рядом все важное в конкретный момент времени. Возможность расширять и оптимизировать работу инструментов за счет информации которая не записывается в тексте проекта.

                                      В принципе эти фичи можно получить и сейчас. Но это будет очень сложно сделать и это будет крайне медленно работать, поэтому никто этого не делает.
                                      вы забыли упомянуть недостатки отказа от текста
                                      Невозможность делиться кодом через текстовые сообщения, невозможность редактировать программу вне специальных IDE. Необходимость наполовину переписать весь стек инструментов.
                                        0
                                        Проблему содержит пункт «вроде есть но с немного другим поведением».

                                        Это то же самое, что в естественных языках. И ничего, справляются.


                                        Важное для программиста в момент чтения кода

                                        … а именно этот момент мне и важен.


                                        Тысячи их.

                                        Все, что "медленно" — решаемо ресурсами. Все, что холиварно, не решаемо отменой текста — вы просто замените одни холивары на другие.


                                        количество языков которые не отличаются почти ничем кроме синтаксиса и имен — миллион.

                                        Это не проблема текста.


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

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


                                        Возможность менять синтаксис, код-стайл, и все остальное на ходу,

                                        Синтаксис на ходу менять не получится. Все остальное… а зачем?


                                        никак не мешая при этом соседнему программисту, который в это время редактирует этот же самый класс в IDE с поддержкой совместного редактирования.

                                        Для этого нужен semantic merge, отказываться от текста не нужно.


                                        Возможность читать код на любом языке выучив один общий синтаксис и лишь доучивая новые фичи.

                                        Для этого нужен один язык, один синтаксис. Вы не добьетесь этого отказом от текста, надо сначала этот один язык придумать.


                                        Все остальное есть — или возможно — и сейчас.


                                        Необходимость наполовину переписать весь стек инструментов.

                                        Полностью написать стек инструментов. И этот стек не будет совместим с соседним стеком, который работает с текстом (версионирование — самый яркие пример).


                                        Текст — это общий делитель, позволяющий как-то уравнять опыт работы с разными языками и подходами. А вы предлагаете заменить его на один язык.

                                          0
                                          Это то же самое, что в естественных языках. И ничего, справляются.
                                          Ну вот что-то не справились, как видим по текущей ситуации в ИТ.
                                          решаемо ресурсами
                                          Почти все решаемо ресурсами. Можно отказаться хоть от качества кода в пользу ресурсов. Это не значит что это хорошо.

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

                                          Ресурсов на создание стека затрачено меньше, скорость работы стека вышла значительно больше.
                                          Все, что холиварно, не решаемо отменой текста — вы просто замените одни холивары на другие.
                                          Я не говорил про отмену текста, я говорил про перенос работы с ним на отдельный уровень. Соотвественно, он не добавляет новых тем для холиваров, но делает менее важными старые темы, ведь теперь выбор каждого члена команды (например касаемо форматирования) трогает остальную команду не больше, чем выбор шрифта или темы в собственной IDE.
                                          Это не проблема текста.
                                          Не имеет значения, чья это проблема, если это решаемо отказом от переноса текста на другой уровень.
                                          что лишает программиста выразительного инструмента
                                          А что плохого в том, что каждый программист видит код таким, каким ему удобно?
                                          Синтаксис на ходу менять не получится.
                                          Почему нет? Если текст это только генерируемое по надобности представление графа, его можно хоть на блоксхему менять. Это как переключение локализации.
                                          Все остальное… а зачем?
                                          Потому что у каждого программиста есть свое мнение по поводу того как удобней читать код.
                                          Для этого нужен semantic merge, отказываться от текста не нужно.
                                          Не нужно, но при работе с кодом как с графом это будет сделать намного проще, и работать оно будет быстрее.
                                          Для этого нужен один язык, один синтаксис.
                                          Нет. Для этого нужен один набор чего-то вроде AST. Синтаксисов можно придумать сколько угодно: C-like, Python-like, Pascal-like, Rust-like и т.д. При этом поскольку за текстовым отображением будет стоять древовидная иерархия блоков, вещи вроде неоднозначностей или конфликтов парсинга в принципе невозможны, что дает полную свободу в придумывании удобного человеку синтаксиса. Не нравятся точки с запятой — пожалуйста, не нравятся круглые скобки в if () {} — без проблем.
                                          И этот стек не будет совместим с соседним стеком
                                          Этот стек может генерировать код для старых языков, с которыми может работать старый стек. Даже больше того, он может расширять возможности старого стека, например добавить более строгие проверки типов, проверять время жизни и освобождение объектов, эмулировать новые синтаксические конструкции, создавать темплейты для обобщенного кода, проводить оптимизацию уровня графа, а потом полученный граф транслировать хоть в C, хоть в JS, хоть в оба сразу.
                                          Текст — это общий делитель, позволяющий как-то уравнять опыт работы с разными языками и подходами. А вы предлагаете заменить его на один язык.
                                          Не, я предлагаю отделить отделить синтаксис от языка, и вынести все что его касается на уровень IDE. А общим делителем языков станет подобие AST, с которым намного проще работать внутренним алгоритмам инструментов, и которое намного «ровнее» между языками чем бесконечный зоопарк синтаксисов.

                                          Главная проблема в том, что никто не захочет уходить со старого стека в виду полной неизвестности нового. Это как раскладка DVORAK, на которой можно печатать проще и быстрей, но почти никто не пользуется потому что большинство привыкли к QWERTY, а потому ее и спрос и предложение.
                                            0
                                            Ну вот что-то не справились, как видим по текущей ситуации в ИТ.

                                            В ИТ пока и словаря нет, поэтому достаточно бесполезно сравнивать.


                                            Чтобы написать IDE для текстового кода нужно больше ресурсов чтобы IDE для графа.

                                            Это, прямо скажем, неправда.


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

                                            Вы так говорите, как будто этот "кусочек текста" не придется каждый раз отрисовывать из графа заново. С форматированием и прочим счастьем.


                                            Соотвественно, он не добавляет новых тем для холиваров,

                                            Добавляет. Какие узлы в графе использовать? Вот вам и холивар.


                                            если это решаемо отказом от переноса текста на другой уровень.

                                            Ну так не решаемо же.


                                            А что плохого в том, что каждый программист видит код таким, каким ему удобно?

                                            То, что он теряет интент другого программиста.


                                            Почему нет?

                                            Потому что разный синтаксис требует разного нижележащего графа. Не всегда, но часто.


                                            Для этого нужен один набор чего-то вроде AST.

                                            AST, тем не менее, привязан к синтаксису языка.


                                            вещи вроде синтаксических неоднозначностей или конфликтов в принципе невозможны,

                                            Они невозможны только если неоднозначностей нет ни в AST, ни в целевом синтаксисе.


                                            Не нравятся точки с запятой — пожалуйста, не нравятся круглые скобки в if () {} — без проблем.

                                            "Пожалуйста" — это в смысле "пожалуйста, напишите"?


                                            Этот стек может генерировать код для старых языков, с которыми может работать старый стек.

                                            А обратно как?


                                            Не, я предлагаю отделить отделить синтаксис от языка, и вынести все что его касается на уровень IDE

                                            … и заставить все IDE работать с вашим AST, да. Не, не выйдет.


                                            А общим делителем языков станет подобие AST, с которым намного проще работать внутренним алгоритмам инструментов, и которое намного «ровнее» между языками чем бесконечный зоопарк синтаксисов.

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

                                              0
                                              Это, прямо скажем, неправда.
                                              Почему? Половина кода которая нужна в текстовой IDE вообще не нужна в графовой, та которая нужна является более простой в реализации, а прям совсем нового немного и оно в основном тривиально.
                                              Вы так говорите, как будто этот «кусочек текста» не придется каждый раз отрисовывать из графа заново. С форматированием и прочим счастьем.
                                              Рендеринг сотен строк занимающих всю ширину экрана на каком нибудь медленном GDI методом в лоб само по себе занимает меньше 10мс, и это на средненькой машине. Выполнение операций над текстом достаточно долгое, чтобы современным IDE пришлось рисовать фейковое обновление текста перед выполнением всех этих операций, чтобы после их выполнения нарисовать все по честному. И это даже при вводе одного символа, а про пример с переименованием класса в проекте я вообще молчу, это может десятки секунд длиться.

                                              Граф всегда будет проще и быстрее текста, на любых операциях и в любых алгоритмах. Потому что:

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

                                              2) Локальность изменений. Операции над узлами графа обычно трогают только эти сами узлы. Вставка или удаления символа в среднем сдвигает половину содержимого буфера символов представляющего текстовый файл. Современные редакторы всячески откладывают это хитрыми структурами, но так или иначе это все равно приходится сделать, рано или поздно.

                                              3) Атомарность изменений. В графе можно вставить, удалить или изменить узел или ссылку. При этом эти операции нельзя недовыполнить. В тексте атомом является символ, который лишь часть какого-то изменения.

                                              4) Идентифицируемость изменений. Следствие из предыдущего. Любая операция над графом имеет четкое понятное значение — например элемент добавился, элемент удалился, изменилось имя объекта или значение литерала. Операции над текстом ортогональны значению вложенному в синтаксис ЯП. Кавычка закрывающая управляющую инструкцию легко метаморфирует в кавычку закрывающую класс, от того что добавился новый текст где-то выше.

                                              5) Согласованность. Граф всегда находится в каком-то корректном, осмысленном, однозначном состоянии, а почти любая операция на нем легко проверяется на непротиворечивость. В тексте же настоящий ад. Пользователь может добавить что угодно, куда угодно, и когда угодно, в том числе текстовым редактором вне самой IDE. Начиная от любых бессмысленных символов в любом месте и заканчивая вставкой цикла прямо в имя аргумента функции, т.е. вообще любой белиберды. И IDE ничего не может с этим сделать, даже внутри себя, потому что она не может определить какое изменение делает пользователь пока он не завершит его до какой-то корректной формы. И что хуже, IDE должна постоянно что-то делать с некорректным состоянием текста, додумывать намерения программиста, и как-то раскрашивать и отображать код.

                                              6) Семантика. Граф уже имеет семантическое значение, выступая в роли готового подобия AST. Чтобы определить семантическое значение текста (для подсветки, автоподстановки, поиска, анализа, компиляции), его нужно парсить, все равно приходя к AST, а потом еще и хранить к индексе (для поиска), и постоянно все это обновлять и согласовывать из текста. Т.е. выполнять очень сложную в реализации и медленную работу, которая в первом случае вообще не нуждается в выполнении, и только для того чтобы выполнять абсолютно те же самые функции. Прошу прощения за такую аналогию, но это похоже на удаление гланд через задний проход.
                                              Какие узлы в графе использовать?
                                              В моем представлении работы IDE такого вопроса не может возникнуть. Программист по прежнему видит код, ему не нужно думать об узлах. Просто маппинг будет направлен не от синтаксису к AST а от графа к синтаксису с подхватом изменений узлов.
                                              Ну так не решаемо же.
                                              Решаемо. Программист видит синтаксис, который является представлением универсального графа, которому все равно в какой язык он будет транслирован. Т.е. если два языка не имеют критических семантических отличий, которые не могут быть сэмулированы, они сводимы к одному одинаковому синтаксису. А как минимум часть неэмулируемых отличий сводятся к множеству-подмножеству синтаксиса. Таким образом человек может выучить наибольшее синтаксическое подмножество, и использовать все языки которые с ним совместимы. А даже если ему нужен несовместимый язык, он не учит вообще все с нуля, а доучивает лишь необходимую разницу.
                                              То, что он теряет интент другого программиста.
                                              Интент программиста выражен не синтаксисом, а значением под этим синтаксисом. А оно одинаково независимо от синтаксиса, потому что синтаксис лишь представление.

                                              При этом в VCS графовое представление наоборот лучше отражает интент. Если мы перенесем класс в другой файл, переименуем его и отрефакторим что в текстовом представлении это выглядит как удаление одного класса и добавление совсем другого. При графовом представлении читающий историю увидит переименование и рефакторинг. А переноса может вообще не быть, потому что классы принадлежат неймспейсам, а не файлам.
                                              Потому что разный синтаксис требует разного нижележащего графа.
                                              Так у нас же обратная зависимость. Граф всегда один.
                                              AST, тем не менее, привязан к синтаксису языка.
                                              Поэтому я и говорю «подобие AST». Просто подходящего термина сейчас вроде как нет. Не уверен, подходит ли «семантическое дерево».
                                              Они невозможны только если неоднозначностей нет ни в AST, ни в целевом синтаксисе.
                                              В данном случае они невозможны в принципе, потому что IDE никогда не пытается выводить структуру из символов. Даже если все без исключения инструкции отображаются одними только пробелами и переносами строк, IDE все равно знает что находится под каждым символом на экране. Само собой в таком крайнем случае о читабельности человеком речи не идет, но на то он и крайний случай.
                                              «Пожалуйста» — это в смысле «пожалуйста, напишите»?
                                              Написать не сложно, ведь правила генерации пишутся не сложнее чем локализация со строковой интерполяцией. Но я имел в виду другое. Если сделать подобное изменение в синтаксисе скажем С++, то это просто не сможет работать из-за заведомо известной неоднозначности. Я конечно не предлагаю делать именно такое изменение, но например лямбды в С++ сейчас выглядят совершенно уродскими, потому что его синтаксис загнал сам себя в угол.
                                              А обратно как?
                                              Распарсить код уже существующими парсерами, и привести полученное к графу.
                                              и заставить все IDE работать с вашим AST, да. Не, не выйдет.
                                              Я и не предлагаю переписывать существующие IDE. Я предлагаю в будущем создать новое поколение IDE. Не думаю что это хуже, чем создавать новые IDE на старом принципе, или писать плагины для кучи IDE для каждого нового языка. Да и уже сейчас есть применение подобного подхода с децентрализацией — Language Server Protocol.
                                                0

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


                                                Программист по прежнему видит код

                                                Что важнее, он не только видит код, но и пишет — код. Вы же не предлагаете ему каким-то образом напрямую редактировать графовое представление?


                                                А вот теперь, исходя из этого знания, посмотрим на все, что дальше.


                                                Почему?

                                                Потому что вам все равно нужно сделать преобразование из текста в граф (потому что программист пишет текст), и сделать его максимально оптимальным образом.


                                                Половина кода которая нужна в текстовой IDE вообще не нужна в графовой

                                                Это какая же?


                                                Рендеринг сотен строк занимающих всю ширину экрана на каком нибудь медленном GDI методом в лоб само по себе занимает меньше 10мс, и это на средненькой машине. Выполнение операций над текстом достаточно долгое

                                                Вы себе противоречите. Рендеринг — это и есть операция над текстом.


                                                Быстро можно отрендерить заранее готовый текстовый файл (сюрприз — это то, что делает текстовая IDE).


                                                Граф всегда будет проще и быстрее текста, на любых операциях и в любых алгоритмах.

                                                Докажите. В той части, которая на любых.


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

                                                И это значит, что при каждой отрисовке узла (например, переменной) вам надо сходить в узел и взять оттуда это имя. Сто упоминаний переменной на экране? Сто раз сходить и взять.


                                                Операции над узлами графа обычно трогают только эти сами узлы

                                                И, возвращаясь к примеру с сотней упоминаний переменной на экране, вам все равно надо будет отрисовать сто изменений при переименовании.


                                                Любая операция над графом имеет четкое понятное значение — например элемент добавился, элемент удалился, изменилось имя объекта или значение литерала.

                                                Операция — да. Но операции вы не храните, а при сравнении двух состояний графа совсем не так очевидно, что произошло.


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

                                                А теперь вспомним, что пользователь-то вводит текст. Что вы будете делать с графом, когда пользователь ввел какой-то текст в IDE? А если этот текст не имеет смысла?


                                                Т.е. выполнять очень сложную в реализации и медленную работу, которая в первом случае вообще не нуждается в выполнении

                                                Как это не нуждается, если пользователь вводит текст?


                                                В моем представлении работы IDE такого вопроса не может возникнуть. Программист по прежнему видит код, ему не нужно думать об узлах.

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


                                                Программист видит синтаксис, который является представлением универсального графа

                                                если вам удастся сделать такой универсальный граф. Что не доказано.


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

                                                А если имеют? Ну банально, язык, в котором есть исключения, пытаемся отобразить в язык, в котором исключений нет.


                                                Интент программиста выражен не синтаксисом, а значением под этим синтаксисом.

                                                Это неправда. Я, как программист, использую в том числе и синтаксис для выражения своего интента.


                                                Так у нас же обратная зависимость. Граф всегда один.

                                                Он один если вам удалось найти такой граф, который удовлетворяет всем синтаксисам.


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

                                                Вы забыли, что надо написать не только правила генерации (которые тоже сложнее интерполяции), но и правила парсинга.


                                                Распарсить код уже существующими парсерами, и привести полученное к графу.

                                                А если результат работы существующего парсера несовместим с вашим графом?


                                                Я и не предлагаю переписывать существующие IDE. Я предлагаю в будущем создать новое поколение IDE.

                                                А какая мне разница, если вашими стараниями я не смогу пользоваться той IDE, которая мне удобна?

                                                  0
                                                  Мне кажется, что все ключевое по этой теме прекрасно сформулировал Фаулер ещё в 2008 году — www.martinfowler.com/bliki/ProjectionalEditing.html

                                                  И ide построенная вокруг идеи универсального графа есть — www.jetbrains.com/ru-ru/mps/concepts/index.html#projection-editor

                                                  Вокруг неуниверсальных графов и их проекционного представления в виде текста (как наиболее универсального формата сериализации «графа») построены вообще все современные ide, компиляторы и статические анализаторы.
                                                    0
                                                    Вокруг неуниверсальных графов и их проекционного представления в виде текста (как наиболее универсального формата сериализации «графа») построены вообще все современные ide, компиляторы и статические анализаторы.

                                                    Собственно, что-то такое я уже и думал.

                                                      0
                                                      Вокруг неуниверсальных графов и их проекционного представления в виде текста
                                                      Это все-таки не совсем то, что я имею ввиду. Хотя моем видении представление визуально выглядит как текст, но на самом деле им не является. В действительности на экране находится дерево вложенных друг в друга виджетов, соответствующих узлам графа.

                                                      Т.е. есть например узел «while». У него есть поля «условие» и «тело». Есть настройки, описывающие правила генерации синтаксиса и форматирования. На основе этих настроек создается прототип виджета while и дополняется анализатор ввода.

                                                      Когда пользователь начинает печатать в пустом месте, в нем создается временный виджет, отображающий введенный текст. Когда пользователь наберет «while» (или раньше, через fuzzy-match), анализатор ввода распознает что это слово может обозначать узел, и IDE предложит пользователю подстановку. Пользователь может согласиться с подстановкой нажатием tab, или закончить слово пробелом. В этот момент в граф добаляется узел а в отображаемый код вставляется виджет «while () {}» с пустыми полями ввода указывающими на поля узла и переводит в них курсор. А дальше рекурсивно. Альтернативами являются навигация и редактирование кода с помощью vim-like комбинаций или мыши/тачскрина.

                                                      При подборе подстановки учитывается контекст. Т.е. нельзя добавить управляющую конструкцию в список аргументов или тело класса. Если набрать в них «while» будет отображено предупреждение о совпадении с ключевым словом текущего синтаксиса, но подстановка предложена не будет.

                                                      Пользователь может перемещаться курсором по любым отображаемым символам как по тексту, но виджет не разрешает вставлять символы там где их не должно быть (например между while и левой скобкой), или удалять обязательные символы (например левую скобку после while). Пользователь также не может нарушить форматирование, но может вставлять разделительные переносы строк для группировки кода, которые тоже являются узлами и виджетами. Пользователь может поставить курсор в слово «while» и изменить его, но изменение останется повисшим и будет подсвечено ошибкой пока пользователь не вернет его обратно или не введет имя узла с совместимыми типами полей (например в «if», где тоже тоже логическое выражение и тело). Виджет и узел удалится, если он полностью попадает в выделение принажатии delete, или при нажации специальной комбинации клавиш.

                                                      При редактировании имен в месте объявления сущностей редактируется напрямую поле имени соответствующего узла. При редактировании имен в других местах стратегия примерно как при редактивании ключевого слова «while», но дополнительно предлагается изменение имени в проекте.

                                                      Примерно так я это вижу. Т.е. ни на каком этапе код не находится в состоянии «текст отображаемого файла», и соответственно не требует парсинга в привычном его понимании. Обновление подсветки и форматирования производится виджетами на ходу во время редактирования их полей. При этом виджеты создаются лениво по необходимости в зависимости от близости к видимой области окна редактора. Единицей отображения в редакторе является не файл, а неймспейс/класс/функция или произвольный запрос по ним или их метаданным.
                                                        0

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

                                                          0
                                                          Все что вы описываете уже примерно так и работает, с поправкой на то, что когда вы редактируете имя переменной вы можете как и просто отредактировать текст в этом месте, так и отредактировать имя того что вы называете «виджетом» (Shift+F6 в популярных ide).
                                                        0
                                                        Вы же не предлагаете ему каким-то образом напрямую редактировать графовое представление?
                                                        Нет. Он пишет и читает код, но под видимым текстом лежит не типичный текстовый редактор, а подобие иерархии виджетов.
                                                        Потому что вам все равно нужно сделать преобразование из текста в граф
                                                        На самом деле нет. Нужно только определять тип нового вводимого узла, для чего в 99% случаев достаточно одного слова и автоподстановки, или vim-like комбинации клавиш, или программирования «мышью», как кому удобно.
                                                        Это какая же?
                                                        Например парсинг. Один раз для подсветки, второй раз для индексирования, третий раз для автоформатирования, четвертый раз всякими посторонними плагинами в хуках на редактирование, ну и в конце концов компилятором для подсветки ошибок. И все это должно быть написано одельно для каждого из языков которые поддерживает IDE. Иногда пара из этих этапов таки склеиваются или выполняются одной либой, и требутся всего 2-3 их написания и вызова.
                                                        Рендеринг — это и есть операция над текстом.
                                                        Под операцией я подразумевал операцию редактирования.
                                                        Докажите. В той части, которая на любых.
                                                        Текстовый код это и есть многократно избыточное, медленное и часть времени нарушенное представление абсолютного того же самого графа. К тому же требующее для некоторых операций медленной конвертации в него же.

                                                        Это то же самое что заменить в СУБД представление базы на текстовый SQL, и выполнять все CRUD операции путем редактирования этого текстового SQL, а все поисковые запросы его парсингом.

                                                        Да, я и не могу дать формальное доказательство. Но попробуйте вспомнить первые 20 операций редактирования/поиска которые вы чаще всего выполняете в IDE, и прикинуть что в это время выполняется под капотом. Все они в графовом представлении будут делать то же самое, но в намного меньшем объеме работы.
                                                        Сто упоминаний переменной на экране? Сто раз сходить и взять.
                                                        Во-первых это все еще микроскопически мало по времени, даже если на экране будет тысяча переменных. Во-вторых не нужно вытаскивать из графа имя переменной в каждом кадре отрисовки, лишь в момент когда она впервые появляется в видимой зоне или рядом с ней. В-третьих в текстовом представлении доставать значения придется ничуть не меньше. А в четвертых за счет «графовости» можно использовать такую оптимизацию рендеринга как атлас слов, при которой каждое слово отрендерится всего один раз, а дальше будет просто вклеиваться в области на экране, отрываясь по производительности от посимвольного текста еще дальше.
                                                        вам все равно надо будет отрисовать сто изменений при переименовании
                                                        Меньше миллисекунды на среднем ноуте десятилетней давности, т.е. на моем. Повторю еще раз — операция рендеринга текста на экран не является не то что боттлнеком, она незаметна невооруженному глазу даже если заполнять несколько полных экранов текста ежекадрово и вообще без оптимизаций.
                                                        Но операции вы не храните
                                                        В контексте перехода на графовый подход их будет хранить VCS. Т.е. сейчас вы редактируете в IDE текст, ждете N секунд не внося изменений (или сохраняете файл), IDE запускает VCS на сканирование всего проекта или в лучшем случае файла. Ожидание в N секунд нужно как раз потому, что иначе вы будете видеть слайдшоу при работе с текстом. В случае с графовым предсавлением IDE нужно будет передать VCS лишь короткую информацию об конкретно изменении, что произойдет мгновенно. Даже не нужно будет ждать.
                                                        А если этот текст не имеет смысла?
                                                        Этот текст будет представлен узлом чистого текста в процессе редактирования. Т.е. весь бред будет локализован в одном месте редактирования, которое будет подсвечено и обведено как ошибка. Без какой либо порчи любых узлов до и после этого.
                                                        Как это не нуждается, если пользователь вводит текст?
                                                        Ему кажется что он вводит текст. Но на самом деле например когда он удаляет символ посреди имени переменной он редактирует напрямую поле имени в узле этой переменной.
                                                        Во-первых, это не отменяет срача при проектировании самого графа с его узлами
                                                        Хорошо, что такой срач и так есть, и ничего нового по сути не добавляется.
                                                        А во-вторых, и в более важных, если вы считаете, что ваш граф позволит вам сделать ровно один вариант узла для одной операции пользователя, вы глубоко ошибаетесь.
                                                        Можно пример?
                                                        А если имеют? Ну банально, язык, в котором есть исключения, пытаемся отобразить в язык, в котором исключений нет.
                                                        Пользователь просто доучит новую/отличающуюся часть. Т.е. в вашем примере в первом случае у нас будет синтаксис чистого C, а во втором он же но с «try {} catch () {}».

                                                        Само собой, для языков в разными парадигмами так не получится. Но как минимум это схлопнет крупные группы схожих языков.
                                                        использую в том числе и синтаксис для выражения своего интента
                                                        Можно пример, кроме разделительного переноса строк, и чтобы это нельзя было повторить в автоформатировании?
                                                        который удовлетворяет всем синтаксисам
                                                        Я же говорю, зависимость идет наоборот. Граф не пытается удовлетворять синтаксисам, он изначально создается исходя из набора возможностей и операций. А уже потом пишутся синтаксические генераторы, которые по запросу делают нужные представления из узлов. Синтаксис при этом не обязательно будет совпадать со своим прототипом.
                                                        Вы забыли, что надо написать не только правила генерации (которые тоже сложнее интерполяции), но и правила парсинга.
                                                        Не нужно. Ни на каком из этапов работы не производится более сложный анализ текста чем fuzzy-match текущего вводимого слова.
                                                        А если результат работы существующего парсера несовместим с вашим графом?
                                                        Значит для этого языка пишется плагин с его несовместимыми узлами. Так же как плагины для IDE под каждый язык пишутся сейчас. Существующий несовместимый язык в любом случае ничего не теряет.
                                                        А какая мне разница, если вашими стараниями я не смогу пользоваться той IDE, которая мне удобна?
                                                        Каким образом появление новых IDE запретит вам пользоваться старой IDE, и почему вы считаете что новые IDE будут вам неудобны?
                                                          0
                                                          Он пишет и читает код, но под видимым текстом лежит не типичный текстовый редактор, а подобие иерархии виджетов.

                                                          А эта "иерархия виджетов" ресурсов не потребляет?


                                                          Нужно только определять тип нового вводимого узла, для чего в 99% случаев достаточно одного слова и автоподстановки

                                                          А можно увидеть источник для этой цифры? А если я не хочу пользоваться автоподстановкой?


                                                          Например парсинг. Один раз для подсветки, второй раз для индексирования, третий раз для автоформатирования, четвертый раз всякими посторонними плагинами в хуках на редактирование, ну и в конце концов компилятором для подсветки ошибок.

                                                          А зачем это делать столько раз, когда можно сделать один?


                                                          Под операцией я подразумевал операцию редактирования.

                                                          А операции редактирования — они быстрые. Точнее говоря, они быстрее, чем операции над графом, потому что вам надо сделать не больше работы, чем при выполнении той же наблюдаемой пользователем операции.


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

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


                                                          Все они в графовом представлении будут делать то же самое, но в намного меньшем объеме работы.

                                                          Нет, не будут.


                                                          Во-первых это все еще микроскопически мало по времени, даже если на экране будет тысяча переменных.

                                                          Ну так докажите это "микроскопически мало".


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

                                                          … а потом что? Когда отрисовка меняется?


                                                          В-третьих в текстовом представлении доставать значения придется ничуть не меньше.

                                                          Конечно, меньше. Они лежат прямо там, где мы хотим отрисовать, а не в каком-то другом месте, куда ведет ссылка.


                                                          А в четвертых за счет «графовости» можно использовать такую оптимизацию рендеринга как атлас слов, при которой каждое слово отрендерится всего один раз, а дальше будет просто вклеиваться в области на экране,

                                                          А это точно будет производительнее?


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

                                                          Ну вот именно поэтому текстовый редактор и быстрее графа.


                                                          В контексте перехода на графовый подход их будет хранить VCS.

                                                          … которые надо будет полностью переписать, ага.


                                                          Т.е. сейчас вы редактируете в IDE текст, ждете N секунд не внося изменений (или сохраняете файл), IDE запускает VCS на сканирование всего проекта или в лучшем случае файла.

                                                          Нет. Я так не делаю. Зачем?


                                                          В случае с графовым предсавлением IDE нужно будет передать VCS лишь короткую информацию об конкретно изменении, что произойдет мгновенно.

                                                          И IDE станет тесно связана с VCS.


                                                          Этот текст будет представлен узлом чистого текста в процессе редактирования.

                                                          "Узел чистого текста" лишен семантики в графе.


                                                          Без какой либо порчи любых узлов до и после этого.

                                                          Как это "без порчи", если последующие токены потеряли смысл? Вот было у меня int foo = bar(), я написал baz после int, все foo = bar() теряет (формально) смысл. Граф больше не консистентен в этой строчке, он не может быть.


                                                          Ему кажется что он вводит текст. Но на самом деле например когда он удаляет символ посреди имени переменной он редактирует напрямую поле имени в узле этой переменной.

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


                                                          Можно пример?

                                                          Да легко. void Bar(Action<T> foo). Можно вызвать как Bar(SomeAction), можно как Bar(q => baz). Вот вам два разных способа, и вам надо сохранить, что они разные.


                                                          Пользователь просто доучит новую/отличающуюся часть.

                                                          При чем тут пользователь? В языке нет той части, которая представлена в графе, что делать?


                                                          Можно пример, кроме разделительного переноса строк, и чтобы это нельзя было повторить в автоформатировании?

                                                          Да вот только что выше, с двумя разными делегатами.


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

                                                          А набор возможностей — это и есть синтаксисы, как ни странно.


                                                          Ни на каком из этапов работы не производится более сложный анализ текста чем fuzzy-match текущего вводимого слова.

                                                          Это, очевидно, неправда. Просто представьте, что я начал вводить текст с самого начала.


                                                          Существующий несовместимый язык в любом случае ничего не теряет.

                                                          Теряет. VCS он теряет, например.


                                                          Каким образом появление новых IDE запретит вам пользоваться старой IDE

                                                          Я не смогу открыть новые языки в старом редакторе. И я не смогу пользоваться общей VCS.

                                                            0
                                                            А эта «иерархия виджетов» ресурсов не потребляет?
                                                            Намного меньше чем структуры представляющие текст в существующих IDE. На самом деле, даже нет необходимости хранить более чем те пару сот виджетов которые обычно влазят в околовидимую область и их прямых родителей, достаточно создавать их по необходимости.
                                                            А можно увидеть источник для этой цифры?
                                                            Взял из головы, но скорей всего там будет еще больше. Просто потому что практически всегда можно определить тип элемента по его родителям, соседям, напечатанному слову и информации о всех идентификаторах видимых в текущем неймспейсе. На самом деле, я затрудняюсь придумать пример где это вообще может не работать. Тем более когда речь о генерируемом синтаксисе, в котором это изначально может учитываться.
                                                            А зачем это делать столько раз, когда можно сделать один?
                                                            Спросите у разработчиков IDE и текстовых редакторов, почему они так не делают, я был бы только рад, будь одно так. Хотя некоторые этапы и так понятны. Например поддержку языков сейчас модно делать плагинами, а в API просто не вынесена работа с AST. Или например у реализации языка не имеется нужных инструментов интеграции.
                                                            А операции редактирования — они быстрые.
                                                            Нет. Даже просто вставка одного символа в файл рано или поздно потребует движения кучи символов буфере, а затем и всех прелестей с парсингом и анализом перечисленных выше, после чего весь файл еще просканируется VCS, а в процессе еще будут вызваны хуки посторонних плагинов редактирования, которые тоже что-то анализируют ввиду отсутствия какой-либо семантической инфы о том что сейчас произошло, иногда вызывая каскады последующих изменений.
                                                            они быстрее, чем операции над графом, потому что вам надо сделать не больше работы
                                                            Больше. Вам нужно парсить документ как минимум единожды после изменения, нужно сканировать файл целиков в VCS как минимум единожды после изменения, нужно обновлять форматирование и иногда перерасчитывать подсветку, а в придачу все это все равно будет записано в индекс проекта, который по сути сам дублирует граф и значительную часть работы с ним.
                                                            В частности, я могу читать текстовый код, не взаимодействуя с графом.
                                                            Представлением он быть от этого не перестал. Вы просто сделали граф неявным.
                                                            Нет, не будут.
                                                            Можно конкретный пример?
                                                            Ну так докажите это «микроскопически мало».
                                                            А может вы сами докажете что это долго, если приводите это в пример долгой операции? А то все я да я.
                                                            а потом что? Когда отрисовка меняется?
                                                            В смысле «отрисовка меняется»? Если меняется значение узла, то обновляются и виджеты в составе которых есть это значения. К счастью, кода в котором в экран влазит тысячи раз одна и та же измененная переменная нет. Если ничего не меняется, виджет при необходимости рисует закешированное значение. А необходимости может и не быть, например если ничего не менялось, или менялось но область пререндерена в буфер.
                                                            Они лежат прямо там, где мы хотим отрисовать
                                                            Где «там»? Увы, отображение текстового кода программ совсем не тревиальное, и требует отдельной от фактического буфера текста структуры, которая будет поддерживать все эти реальные строки, виртуальные стоки wordwrap'а, fold'ы строк, подсветку синтаксиса, и прочие веселости. И все это ссылается на структуру хранящую сам текст, которая зачастую ничуть не проще структуры отображения, и устроена специальным образом чтобы при каждом вводе символа не пришлось двигать по пол документа символов. Иногда это кстати дерево. Из которого вид в итоге будет получать значения.
                                                            А это точно будет производительнее?
                                                            Да, но не то чтобы прям сильно.
                                                            Ну вот именно поэтому текстовый редактор и быстрее графа.
                                                            Почему?
                                                            которые надо будет полностью переписать, ага
                                                            Действительно. Сейчас то все само по себе лучше становится, никто ничего не пишет.
                                                            Нет. Я так не делаю. Зачем?
                                                            Это описание того, как сейчас работают большинство IDE.
                                                            И IDE станет тесно связана с VCS.
                                                            Не тесней чем сейчас, просто в командную строку будет передаваться не путь к проекту/файлу, а что-то другое.
                                                            «Узел чистого текста» лишен семантики в графе.
                                                            Ну и пусть себе лишен. По крайней мере это ничего не ломает и не требует дополнительной обработки, в отличие от текстового кода.
                                                            Граф больше не консистентен в этой строчке, он не может быть.
                                                            Текст неконсистентен, граф консистентен. Потому что он не получается от парсинга текста. Вы можете вписать любую белиберду в любое место, и это просто добавит узел текстовой белиберды, или сделает таковым старый. IDE всегда может указать точное начало и конец белиберды которую нужно исправить, или даже подсветить ее целиком, с максимально человекочитаемой ошибкой. А не поломать к чертям всю расцветку и ругнуться на невинную точку с запятой в каком-то другом файле, как это сейчас может сделать Visual Studio с плюсами.
                                                            Вы понимаете же, что в этот момент вам надо разорвать все связи из других упоминаний этой переменной? Потому что вы не знаете, он хочет эту переменную переименовать, или другую создать.
                                                            Я описал это в соседнем комментарии. Если редактируем определение — меняем всю переменную. Если меняем упоминание — то по умолчанию меняется только оно, а по требованию можно как угодно.
                                                            Вот вам два разных способа, и вам надо сохранить, что они разные.
                                                            Честно, но вижу никакой проблемы. В указанных способах будут разные узлы графа. В первом вызов с передачей делегата, во втором вызов с передачей определенной на месте лямды.
                                                            В языке нет той части, которая представлена в графе, что делать?
                                                            То же, что и сейчас, когда в IDE нет поддержки какого-то языка. Дописать ее. Только не почти с нуля, как сейчас, а просто допилив нужные или отличающиеся узлы.
                                                            Да вот только что выше, с двумя разными делегатами.
                                                            Разные делегаты — разные узлы в графе.
                                                            А набор возможностей — это и есть синтаксисы
                                                            Все же не совсем. Иногда синтаксис это просто сахар над другим синтаксисом.
                                                            Просто представьте, что я начал вводить текст с самого начала.
                                                            Представил. Проблемы не обнаружено.
                                                            VCS он теряет, например.
                                                            У него уже есть десяток совместимых VCS, зачем ему еще одна VCS совсем другого типа?
                                                            Я не смогу открыть новые языки в старом редакторе.
                                                            Возможно. Но это не значит что это плохо. Можно продолжать на старых текстовых языках, или перейти на новые графовые редакторы. Нельзя получить плюхи нового не отказавшись от старого.
                                                              0
                                                              Намного меньше чем структуры представляющие текст в существующих IDE.

                                                              Почему? Они же ничем не легче.


                                                              Взял из головы

                                                              Ну слушайте. Вот вы "взяли из головы", что там 99%. А я — что там меньше 5. И что делать будем?


                                                              Просто потому что практически всегда можно определить тип элемента по его родителям

                                                              Определяйте тип элемента для bar.


                                                              class Foo
                                                              {
                                                                bar
                                                              }

                                                              Спросите у разработчиков IDE и текстовых редакторов, почему они так не делают

                                                              … а почему вы считаете, что они так не делают?


                                                              Даже просто вставка одного символа в файл рано или поздно потребует движения кучи символов буфере

                                                              То есть той самой отрисовки, которую вы называете быстрой.


                                                              а затем и всех прелестей с парсингом и анализом перечисленных выше

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


                                                              после чего весь файл еще просканируется VCS

                                                              Я не знаю за все VCS, но git ничего не сканирует.


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

                                                              Не пользуйтесь такими плагинами. Пользуйтесь теми, которые смотрят на семантическую информацию.


                                                              Вам нужно парсить документ как минимум единожды после изменения

                                                              Нет. Только после открытия, один раз.


                                                              Представлением он быть от этого не перестал. Вы просто сделали граф неявным.

                                                              Йеп. Но зато это очень быстро.


                                                              Можно конкретный пример?

                                                              Можно. Отрисовка текста из текста быстрее, чем из графа.


                                                              А может вы сами докажете что это долго, если приводите это в пример долгой операции?

                                                              Неа. Утверждения "граф быстрее" начали вы.


                                                              В смысле «отрисовка меняется»?

                                                              В прямом. Элемент вставили на два слова левее.


                                                              Где «там»?

                                                              В текстовой строке, которую мы выводим на экран. Это локально.


                                                              Увы, отображение текстового кода программ совсем не тревиальное, и требует отдельной от фактического буфера текста структуры, которая будет поддерживать все эти реальные строки, виртуальные стоки wordwrap'а, fold'ы строк, подсветку синтаксиса, и прочие веселости.

                                                              … и чтобы отрисовать текст из графа, нужно будет все это же построить из графа.


                                                              Почему?

                                                              Потому что структура проще.


                                                              Это описание того, как сейчас работают большинство IDE.

                                                              Что-то я не замечал, чтобы хотя бы одна из используемых мной IDE так делала.


                                                              Не тесней чем сейчас, просто в командную строку будет передаваться не путь к проекту/файлу, а что-то другое.

                                                              Да нет, теснее. Вам же надо договориться о формате "изменений", которые вы передаете.


                                                              Ну и пусть себе лишен. По крайней мере это ничего не ломает и не требует дополнительной обработки, в отличие от текстового кода.

                                                              А какой магией этот "узел чистого текста" превратится в правильные узлы? Не дополнительной обработкой?


                                                              Текст неконсистентен, граф консистентен.

                                                              Если граф консистентен, он больше не соответствует тому, что ввел программист. То есть, на самом деле, неконсистентен.


                                                              Потому что он не получается от парсинга текста.

                                                              Да нет, он получается именно от парсинга текста, потому что я, как программист, ввожу текст и ничего больше. Неоткуда больше графу взяться.


                                                              Я описал это в соседнем комментарии. Если редактируем определение — меняем всю переменную. Если меняем упоминание — то по умолчанию меняется только оно, а по требованию можно как угодно.

                                                              А откуда вы знаете, какое из двух "если" выбрать, если я, как программист, просто поставил курсор в текст переменной и нажал del?


                                                              Честно, но вижу никакой проблемы. В указанных способах будут разные узлы графа.

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


                                                              То же, что и сейчас, когда в IDE нет поддержки какого-то языка. Дописать ее. Только не почти с нуля, как сейчас, а просто допилив нужные или отличающиеся узлы.

                                                              Да куда дописывать-то? В целевом языке нет такой конструкции. В стандарт языка? Не выйдет.


                                                              Разные делегаты — разные узлы в графе.

                                                              Ну да. Вы просили пример, вы его получили.


                                                              Иногда синтаксис это просто сахар над другим синтаксисом.

                                                              Иногда, но не всегда.


                                                              У него уже есть десяток совместимых VCS, зачем ему еще одна VCS совсем другого типа?

                                                              Затем, что в одной компании обычно держат одну VCS, а не несколько, даже если разработка ведется на нескольких языках.


                                                              Нельзя получить плюхи нового не отказавшись от старого.

                                                              Вы спросили, почему придется отказываться? Я показал.

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


                                                                Известные мне IDE парсят/индексируют код один раз и потом инкрементально обновляют дерево при изменениях.
                                                  0
                                                  Я имею ввиду что код на диске и во внутренних структурах IDE/компиляторов/анализаторов должен быть представлен графом.


                                                  А с чего вы взяли что это не так?
                                                    0
                                                    Потому что это так и есть. Код на диске хранится в текстовом виде, даже не уверен, должен ли я писать о том как это проверить. Соответственно и IDE с ними работают как с текстом, но если такого следствия вам не достаточно, вы можете посмотреть их исходники, или например почитать вот эту статью от авторов одной популярной IDE.
                                                      0
                                                      Спасибо, мне очень приятно, что вы в качестве примера приводите IDE от компании, в которой я работаю.
                                            0
                                            Не трогайте текст!
                                            Это лучшее что случилось с человечеством)
                                              0
                                              А что в нем хорошего, кроме возможности обмена через текстовые сообщения и возможности программирования из блокнота?
                                                0
                                                А что в нем хорошего

                                                Гм, что хорошего в письменной культуре, насчитывающей тысячелетия?


                                                Она эффективна.

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

                                                    Это самая важная часть в разработке. Ну, на мой личный вкус.

                                                  0
                                                  А у вас есть предложения получше?
                                                  Я абсолютно серьезно, если есть — давайте, это же революцию в коммуникациях совершит.
                                                    0
                                                    Есть, описал выше, но особого отношения к коммуникации это не имеет.
                                              • UFO just landed and posted this here
                              +1
                              Проблема всех подобных метрик, принципов и методик в том, что нет доказанной корреляции с реальным эффектом. Как качество ПО связано с ошибками, багами, скоростью? Формулы не существует. Это что-то на грани интуиции и здравого смысла. Вот если бы мы знали, что D=3,63 => 2 падения серверов в год, то это имело бы какой-то смысл.
                              Второй вопрос: как считать метрики; что такое качество. Этого тоже никто не знает.
                                0
                                На деле нет даже гарантий, что не становится хуже. Возможно, выгоднее было бы сделать один большой класс вместо кучи маленьких. Менее гибко? А кто сказал, что эта гибкость реально нужна? Да и поддерживаемого кода становится не меньше, а больше. Может и нафиг это всё?
                                  0
                                  Практический любой выбор в программировании это торговля между характеристиками процесса разработки, процесса тестирования, процесса компиляции, и процесса выполнения программы. Какие свойства важнее («лучше») зависит от каждого конкретного случая.

                                  Принцип SRP решает конкретный набор проблем, описанных Робертом в его примерах (например тут). Если вам эти проблемы не грозят, вы можете пожертвовать этим принципом.
                                  0
                                  Есть корреляция. Ее давно заметили писатели и дизайнеры.

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

                                  Вы сразу заметите разницу с чистыми фразами. Из которых выкинули весь мусор. Разбили на понятные, завершенные предложения. Они не отвлекают. Не предлагают лишнего. Даже метафоры в них используются для лучшего понимания. Чистый текст — обращается к вам с честностью джентельмена. Грязный — напоминает цыганку с вокзала.

                                  Вы заметили разницу в восприятии двух абзацев? Она же будет между «чистым» и «грязным» кодом. Разницу можно наблюдать, пусть формулы которая описывает ее пока нет. Но это вопрос работы и времени.
                                    0
                                    Есть корреляция. Ее давно заметили писатели и дизайнеры.

                                    Корреляцию чего и с чем "давно заметили писатели и дизайнеры"?


                                    Вы заметили разницу в восприятии двух абзацев?

                                    Второй воспринимается хуже, дадада.

                                      0
                                      Вы серьезно не поняли, что я хотел сказать, или вам так, потроллить?)

                                      На случай если я непонятно выразился:

                                      Корреляцию между представлением графической/текстовой информации и результатами её обработки в мозге. Например, в разрезе запоминания информации.

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

                                        0
                                        Вы серьезно не поняли, что я хотел сказать

                                        Я не понял, как то, что (как я понял) вы хотите сказать, соотносится с тем, что (как я понял) спрашивает автор комментария, на который вы отвечаете.


                                        Корреляцию между представлением графической/текстовой информации и результатами её обработки в мозге.

                                        А вопрос — как я его понял — был о корреляции между представлением информации и качеством получившегося программного продукта.


                                        И это не говоря о том, что там, где вы пишете "корреляция", мне на самом деле приходится читать "какая-то неизмеримая связь".


                                        Пример попроще?

                                        И вот как этот пример связан с качеством ПО?

                                          0

                                          Блин, опять пропустил слишком много шагов в обьяснении)


                                          В моем понимании, в качество ПО входит не только отказоустойчивость и скорость работы, но и минимизации стоимости внесения в него изменений. Измеряется в в часах или зелёных президентах, не важно.


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


                                          Если код плохо структурирован — это отнимет у них больше времени.


                                          Вывод — неплохо бы понять, как структурировать код для оптимизации внесения изменений.


                                          Где я ошибаюсь, или где вы меня не поняли?)

                                            –1
                                            Где я ошибаюсь, или где вы меня не поняли?

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

                                              0

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


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

                                                +1
                                                Вы заметили разницу в восприятии двух абзацев?

                                                Вы попробуйте во втором абзаце перемешать предложения или изменить одно из них. Да, вы декомпозировали, но у вас появилось множество связей, конкретная последовательность, зависимости, множество точек отказа, упала общая надёжность.

                                                И что нам это даёт? Мы выполняем один принцип, но нарушаем множество других. Следовательно, здесь имеется задача многокритериальной оптимизации. Как нам её решить, если мы не знаем критерием, их значений, их взаимосвязи, конечной цели оптимизации, ограничений?

                                                Где формула? Где её доказательство? Что вы подразумеваете под качеством ПО и как его измерить, сравнить, построить его тренд? Как объяснить задержку сроков и потерю денег на две недели из-за переписывания кода класса на множество классов?

                                                Я согласен с ребятами выше: необходимо провести какие-нибудь исследования, чтобы понять, что и как использовать. Скорее всего, в будущем это будет целая наука (как сейчас развились devops).
                                                  0
                                                  Я лишь сказал, что корреляция есть, судя по тому, что ее можно почувствовать. Значит в поиске объяснения этой корреляции есть смысл.

                                                  А формулу — да, согласен довести до конечной формулы было бы неплохо.
                                                  Но надо довести.

                                                  Физика начиналась не с формул. Физика начиналась с того, что люди замечали какую-то зависимость. У Архимеда не было формулы, он предметы в воду пихал и заметил интересную штуку — они по разному вытесняют воду. Это он позднее сформулировал в закон, который лишь через много лет обрел привычный нам вид формулы.
                                                  Математика начиналась не с формул. Она началась с того, что какой-то гений дошел до того, что камушки и яблоки можно связать через свои действия, и их у них появляется общая характеристика, которую мы называем «количество».

                                                  Ни одна область не начиналась с формулы. Они начинались с наблюдений. С проблем. С вопросов.

                                                  Когда я вам говорю, что модуль написан качественно — у вас какая картина в голове возникает? Какие ожидания от такого кода?
                                                  Когда вы говорите, что код «не качественный» — как именно вы это понимаете?
                                                  Вот с этих вопросов я бы и начал.

                                                  Полностью согласен с необходимостью исследования, готов быть в первых рядах)
                                                  0
                                                  Окей, в вашем понимании стоимость модификации в качество не входит.

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


                                                  И, на самом деле, это не важно. Потому что даже если стоимость модификации входит в качество ПО, количество неопределенных все еще слишком велико (начиная с самого простого — вклада стоимости модификации в качество).


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

                                                    +2
                                                    И в итоге это все равно придет к тому, что есть здравый смысл, который говорит «ну да, хорошо структурированный код легче модифицировать», но знаете, что будет ответом на вопрос «что такое хорошо структурированный код»? «Тот, который легче модифицировать».


                                                    Разложите по кирпичикам «модификацию кода» — и да, вы получите верный ответ.

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

                                                    Есть моя кодовая база. Где-то в ней находится код этого окна. Если я нормально назвал файл с ним, то у меня есть Map из названия окна в требованиях, к файлу в котором хранится код окна. Например, в требованиях оно Add Comment, в коде AddCommentWindow Сложность поиска в мапе? O(1), если нет коллизий.

                                                    Я был не прав и назвал окно не правильно. Но разложил файлики по папочкам с нормальными именами. Чтобы найти нужный файл — иду по пути /habr/ui/posts/comments/edit. Я шел по дереву, сложность логарифмическая.

                                                    Я положил все файлики в одну папку и дал им имена file1, file2… Теперь мне нужно зайти в файл, чтобы понять где нужный мне. Сложность поиска — линейная.

                                                    Я запихал все в один файл — сложность как минимум линейна.

                                                    Вывод — давай имена файлам такие, чтобы потом найти их при упоминании в требованиях, и на всякий случай организуй нормальную иерархию, чтобы худший случай поиска был O(log(n)).
                                                    Итак, хорошо структрированный для поиска по требованиям код, это код в котором:
                                                    1) для каждой сущности из требований имеется отображение в виде файла с кодом, описывающим поведение этой сущности.
                                                    2) организованный в древовидную структуру каталогов, где каждое название каталога является обобщением какой-либо характеристики сущностей из требований, а название каждого каталога каталогов — обобщает характеристики вложенных элементов.

                                                    Сойдет за разрешение циклической зависимости?)

                                                    Более того, это работает для FizzBuzzEnterprise. Есть задача, код которой укладывается в 10 строк. Предположим, что вместо того, чтобы писать один файл, мы разнесли эти десять строк по синглтонам фабрик фабрик стратегий, с иньекцией зависимости через кодогенерацию. Что мы получили? Логарифм от огромного дерева, вместо константы для поиска файла и линейного поиска в самом файле.

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

                                                    Поиск, конечно, простой пример. Но, мне кажется, что сложность остальной части задачи не говорит о принципиальной невозможности применения такого подхода. Могу ошибаться.
                                                      –2
                                                      Разложите по кирпичикам «модификацию кода» — и да, вы получите верный ответ.

                                                      Что такое "верный ответ"?


                                                      Итак, хорошо структрированный для поиска по требованиям код, это код в котором

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


                                                      Сойдет за разрешение циклической зависимости?)

                                                      Нет.


                                                      Мне показалось, или я только что соединил одну из подзадач модификации кода, с теорией вычислительной сложности?

                                                      Вам показалось.


                                                      И сделал из этого выводы, которые весьма соответствуют здравому смыслу.

                                                      Я даже не понял, какие выводы вы сделали, не говоря уже о том, чему они соответствуют.


                                                      И это, собственно, возвращает нас к началу вашего комментария:


                                                      Разложите по кирпичикам «модификацию кода»

                                                      А почему вы думаете, что эта декомпозиция одинакова для всех?

                                                        +2
                                                        Что такое «верный ответ»?

                                                        Переформулирую:
                                                        Дайте определение тому, что значит «модифицировать код в соответствии с требованиями» и вы поймете, как можно сравнивать его структуру с точки зрения сложности модификации. Или наоборот, сможете строго доказать, что эта задача не решаема.

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

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

                                                        Нет.
                                                        Вам показалось.

                                                        Простите, но на это мне не ответить.
                                                        Добавьте подробностей. Расскажите, почему циклическая зависимость в моем примере осталась.
                                                        Или где я ошибся, переводя задачу поиска по проекту, в задачу абстрактного поиска в структуре данных?

                                                        Я даже не понял, какие выводы вы сделали, не говоря уже о том, чему они соответствуют.

                                                        Вывод об оптимальной структуре для поиска, с учетом вышеизложенной модели. Обозначен словом «вывод».

                                                        А почему вы думаете, что эта декомпозиция одинакова для всех?

                                                        А это отличный вопрос. Ответ — я так не думаю. Я лишь утверждаю, что зная алгоритм, можно посчитать сложность от его входных параметров.

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

                                                        И да, если вам кажется, что задача принципиально не формулируема, и именно поэтому все останется в рамках цикла бессмысленных слов определяющих друг-друга — вы не могли бы это доказать?
                                                          –2
                                                          Дайте определение тому, что значит «модифицировать код в соответствии с требованиями» и вы поймете, как можно сравнивать его структуру с точки зрения сложности модификации.

                                                          Я в курсе, что можно сравнивать. Я не в курсе, как это делать объективно.


                                                          Не понял — это вы мой первый пункт из вывода, своими словами перепечатали, или свое новое определение даете?

                                                          Свое новое определение даю.


                                                          поэтому для подстраховки можно использовать папочки

                                                          А папочки — это тоже предположения, не более того.


                                                          Расскажите, почему циклическая зависимость в моем примере осталась.

                                                          Сначала расскажите, где циклическая зависимость в вашем примере была.


                                                          Или где я ошибся, переводя задачу поиска по проекту, в задачу абстрактного поиска в структуре данных?

                                                          Приблизительно там, где не сформулировали критерии поиска. В смысле, четкие критерии.


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

                                                          Как мы видим, мой и ваш выводы не совпадают. У кого здравый смысл здравее?


                                                          Что веселее, о структуре кода это еще ничего не говорит. Только о распределении кода по файлам.


                                                          Я лишь утверждаю, что зная алгоритм, можно посчитать сложность от его входных параметров.

                                                          А толку-то?


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

                                                          Более подходящий по какому критерию?


                                                          И вопрос в том, чтобы сформулировать задачу.

                                                          Именно. А вы, еще не разобравшись в формулировке задачи, уже утверждаете, что корреляция давно найдена.


                                                          И да, если вам кажется, что задача принципиально не формулируема, и именно поэтому все останется в рамках цикла бессмысленных слов определяющих друг-друга — вы не могли бы это доказать?

                                                          Вы про чайник Рассела слышали?


                                                          Впрочем, мне не кажется, что задача принципиально не формулируема, мне всего лишь кажется, что я до сих пор не видел ни одной формальной формулировки.

                                                            0
                                                            Сначала расскажите, где циклическая зависимость в вашем примере была.


                                                            Я отвечал на ваш пример:
                                                            но знаете, что будет ответом на вопрос «что такое хорошо структурированный код»? «Тот, который легче модифицировать».

                                                            И вел к тому, что разрешить цикл: «легко модифицируемый код — это код хорошо структурированный, а хорошо структурированный код это тот, который легко модифицировать» — можно, если понять что значит модифицировать. Не более. Не утверждал, что знаю точно «как» это сделать, но предложил один из подходов, который мне показался перспективным.

                                                            А папочки — это тоже предположения, не более того.

                                                            Да, но матожидание времени поиска, в случае ошибок, они уменьшат. На большее не претендуют.

                                                            Как мы видим, мой и ваш выводы не совпадают. У кого здравый смысл здравее?
                                                            Что веселее, о структуре кода это еще ничего не говорит. Только о распределении кода по файлам.

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

                                                            А толку-то?

                                                            Объективная величина для сравнения.

                                                            Более подходящий по какому критерию?

                                                            Минимизации временной сложности.

                                                            Именно. А вы, еще не разобравшись в формулировке задачи, уже утверждаете, что корреляция давно найдена.

                                                            Я утверждал, что разницу можно почувствовать, и это свидетельство в пользу существования зависимости, если вы хотите более точное определение.
                                                            Можно поставить чайник греться и переодически пихать в него руку. Можно заметить, что с течением времени, ощущения при пихании руки меняются. И понять, что время стояния чайника каким-то образом коррелирует с тем, обожжетесь вы пихнув в него руку, или нет. И знание термодинамики не является для этого необходимым. Но если вы хотите точно знать, начиная с какого момента вам обожжет руку — вам нужно знать термодинамику, параметры руки, воды, чайника… Но это уже не корреляция.

                                                            Вы про чайник Рассела слышали?
                                                            Впрочем, мне не кажется, что задача принципиально не формулируема, мне всего лишь кажется, что я до сих пор не видел ни одной формальной формулировки.


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

                                                            И я полностью согласен насчет отсутствия нормальной формулировки.
                                                              0
                                                              И вел к тому, что разрешить цикл: «легко модифицируемый код — это код хорошо структурированный, а хорошо структурированный код это тот, который легко модифицировать» — можно, если понять что значит модифицировать.

                                                              А вы его разрешили? По-моему, вы просто подменили одно из понятий.


                                                              Почему код, который получается благодаря вашему подходу выше, хорошо структурированный?


                                                              Да, но матожидание времени поиска, в случае ошибок, они уменьшат.

                                                              Как это доказать?


                                                              Вы ведь тоже говорите о том, что нужен мэппинг из системы теминов требований, в термины кода.

                                                              Я говорю, что он достаточен для решения проблемы поиска. Это не "тоже", это другое.


                                                              Объективная величина для сравнения.

                                                              Объективная величина для сравнения появится тогда, когда у вас будет не абстрактный алгоритм, для которого можно посчитать, а конкретный алгоритм, применимый к данному случаю, и его входные данные. А в случае с "поиском объекта для модификации" у вас нет ни того, ни другого.


                                                              Минимизации временной сложности.

                                                              Временной сложности чего? Поиска? А почему не минимизации временной сложности написания кода? А если двукратное уменьшение временной сложности поиска возможно только двадцатикратным увеличением временной сложности написания — тоже будем поиск оптимизировать?


                                                              Я утверждал, что разницу можно почувствовать, и это свидетельство в пользу существования зависимости, если вы хотите более точное определение.

                                                              Ну да, какая-то зависимость есть. Какая?


                                                              А то понимаете, свидетельства в пользу существования зависимости "сложность модификации кода зависит от того, как код написан" не очень нужны, если честно.


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

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

                                                                0
                                                                Почему код, который получается благодаря вашему подходу выше, хорошо структурированный?


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

                                                                Как это доказать?

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

                                                                Я говорю, что он достаточен для решения проблемы поиска. Это не «тоже», это другое.

                                                                И я с вами полностью согласен, если есть идеальный мэппинг — древовидная структура организации не нужна. Если он не идеален, то весь вопрос в том, насколько.

                                                                Объективная величина для сравнения появится тогда, когда у вас будет не абстрактный алгоритм, для которого можно посчитать, а конкретный алгоритм, применимый к данному случаю, и его входные данные. А в случае с «поиском объекта для модификации» у вас нет ни того, ни другого.


                                                                Вы не правы. Конкретным должен быть только алгоритм.
                                                                Входные данные — могут быть абстрактны. Вы же можете сказать, сколько конкретный бинарный поиск, занимает шагов на абстрактном сортированом массиве длины N, ища абстрактное значение. Разумеется не численно, но ассимптотику оценки дать можете.

                                                                Временной сложности чего? Поиска? А почему не минимизации временной сложности написания кода? А если двукратное уменьшение временной сложности поиска возможно только двадцатикратным увеличением временной сложности написания — тоже будем поиск оптимизировать?


                                                                Нет, просто для «написания» я не могу сходу придумать алгоритм. Повторю свой предыдущий пост, где уже говорил, что не претендую на носителя сокровенного знания «как сделать код проще»:
                                                                Не утверждал, что знаю точно «как» это сделать, но предложил один из подходов, который мне показался перспективным.


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

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

                                                                У меня сложилось ощущение, что вы пытаетесь мне сказать, что у меня нет знания про все чайники, а я говорю о том, что чайники познаваемы)
                                                                И вы вроде даже согласились с познаваемостью чайника, что и являлось целью моего первого комментария.
                                                                  0
                                                                  Как не вижу и того, что утверждаю, что так можно получить «хорошо структурированный код».

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


                                                                  В моем случае существует ненулевая вероятность того, что файл можно найти за логарифм.

                                                                  Ээээ… я не понимаю, что такое "ненулевая вероятность того, что файл можно найти за логарифм". Временные сложности так не работают.


                                                                  (и откуда вообще взялся логарифм?)


                                                                  В вашем случае — она стремится к нулю, т.е. поиск гарантированно вырождается в линейный.

                                                                  … в моем случае вообще нет файлов. Это если быть точным.


                                                                  Я не понимаю, почему "мой" поиск гарантированно вырождается в линейный, а ваш — нет.


                                                                  Вы не правы.

                                                                  То есть вы можете посчитать (и доказать) временную сложность поиска файла, соответствующего объекту из требований?


                                                                  Входные данные — могут быть абстрактны.

                                                                  Вот только абстракция должна быть правильно выбрана.


                                                                  Как минимум вы не будете пихать руки в ваш собственный чайник, после того как он провел достаточно времени на плите.

                                                                  … и это будет ошибкой, потому что чайник остывает. Ровно то же самое, что произошло с эмпирическим наблюдением "код лучше, если...".


                                                                  У меня сложилось ощущение, что вы пытаетесь мне сказать, что у меня нет знания про все чайники

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


                                                                  И вы вроде даже согласились с познаваемостью чайника,

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

                                                                    0
                                                                    Я пытаюсь вам сказать, что ваше утверждение «Есть корреляция. Ее давно заметили писатели и дизайнеры.» (а) неверно и (б) даже если бы было верно, неприменимо к комментарию, на который вы отвечаете.


                                                                    Мы все это время говорим о том, что не одинаково понимаем слово корреляция?)
                                                                      0

                                                                      Хотя это само по себе прекрасная демонстрация к вопросу о том, что такое SRP, нет, мы говорим не только об этом — а еще и, как минимум, об измеримости и формализуемости.

                                                                      0
                                                                      Значит, ваш подход не применим к задаче «что такое хорошо структурированный код», или к задаче «какой код легко модифицировать» — потому что нельзя сводить «легко модифицируемый код» к «коду, в котором легко искать».


                                                                      Конечно нельзя. Я описал лишь одну из подзадач модификации. Самую простую и очевидную. И раз 5 написал, что не претендую на то, что в комментарии могу так взять и описать математически весь процесс работы с кодом.

                                                                      То, что одна из подзадач может быть описана математически — свидетельство в пользу того, что задача целиком тоже может быть так описана. Не доказательство. Свидетельство. Вы понимаете концепцию оценки вероятности гипотезы исходя из свидетельств?

                                                                      Ээээ… я не понимаю, что такое «ненулевая вероятность того, что файл можно найти за логарифм». Временные сложности так не работают.
                                                                      (и откуда вообще взялся логарифм?)

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

                                                                      … в моем случае вообще нет файлов. Это если быть точным.
                                                                      Я не понимаю, почему «мой» поиск гарантированно вырождается в линейный, а ваш — нет.

                                                                      Так вы опишите формально «ваш поиск» и «ваше условие». Свяжите его с математикой.

                                                                      Ваше определение:
                                                                      Нет. Это код, в котором элемент назван так, чтобы из требований можно было предположить, как этот элемент называется. Дальше работает инструмент поиска, которому плевать на раскладку по файликам и папочкам.

                                                                      Что вы понимаете под «предположить», под «элементом кода», под «инструментом поиска», как вы получили отсутствие коллизий «предположений»?

                                                                      Я из этого совсем не четко сформулированного определения предположил, что вы решили сделать Map. Потому что по вашему описанию, у вас есть функция предположения, которая из какого-то элемента требований получает название элемента кода.
                                                                      Т.Е. ваш «элемент требований» является ключем, «название элемента кода» — значением, и ключ отображается в значение за счет функции «предположить»

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

                                                                      То есть вы можете посчитать (и доказать) временную сложность поиска файла, соответствующего объекту из требований?

                                                                      Я могу составить модель процесса поиска файла, и да, посчитать и доказать ее сложность от различных начальных параметров.
                                                                      Чтобы показать что она работает в реальности — придется проводить эксперимент. Знаете, ну, как все нормальные ученые делают.
                                                                      Сформулировать гипотезу, вывести следствия, верифицировать, указать условия, при котором мы считаем гипотезу разрушенной, сделать эксперимент на это, и таким образом показать фальсифицируемость.

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

                                                                      Второй эксперимент — возьмем обфусцированный код из первого эксперимента, но теперь вернем нормальные названия для папок. Опять сравним время на поиск.
                                                                      Если люди без папок не будут сильно проигрывать — признаю что не прав.
                                                                        0
                                                                        Конечно нельзя. Я описал лишь одну из подзадач модификации. Самую простую и очевидную.

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


                                                                        То, что одна из подзадач может быть описана математически — свидетельство в пользу того, что задача целиком тоже может быть так описана.

                                                                        А я пока не уверен, что вы описали (под)задачу математически.


                                                                        Поэтому если мы моделируем процесс с ошибками, то придется подрубить матстат к обычной теории вычислительной сложности.

                                                                        Ну то есть незаметно добавить еще допущений. Не описав их, конечно же.


                                                                        Расскажите хотя бы откуда вы логарифм взяли.


                                                                        Я из этого совсем не четко сформулированного определения предположил, что вы решили сделать Map. Потому что по вашему описанию, у вас есть функция предположения, которая из какого-то элемента требований получает название элемента кода.
                                                                        Т.Е. ваш «элемент требований» является ключем, «название элемента кода» — значением, и ключ отображается в значение за счет функции «предположить»

                                                                        Ну да, так и есть.


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

                                                                        Вот только не по всему множеству, а по возможным ключам. А потом по использованиям возможных ключей. И с неточными совпадениями.


                                                                        Я могу составить модель процесса поиска файла, и да, посчитать и доказать ее сложность от различных начальных параметров.

                                                                        … и сколько из этих параметров вам известно?


                                                                        Например, если мы поставим эксперимент формата

                                                                        Проблема с этими экспериментами состоит в том, что они далеки от реальности.


                                                                        В частности, вы предполагаете, что в папках ошибок нет… а почему?

                                                                          +1
                                                                          Вот только не по всему множеству, а по возможным ключам. А потом по использованиям возможных ключей. И с неточными совпадениями.

                                                                          И из какой шляпы вы сейчас достали возможные ключи?)

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

                                                                          Так, может я реально ошибся с логарифмом. Ща прикину. Ну, окей, на каждой папке, которая является узлом дерева у нас есть список следующих узлов. Обозначим количество узлов за n, среднее число узлов в списке за p. Глубина дерева получится порядка logp(n), заходим в первый узел- среднее время выбора следующего — p/2, идем в следующий узел, повторяем logp(n) раз, потому что это глубина дерева. Итого — в среднем p/2*logp(n), где p среднее количество узлов в списке связей каждого узла, а n — количество узлов. Если P не является функцией от N, то на больших N можно им вообще принебрегать, логарифмическая ассимптотика не поменяется. Если является — окей, не логарифм, а f(n)*logf(n)(n), где f(n)<n (потому что если равно, то это все равно что все пихать в один узел).

                                                                          Проблема с этими экспериментами состоит в том, что они далеки от реальности.
                                                                          В частности, вы предполагаете, что в папках ошибок нет… а почему?

                                                                          И это отличная идея, для еще одного эксперимента!

                                                                          … и сколько из этих параметров вам известно?

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

                                                                          Или вы опять пытаетесь утверждать, что нельзя посчитать сложность поиска в абстрактной структуре данных, не зная конкретный искомый элемент?) Да — нельзя. Дать оценку в зависимости от количество элементов структуры и того, как выглядит на ней алгоритм поиска — можно.
                                                                            +1
                                                                            И из какой шляпы вы сейчас достали возможные ключи?

                                                                            Из той же, из которой вы достали названия папок.


                                                                            Обозначим количество узлов за n, среднее число узлов в списке за p.

                                                                            Вы же понимаете, что это не то же самое n, которое число элементов, среди которых мы ищем?


                                                                            На примере: вот у вас есть восемь элементов (например, для простоты, классов). Если они все принадлежат к одной группе (например, домену), то у вас добавляется всего один узел — domain, и n=9. Но если у вас домен побит (внутри себя) на два поддомена (sales и purchases), то у вас n=11. А если из этих классов половина относится к UI (по тем же поддоменам), то вы получаете n=14. Но при этом количество элементов кода, среди которых идет поиск, все равно 8!


                                                                            Аналогичное рассуждение применимо и к высоте дерева — она определяется не числом элементов, а исключительно принятой конвенцией. Если мы добавим еще два поддомена, эффективно увеличив число элементов вдвое, глубина дерева не изменится.


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


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

                                                                            Еще вас, как мы только что выяснили, интересует структура дерева.


                                                                            Дать оценку в зависимости от количество элементов структуры и того, как выглядит на ней алгоритм поиска — можно.

                                                                            В том-то и дело, что мы, на самом деле, не знаем, какой алгоритм поиска.


                                                                            Например, вы исходите из предположения, что время поиска на каждом уровне имеет одну и ту же константу при линейной асимптотике (и это та же константа, что и происке нужного объекта перебором). Но это не так — вы на каждом уровне сравниваете разные характеристики объекта (в примере выше на первом уровне вам надо понять, ui это или domain, на втором — sales или purchases), и принятие этого решения не сводится к константе.

                                                                              0
                                                                              Вы же понимаете, что это не то же самое n, которое число элементов, среди которых мы ищем?
                                                                              На примере: вот у вас есть восемь элементов (например, для простоты, классов). Если они все принадлежат к одной группе (например, домену), то у вас добавляется всего один узел — domain, и n=9. Но если у вас домен побит (внутри себя) на два поддомена (sales и purchases), то у вас n=11. А если из этих классов половина относится к UI (по тем же поддоменам), то вы получаете n=14. Но при этом количество элементов кода, среди которых идет поиск, все равно 8!

                                                                              Блин, вот это я налажал. Да, вы правы, нельзя брать количество узлов, надо считать от листьев.

                                                                              Интересно, сильно ли это меняет ситуацию? Вот ваше количество элементов — 8.
                                                                              Вот мы разбили их на два поддомена — каждый по 4. И еще разок — теперь у нас 2+4+8 элементов в дереве. А шагов по уровням сколько, для навигации до нужного элемента?
                                                                              1+1+1 = 3. Если я не ошибаюсь, это логарифм от 8 по двум?)
                                                                              И, кажется это всегда будет так, учитывая что на следующем уровне дерева можно расположить в p раз больше элементов, чем на предыдущем. Т.Е. картину не особо меняет. Кажется, константу добавит, плюс один шаг нужен вглубь нужен.
                                                                              Окей, формула: p/2*(logp(n)+1).
                                                                              Ну, да, кстати, логично. Чтобы перейти в 1 файл в 1 папке нужно 2 шага — открыть папку, открыть файл.

                                                                              Аналогичное рассуждение применимо и к высоте дерева — она определяется не числом элементов, а исключительно принятой конвенцией. Если мы добавим еще два поддомена, эффективно увеличив число элементов вдвое, глубина дерева не изменится.


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

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


                                                                              Да, я согласен, что у меня не дерево поиска. Поэтому я и не стал говорить, что это «логарифм по 2, потому что это дерево поиска», а занялся прикидками.

                                                                              Еще вас, как мы только что выяснили, интересует структура дерева.

                                                                              Бинарное дерево тоже умеет вырождаться в список. И быть несбалансированным. Именно поэтому есть средний и худший случай. У меня тоже есть худший, и он тоже, как ни странно O(n)

                                                                              В том-то и дело, что мы, на самом деле, не знаем, какой алгоритм поиска.
                                                                              Например, вы исходите из предположения, что время поиска на каждом уровне имеет одну и ту же константу при линейной асимптотике (и это та же константа, что и происке нужного объекта перебором). Но это не так — вы на каждом уровне сравниваете разные характеристики объекта (в примере выше на первом уровне вам надо понять, ui это или domain, на втором — sales или purchases), и принятие этого решения не сводится к константе


                                                                              На достаточно большом дереве на это можно забить. На маленьком — да, имеет вес. Но врятли это что-то большее чем константный множитель на шаге выбора из детей узла.
                                                                              У обычных алгоритмов есть похожие «девиации» поведения, когда сортировка вставками уделывает quickSort на мелких данных за счет констант. А еще архитектура проца роль играет в том, какие операции исполняются эффективнее. И ничего, живем как-то, и почему-то все еще учитываем при написании кода вычислительные сложности, не смотря на, простите за каламбур, сложности)
                                                                                –1
                                                                                Вот мы разбили их на два поддомена — каждый по 4. И еще разок — теперь у нас 2+4+8 элементов в дереве. [...] И, кажется это всегда будет так, учитывая что на следующем уровне дерева можно расположить в p раз больше элементов, чем на предыдущем.

                                                                                Нет, не всегда будет так. Число элементов на уровне дерева определяется не математическими ограничениями, а семантикой.


                                                                                В этом и фича. Вы добавили еще элементов — глубина не изменилась, количество шагов все еще зависит от глубины дерева.

                                                                                … но количество операций на уровне зависит от числа элементов на уровне, а не от глубины дерева. Поэтому общее число операций изменилось.


                                                                                Именно поэтому есть средний и худший случай. У меня тоже есть худший, и он тоже, как ни странно O(n)

                                                                                … где n — не число элементов в изначальном множестве, а число элементов плюс число промежуточных узлов.


                                                                                Впрочем, интереснее то, какой случай средний.


                                                                                На достаточно большом дереве на это можно забить.

                                                                                Неа, нельзя — по крайней мере, пока не показано явно, что вес этих операций пренебрежим по сравнению с остальными.

                                                                                  0
                                                                                  Нет, не всегда будет так. Число элементов на уровне дерева определяется не математическими ограничениями, а семантикой


                                                                                  Так, я кажется понял в чем проблема: Я говорю, если строить дерево вот с такими ограничениями, то поиск ассимптотически стремится к логарифму.
                                                                                  Вы говорите о том, что если строить дерево по другому, то не будет? — Согласен, если строить дерево по другому — то может и не будет.
                                                                                  Если в бинарное дерево поиска пихать элементы строго в порядке их убывания/возрастания и не балансировать — то там тоже не получится логарифм для поиска, оно в список выродится. Его можно «строить не правильно». Однако, если принять, что мы не строим дерево «специально не правильно», а раскидываем элементы случайно, в среднем у нас получается logN.

                                                                                  … но количество операций на уровне зависит от числа элементов на уровне, а не от глубины дерева. Поэтому общее число операций изменилось.

                                                                                  Да, и я дал вам формулу, от числа детей узла — p и количества листьев — n.

                                                                                  … где n — не число элементов в изначальном множестве, а число элементов плюс число промежуточных узлов.
                                                                                  Впрочем, интереснее то, какой случай средний
                                                                                  .
                                                                                  Ага, только число промежуточных узлов, для случая когда у нас поиск идет линейно — равно 1. Все лежит в одном домене. И я даже добавил эту единицу в формулу)
                                                                                  Средний случай, как и в случае бинарного дерева определяется тем, что при бесконечном числе вставок дерево будет заполняться равномерно.

                                                                                  Неа, нельзя — по крайней мере, пока не показано явно, что вес этих операций пренебрежим по сравнению с остальными.

                                                                                  При оценке ассимптотики на бесконечности — можно забивать на множители. Вот если количество элементов в одном домене — функция от количества листьев — тогда да, нельзя.
                                                                                  О малых n я отдельно упомянул, и сказал, что в этом случае на множитель забить нельзя. Как и в случае, когда на малых n квадратичные сортировки уделывают quick.

                                                                                    0
                                                                                    Однако, если принять, что мы не строим дерево «специально не правильно», а раскидываем элементы случайно, в среднем у нас получается logN.

                                                                                    Но для семантического дерева поиска элементы раскиданы не случайно.


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

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


                                                                                      Поиграйтесь сами:
                                                                                      www.desmos.com/calculator/tkfwa8r8tp

                                                                                      Красный график — линейная сложность. В среднем поиск в списке займет x/2 итераций.

                                                                                      Фиолетовый график — Охренительно плохой случай, когда мы можем создать всего n папок и распихать элементы по ним примерно одинаково. Т.Е. у нас есть линейная зависимость количества детей узла, от количества элементов. Спойлер — даже в таком варианте оно уделает линейный случай, начиная с n = 2.

                                                                                      Черный график — случай, когда мы можем нормально составить иерархию, как я описывал, т.е. у каждого узла ровно n детей.

                                                                                      А теперь немного математической магии: Все ваши описания неидеальности моего метода находятся в промежутке от моего худшего случая, до лучшего. Вообще все. Что бы вы не придумали, какую бы схему дерева не предложили — ее график зажат между графиками худшего и лучшего случая. Даже если вы захотите «добавить штраф» к выбору папки, он не изменит ассимптотику.
                                                                                      И мой худший случай — это когда ни для каких элементов нельзя выделить признак, по которому их можно сгруппировать. И тогда у нас все в одной папке — т.е. сложность ассимптотически x/2.

                                                                                      Даже если вы в этой папке, создадите подпапку и положите в нее всего 2 элемента — вы не измените сложность. Вы добавили 1 переход в глубину. Добавили один выбор элемента из двух. т.е. два шага. Вы уменьшили основной список на 2 элемента. Теперь вам нужно пройти 1 список из n-2 элементов, и если в нем не найдется нужный элемент (что произойдет с вероятностью 2/общее количество элементов) — вам придется сделать еще 2 шага. Средняя сложность (матожидание сложности, ведь матожидание это тоже самое что среднее для бесконечной выборки) — n-1/2 + 2*2/n. Кажется, последним членом можно принебречь, он стремится к нулю на бесконечности. Если вы создадите еще одну такую папку — у вас появится лес из 2 папок по два элемента, в который вы придете с вероятностью 4/n. И в этом лесу вы потратите 1 + 1 шаг. И опять за счет малой вероятности того, что ваш элемент находится в этом дереве — его вклад в среднее — стремится к нулю. Если вы сделаете так еще раз — получите вероятность 6/n в лесу из трех мини деревьев. Количество шагов в нем — 1.5+1. Я прикинул формулу для расчета среднего количества шагов в таком случае — она зеленая в ссылке выше. Параметры k и p — это количество папок и количество элементов в папке. Разумеется, смотреть нужно с момента когда x>k*p, до этого там будет какой-то бред ибо мы пытаемся создать папки для несуществующих элементов. И ассимптотика в этом случае все еще линейная.

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

                                                                                      Так что там не будет работать в реальности?) Если вы сейчас в этой самой реальности, не сможете придумать контрпример, в котором разбиение на папки будет отличаться в худшую сторону хотя-бы множителем?)
                                                                                        0
                                                                                        Если вы сейчас в этой самой реальности, не сможете придумать контрпример, в котором разбиение на папки будет отличаться в худшую сторону хотя-бы множителем?)

                                                                                        В том-то и дело, что могу. Достаточно положить по одному элементу в папку, чтобы в случае перебора получить 2n вместо n (и это если исходить из того, что стоимость сравнения для папки такая же, как для элемента, что не данность). Два уровня вложенности, по одному элементу в папке нижнего уровня? 3n.

                                                                                          –1
                                                                                          Поржал.
                                                                                          Нет, серьезно?) Вам настолько не терпится доказать мне что-то, что вы, кхм решили подогнать под определение «дерева» заранее абсурдную структуру? Вы предлагаете как контрпример создавать папки с 1 ребенком?) Нет, я думал написать, то в папке должно быть минимум 2 элемента, но решил что это излишне, ибо ни один дурак не будет так делать. Это, кхм, абсурдно, даже исходя из интуиции. Вашим образом можно доказать что поиск одного элемента в случае папок стремится к бесконечности) Кстати, моя формула это тоже учитывет, ибо предел логарифма по основанию k где k стремится к 1 — стремится к бесконечности. Можете снять ограничения с графиков, что я вам скинул, да посмотреть — там есть поведение в таком случае))

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

                                                                                          Нет, вам серьезно не понятно, что если список — это вырожденное дерево, то поиск в любой структуре, кхм, промежуточной, между деревом и списком, ассимптотически по сложности тоже будет лежать между поиском в дереве и поиском в списке?) Даже, если строить дерево «над» списком.

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

                                                                                          Притом вы даже не стараетесь. Вы могли сказать «Эй, парень, но для перехода по папкам требуется намного больше времени, это же кликнуть на папку и ждать пока она откроется. А поскольку файлов у нас не бесконечность, надо смотреть как ведет себя функция в таких условиях на сравнительно небольших числах» — и я бы добавил множитель, и мы бы посмотрели как ведет себя функция (спойлер — с таким условием, при маленьком количестве файлов разбивать их на папки действительно нельзя).

                                                                                          Мне нравится с вами дискутировать. Несмотря на то, что иногда у вас иногда не очень с аргументами — в целом, вы неплохой собеседник. У вас хотя-бы есть достаточно терпения.
                                                                                          Но если вы заранее для себя определили, что «разбиение на папки — не работает», какой смысл убеждать вас в обратном? Если вы не будете пытаться вместе со мной ответить на вопрос «а работает ли?», чем я собственно тут и занимаюсь, а уже заранее записали для себя «верный» ответ, и теперь на каждое свидетельство против вашего убеждения ищете отмазку? Каков критерий для фальсификации вашей гипотезы?
                                                                                            0
                                                                                            Вам настолько не терпится доказать мне что-то, что вы, кхм решили подогнать под определение «дерева» заранее абсурдную структуру?

                                                                                            А откуда вы взяли определение "дерева", которое используется в реальной задаче распределения объектов во время разработки ПО, а не как структура данных?


                                                                                            ибо ни один дурак не будет так делать. Это, кхм, абсурдно, даже исходя из интуиции.

                                                                                            Да нет, не абсурдно. Поймите, это дерево состоит из синтаксических элементов. Возьмем какой-нибудь дурацкий MVC. Верхний уровень: ui. Под ним три элемента: models, views, controllers. Добавляем первую триаду: в каждом элементе появилось по одной имплементации. Абсурдная ситуация?


                                                                                            как я математически показал всю ассимптотику.

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


                                                                                            Но если вы заранее для себя определили, что «разбиение на папки — не работает», какой смысл убеждать вас в обратном?

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


                                                                                            Каков критерий для фальсификации вашей гипотезы?

                                                                                            Повторимый эксперимент, соотносимый с реальным опытом разработчика.

                                                                                              0
                                                                                              А откуда вы взяли определение «дерева», которое используется в реальной задаче распределения объектов во время разработки ПО, а не как структура данных?


                                                                                              Эм. Дерево — это связный граф, без циклов. Вы предложили набор отдельных списков. Он не связный. Это лес.
                                                                                              И, да, дерево, с одним элементом у каждого родителя — это… список. Список, конечно технически является деревом. Но, мне кажется, что исходя из контекста диалога, можно было понять, что я не предлагаю строить дерево так, чтобы в глубину получать списки. В этом суть дерева, как структуры данных — не быть списком.

                                                                                              Да нет, не абсурдно. Поймите, это дерево состоит из синтаксических элементов. Возьмем какой-нибудь дурацкий MVC. Верхний уровень: ui. Под ним три элемента: models, views, controllers. Добавляем первую триаду: в каждом элементе появилось по одной имплементации. Абсурдная ситуация?


                                                                                              Ну, как сказать. Если у вас 1 модель, 1 view и 1 контроллер — да. Зачем создавать папки для одного элемента? Даже судя по моему графику, без штрафа на переход, это имеет смысл только ближе к 8 элементам.

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


                                                                                              Вы кажется не понимаете зачем математика нужна. Если я определил как действует сложение — мне не нужно проверять, что оно работает на слонах, черепахах, яблоках, карандашах и так далее. Я вывел правило по которому работает реальность.

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

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

                                                                                              Если что-то не соответсвует опыту — так давайте поработаем, и уточним мою модель. Вам самому не интересно узнать, в какой момент нужно создавать папку? Мне вот очень интересно, поэтому я тут и сижу.

                                                                                              Повторимый эксперимент, соотносимый с реальным опытом разработчика.

                                                                                              Конкретнее, пожалуйста. Если вы не придумали этот эксперимент — у вас пока нет критерия фальсификации. А пока вы мне не описали эксперимент — я не могу сказать, действительно ли он хорош)
                                                                                                0
                                                                                                Вы предложили набор отдельных списков. Он не связный. Это лес.

                                                                                                Добавьте к этому набору общий корневой элемент. Это не нарушит ни логику, ни асимптотику.


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

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


                                                                                                Если у вас 1 модель, 1 view и 1 контроллер — да. Зачем создавать папки для одного элемента?

                                                                                                Затем, что:


                                                                                                (а) переделывать потом дороже
                                                                                                (б) такой стартовый шаблон


                                                                                                Если я определил как действует сложение — мне не нужно проверять, что оно работает на слонах, черепахах, яблоках, карандашах и так далее. Я вывел правило по которому работает реальность.

                                                                                                Вы известную фразу про "1+1" слышали?


                                                                                                Вы можете указать мне на них, я могу уточнить свою модель.

                                                                                                Ну так уже указывал же: стоимость выбора (операции сравнения) в каждом узле может отличаться, вероятность ошибки ненулевая — и тоже разная в каждом узле.


                                                                                                Вам самому не интересно узнать, в какой момент нужно создавать папку? Мне вот очень интересно, поэтому я тут и сижу.

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


                                                                                                А теперь попробуйте это формализовать математически.


                                                                                                Если вы не придумали этот эксперимент — у вас пока нет критерия фальсификации.

                                                                                                Ну да. Моя теория не научна.


                                                                                                Если что-то не соответсвует опыту — так давайте поработаем, и уточним мою модель.

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


                                                                                                И есть некий поддерживающий опыт, состоящий в том, что я (почти) перестал пользоваться папками в почте, предпочитая им поиск.

                                                                                                  0
                                                                                                  Понимаете, вы смотрите на это дерево с точки зрения абстрактной структуры данных. А я — с точки зрения разработчика или архитектора, которому описывать, как это дерево заполняется и как в нем искать.


                                                                                                  Мне кажется, что одно другому не мешает. Или вы думаете, что я этим так интересуюсь в академических целях, а не потому, что меня чертовски волнует вопрос «когда создавать новый уровень иерархии»? Просто я знаю, что я, со своими наблюдениями — ошибаюсь. А вот за математикой я такого не замечал. Всегда работает. Так что проверить интуитивный вывод математической прикидкой мне кажется очень здравой идеей. Иногда можно получить даже больше чем планировал.

                                                                                                  Затем, что:
                                                                                                  (а) переделывать потом дороже
                                                                                                  (б) такой стартовый шаблон


                                                                                                  Но если генерализовать подход «переделывать дороже» — вы получите избыточную архитектуру. Согласитесь, такое случается. Вам, например не мозолит глаз MVC-шная иерархия, для задачи формата «посеттить константную строку в текстовое поле»? Когда создается 3 интерфейса и 3 имплементации, просто потому что такой шаблон, и «если нам когда-то потребуется расширить класс».
                                                                                                  И знаете что интересно. В половине случаев ничего не расширяется. Но получив задачу «обновить строку», человек продирается сквозь чертову кучу шаблонного и абсолютно ненужного кода, потому что забыл как называлась константа в ресурсах, и теперь хочет узнать название.
                                                                                                  Короче, мне кажется, что ленивая инициализация изменений будет выигрывать. Это интуитивное предположение, не претендую на истинность.

                                                                                                  Вы известную фразу про «1+1» слышали?

                                                                                                  Видимо, нет) Поделитесь.

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


                                                                                                  Вы составляете предикаты «принадлежности объекта узлу», и такой предикат должен существовать для каждой пары «лист»-«промежуточный узел»? И, учитывая что это должно работать всегда, и эффективно, то скорее всего, у вас есть сет качественных признаков объекта, а каждый промежуточный узел образуется понятием, которое включает в себя все, что свойственно элементам внутри него. Иными словами — любой промежуточный узел в вашей иерархии это некое обобщенное свойство всех листов ему принадлежащих.
                                                                                                  В таком случае, на каждой развилке человек может сделать правильный выбор.

                                                                                                  Ну так уже указывал же: стоимость выбора (операции сравнения) в каждом узле может отличаться, вероятность ошибки ненулевая — и тоже разная в каждом узле.

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

                                                                                                    Проблема в том, что математика "всегда работает", решая поставленную ей задачу. Вопрос в том, как правильно поставить задачу.


                                                                                                    Но если генерализовать подход «переделывать дороже» — вы получите избыточную архитектуру.

                                                                                                    Вот именно поэтому нет серебряной пули.


                                                                                                    Видимо, нет) Поделитесь.

                                                                                                    "В математике все просто — два прибавить к двум всегда равно четырем, никаких сложностей. А в жизни даже один прибавить к одному непонятно: то ли семья, а то ли заговор с целью ограбления банка".


                                                                                                    Иными словами — любой промежуточный узел в вашей иерархии это некое обобщенное свойство всех листов ему принадлежащих.

                                                                                                    Угу. А теперь, зная это, давайте ответим: сколько же уровней в иерархии?


                                                                                                    Ну, можно просто провести эксперимент, и понимая общую зависимость подобрать недостающие константы.

                                                                                                    Но как понять, что зависимость правильная?

                                                                                                      0
                                                                                                      Я не осилил дочитать эту ветку до конца, но, проходя мимо, замечу, что некоторые IDE (например, Visual Studio) совершенно точно (и это очень очевидно даже если не лезть в исходники) используют знание о разложении кода по файлам, а файлов по папочкам, а так же семантику папочек, для ускорения некоторых видов поиска, в том числе тех, которые нужны для автоматизированных модификаций-рефакторингов.

                                                                                                      UPD. Пожалуйста, раскладывайте код так как принято в приличных домах, сделайте жизнь вашей IDE проще :)

                                                                                                      UPD. Ну и да, использовать для поиска по проекту дерево как структуру данных — совершенно дурацкая затея.
                                                                                                        0

                                                                                                        Суть, кратко:


                                                                                                        Речь шла только о том, что когда попытка использовать мапу через названия провалена (т. е. когда вы не знаете как назвали то, что вы ищете), то бродить по дереву лучше чем по списку.


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


                                                                                                        lair, как я его понял, утверждает что я слишком упрощаю, и в реальном случае это не работает. При этом сам он тоже поддерживает идею раскладывания по папкам.
                                                                                                        Но это моё понимание того, что он говорит, так что я могу ошибаться.


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

                                                                                                          0
                                                                                                          Спасибо, теперь гораздо понятнее о чем шла речь.

                                                                                                          Вы назвали папки не абы как и человек может выбрать из них нужную.


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

                                                                                                          И даже если такое правило можно придумать — в реальном мире он будет нарушаться, а при любом нарушении придется обходить дерево полностью.

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

                                                                                                          Ну и для совсем общего поиска все-таки выгоднее построить нормальный индекс.

                                                                                                          UPD Короче, я согласен что поиск в дереве в общем случае имеет меньшую сложность чем линейный поиск по списку (это правда не означает, что он всегда быстрее), но конкретно обсуждаемая задача в реальном мире решается по другому.
                                                                                                    0
                                                                                                    Ну вот есть конкретный опыт, состоящий в том, что при поиске в проекте, в котором есть инструментальная поддержка, я подавляющее большинство случаев ищу объекта по связям. Ну то есть, если мне надо найти контроллер, название которого я не знаю, я возьму базовый класс для всех контроллеров и посмотрю всех его наследников, а не буду перебирать все компоненты, в которых потенциально могут быть контроллеры, в поисках папочки controllers, в которой может быть есть нужный мне контроллер.


                                                                                                    Так вы же и тут пользуетесь иерархической структурой. Просто другой. Не папочками, наследованием. И да, согласен, если у вас есть элемент в иерархии, в который вы можете перейти за O(1), и который при этом находится близко к искомому — это эффективно. Для того чтобы это описать, можно просто перейти из плоскости в объем, и описывать не только иерархическую структуру папочек, но и наследования. И разумеется есть некоторая вероятность за O(1) попасть в «ближайшего соседа». А потом можно добавить еще одну размерность — это будут использования. Я вот например частенько помню, что «кажется, я использовал нужную мне штуку воот в том классе» и затем ищу ее в нем.

                                                                                                    И есть некий поддерживающий опыт, состоящий в том, что я (почти) перестал пользоваться папками в почте, предпочитая им поиск.

                                                                                                    Пытаетесь вспомнить смысл того что было в письме и перебираете «ключи»? Это вы имели ввиду под «возможными ключами»?
                                                                                                    Согласен — отличное решение, когда вы примерно можете предположить что ищете. Думал о том, что было бы круто, если в поиске IDE была галочка «синонимы», чтобы самому их в мозгу не перебирать.
                                                                                                      0
                                                                                                      в котором есть инструментальная поддержка, я подавляющее большинство случаев ищу объекта по связям


                                                                                                      А инструментальная поддержка легко может учитывать, что сильно связанные компоненты скорее всего лежат где-то рядом и начать искать с текущей, а потом соседних папочек. Собственно, студия так и ищет :)
                                                                                                        0
                                                                                                        Так вы же и тут пользуетесь иерархической структурой. Просто другой.

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


                                                                                                        Пытаетесь вспомнить смысл того что было в письме и перебираете «ключи»? Это вы имели ввиду под «возможными ключами»?

                                                                                                        Ну да.


                                                                                                        отличное решение, когда вы примерно можете предположить что ищете

                                                                                                        То есть в подавляющем большинстве случаев.

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

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

                                                                                                          Но я бы не стал пользоваться для поиска графом с циклами. Я слишком глупый для этого. Оперативка слишком маленькая для всяких DFS в циклических структурах. Через 5 минут я уже забуду, что в этом классе я уже был. И если путь меня опять приведет к нему, я зайду туда, пойму что такое уже видел, что ищу не там, и меня это весьма разочарует.

                                                                                                          То есть в подавляющем большинстве случаев.

                                                                                                          Мы вроде как и начинали с того, что когда у вас есть возможность воспользоваться мапой — это хорошо. Когда нет — это плохо, вот тогда может пригодиться дерево.

                                                                                                          На вскидку, пример из жизни — вам приходит новый человек и вам надо познакомить его с проектом:
                                                                                                          Вот здесь у нас классы для extension функций и утилит, а вот тут транспорт лежит, а вот здесь — наши экраны приложения, а каждый экран — это…

                                                                                                          Мне было бы странно показывать новому коллеге 1000+ файлов с кодом в одном каталоге. Возможно я недостаточно продвинутый, но если бы мне такое показали — я бы прифигел. Даже если бы мне обещали, что угадать, как называется нужный тебе файл, очень легко. Обычно это легко тому, кто этот проект писал, а не мне, который вообще не в курсе что тут происходит)
                                                                                                            0
                                                                                                            Я говорил про ваш конкретный пример, с переходом в базовый класс. В это примере, вроде как, не может быть циклов в иерархии, и поэтому там дерево.

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


                                                                                                            Но я бы не стал пользоваться для поиска графом с циклами.

                                                                                                            Ну вот вы бы не стали, а я пользуюсь.


                                                                                                            На вскидку, пример из жизни — вам приходит новый человек и вам надо познакомить его с проектом:

                                                                                                            Но это уже совсем другая задача, нежели поиск информации. И это одна из тех задач, для которых иерархия работает неплохо… пока удается найти один непротиворечивый способ ее огранизации.

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

                                                                                                              Согласен. Привык думать о наследовании, как о направленной иерархии, без возможности «скрещивать» потомков. Если такая есть в конкретной имплементации — окей, не дерево.

                                                                                                              Ну вот вы бы не стали, а я пользуюсь.

                                                                                                              Не возникает проблемы, с тем, что не помните где уже были, на больших объемах данных?
                                                                                                                +1
                                                                                                                Не возникает проблемы, с тем, что не помните где уже были, на больших объемах данных?

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

                                                                      0

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

                                                                        0
                                                                        Да, можно. В матстатистике есть возможность оперировать с качественными а не количественными величинами. Вроде цвета глаз — его числом не выразишь, да и не зачем. Просто об этом не написано на первой строке в Википедии)

                                                                        Что значит по вашему «не является величиной»? Все что можно ощутить является как минимум альтернативной величиной — ощущение есть или его нет)
                                                                          0
                                                                          Да, можно. В матстатистике есть возможность оперировать с качественными а не количественными величинами.

                                                                          Они от этого не перестают быть величинами. А я спросил о том, что не является величиной. Например, программный продукт — это величина?


                                                                          Все что можно ощутить является как минимум альтернативной величиной — ощущение есть или его нет

                                                                          У вас — есть или нет, а у меня — желтое, синее и абрикосовое. Бессмысленно говорить об этом, как о величине, если у нее нет ни определенного множества значений, ни соглашения о допустимых операциях, ни возможности однозначного определения значения.

                                                                            0
                                                                            Желтое, синие и абрикосовое — тоже величины, только они называются атрибутивными!)
                                                                            Ладно, шучу.

                                                                            А почему вы уверены, что нельзя построить множество? Знаете, есть такая штука, теория потребления. И в ней есть «отношение предпочтения». Строится только на том, что человек может сравнить вещи. Не численно выразить разницу — просто сравнить.
                                                                            Абстрактный потребитель, абстрактные вещи.
                                                                            Еще там же, есть определение функции полезности. Тоже для абстрактного потребителя, на абстрактных товарах, и т.д.

                                                                            А теория игр оперирует с абстрактными выигрышами. Они тоже заданы через оценку самим участником игры, вроде как. Если я правильно помню, то например в «Теории игр и экономическом поведении» Фон-неймана и Моргенштерна, оно введено как раз через отношение предпочтения и полезность.

                                                                            Чем же так сильно отличается ситуация с предпочтениями когда дело касается ПО?)
                                                                              0
                                                                              А почему вы уверены, что нельзя построить множество?

                                                                              Можно. Но для этого надо согласиться. А наши с вами два множества не имеют ни одного общего элемента, а для одного и того же объекта мы выдаем в качестве значения "величины" разные элементы.


                                                                              Чем же так сильно отличается ситуация с предпочтениями когда дело касается ПО?

                                                                              Тем, что мы — вроде бы — говорим не о предпочтениях, а об измеримом экономическом эффекте.

                                                                                0
                                                                                Тем, что мы — вроде бы — говорим не о предпочтениях, а об измеримом экономическом эффекте.


                                                                                Процитирую вас же:
                                                                                Отдельный смешной вопрос состоит в том, можно ли вообще говорить о «корреляции» между понятиями, которые вообще-то не являются случайными величинами (и вообще не являются величинами).


                                                                                Так что об экономическом эффекте я в этой ветке не говорю, вы же сами обозначили эту ветку как «отдельную».

                                                                                Я обсуждаю матстат, корреляцию и математические подходы для определения всякой нечеткой фигни, вроде «нравится», «качественно», «полезно», чтобы в дальнейшем с нею работать математическими же методами.

                                                                                А измеримый экономический эффект — вроде как вполне измерим и является величиной даже по вашим критериям, зачем тащить его сюда? Для этого обсуждения есть основная ветвь.

                                                                                В общем, SRP нарушаете)
                                                                                  –1
                                                                                  Я обсуждаю матстат, корреляцию и математические подходы для определения всякой нечеткой фигни

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

                                                                                    +1
                                                                                    Мда. Для начала я не понимаю о каких величинах вы говорите. Или не величинах.

                                                                                    Во вторых — если для каких-то объектов можно определить отношение НЕравенства и сложение — тадам, это уже величина.

                                                                                    В третьих, матстат работает с такими штуками как качественные признаки. Признаюсь, ошибся словом, сейчас посмотрел. Количественная величина, качественный признак.
                                                                                      0
                                                                                      Для начала я не понимаю о каких величинах вы говорите. Или не величинах.

                                                                                      Читаемость кода. Поддерживаемость системы.


                                                                                      Во вторых — если для каких-то объектов можно определить отношение НЕравенства и сложение — тадам, это уже величина.

                                                                                      Ну вот для двух примеров выше нельзя определить понятие сложения.


                                                                                      В третьих, матстат работает с такими штуками как качественные признаки. Признаюсь, ошибся словом, сейчас посмотрел. Количественная величина, качественный признак.

                                                                                      Работает, да. Насколько я помню, для них даже тоже есть понятие корреляции.


                                                                                      Но качественные признаки (я, правда, привык о них думать, как о категориальных переменных, но не суть) все равно надо формализовывать. И именно тут начинается основная проблема.

                                                0
                                                Вы заметили разницу в восприятии двух абзацев?
                                                Вы забыли разнести все предложения по разным файлам, заменить конкретику на абстракции, обернуть все в фабрики и связать это все DI-контейнером с конфигом в xml.
                                                  0

                                                  Неплохим, хотя и слабоватым, примером этого будет чтение БСЭ или "Алисы в Стране Чудес" в переводе Демуровой.

                                              0

                                              Интересно, есть какие то реализации анализаторов по методу описанному в статье? может быть в виде плагина к IDE..

                                                0
                                                Несмотря на обилие метрик, не существует какого-то единственного способа для вычисления сопряжения и связности модулей.
                                                Отчасти это связано с тем, что это рекомендации, а не закон. У них нет даже границ применения. И они косвенно влияют на коммерческий успех. Так же и SRP — это рекомендация.
                                                Если абстрактно совсем, то DTO — это нарушение ОО парадигмы. Даже если разработать метрики по нарушению ООП, не факт, что от DTO откажутся. Отказ приведет к увеличению сложности и времени разработки и сопровождения (здесь мое мнение). Метрики не могут показать как конкретная команда изменит свою эффективность применяя эти рекомендации.
                                                  0
                                                  Существуют метрики измерения эффективности работы команды. Сначала вводим метрики измерения качества кода, потом измеряем как это сказалось на работе команды.

                                                Only users with full accounts can post comments. Log in, please.