Зависимости между классами и интерфейсами
От: AlexRK  
Дата: 15.09.20 11:36
Оценка:
Приветствую.

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

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

Вопрос у меня таков: насколько сильным вы считаете запрет на любые циклы в графе классов и интерфейсов?

С двумя важными дополнениями/исключениями:
1) Класс/интерфейс может использовать сам себя (то есть можно создать связанный список или дерево).
2) Запрет на циклы — только на уровне объявлений. То есть физически цикл создать можно, передав объект через, скажем, генерик-параметр. Но сами объявления циклическими быть не могут. (В расте все наоборот — объявления можно делать циклическими, но физически циклы без unsafe запрещены.)

Насколько плоха такая организация классов/интерфейсов в виде дерева, без циклов?
Re: Зависимости между классами и интерфейсами
От: Qulac Россия  
Дата: 15.09.20 12:12
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Приветствую.


ARK>Мой вопрос может показаться довольно странным, абстрактным и/или нелепым, но я хотел бы услышать мнения.


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


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

ARK>Вопрос у меня таков: насколько сильным вы считаете запрет на любые циклы в графе классов и интерфейсов?


Он не нужен.

ARK>С двумя важными дополнениями/исключениями:

ARK>1) Класс/интерфейс может использовать сам себя (то есть можно создать связанный список или дерево).
ARK>2) Запрет на циклы — только на уровне объявлений. То есть физически цикл создать можно, передав объект через, скажем, генерик-параметр. Но сами объявления циклическими быть не могут. (В расте все наоборот — объявления можно делать циклическими, но физически циклы без unsafe запрещены.)

ARK>Насколько плоха такая организация классов/интерфейсов в виде дерева, без циклов?


По мне это не нужно.
Программа – это мысли спрессованные в код
Отредактировано 15.09.2020 12:14 Qulac . Предыдущая версия .
Re[2]: Зависимости между классами и интерфейсами
От: AlexRK  
Дата: 15.09.20 12:24
Оценка:
Здравствуйте, Qulac, Вы писали:

Q>Он не нужен.

Q>По мне это не нужно.

Мне как раз интересно — что плохого будет от такого запрета. Что важное нельзя будет написать? Сразу приходит на ум паттерн "родитель и его потомки" со ссылками друг на друга. Не самый лучший паттерн, на мой взгляд, так что и пофиг на него. Но наверняка есть что-то еще.
Re: DAG
От: Qbit86 Кипр
Дата: 15.09.20 12:42
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Насколько плоха такая организация классов/интерфейсов в виде дерева, без циклов?


Только это не дерево, а DAG (directed acyclic graph).

ARK>Вопрос у меня таков: насколько сильным вы считаете запрет на любые циклы в графе классов и интерфейсов?


Подобные ограничения возникают при разбиении на сборки (при разбиении солюшена на проекты). Когда-то слышал, что технически можно дабиться циклических ссылок между сборками/нетмодулями (это не точно); но стандартный инструментарий/IDE это запрещает.
Глаза у меня добрые, но рубашка — смирительная!
Re[2]: DAG
От: AlexRK  
Дата: 15.09.20 12:54
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Только это не дерево, а DAG (directed acyclic graph).


Да, верно, я ошибся.

ARK>>Вопрос у меня таков: насколько сильным вы считаете запрет на любые циклы в графе классов и интерфейсов?Q>Подобные ограничения возникают при разбиении на сборки (при разбиении солюшена на проекты). Когда-то слышал, что технически можно дабиться циклических ссылок между сборками/нетмодулями (это не точно); но стандартный инструментарий/IDE это запрещает.


Но все же в рамках проекта хоть какие-то циклы возможны. А что если их нет совсем (кроме двух указанных мной ограничений)? Можно это терпеть или нет?
Re[3]: Зависимости между классами и интерфейсами
От: · Великобритания  
Дата: 15.09.20 13:17
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK> Q>Он не нужен.

ARK> Q>По мне это не нужно.

ARK> Мне как раз интересно — что плохого будет от такого запрета. Что важное нельзя будет написать? Сразу приходит на ум паттерн "родитель и его потомки" со ссылками друг на друга. Не самый лучший паттерн, на мой взгляд, так что и пофиг на него. Но наверняка есть что-то еще.

Что первое в голову пришло. Типы Node и NodeList. У Node есть имя, т.п. и список детей NodeList, а так же какой-нибудь query может возвращать тоже NodeList который не Node.
avalon/2.0.6
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: Зависимости между классами и интерфейсами
От: AlexRK  
Дата: 15.09.20 14:24
Оценка:
Здравствуйте, ·, Вы писали:

ARK>> Мне как раз интересно — что плохого будет от такого запрета. Что важное нельзя будет написать? Сразу приходит на ум паттерн "родитель и его потомки" со ссылками друг на друга. Не самый лучший паттерн, на мой взгляд, так что и пофиг на него. Но наверняка есть что-то еще.

·>Что первое в голову пришло. Типы Node и NodeList. У Node есть имя, т.п. и список детей NodeList, а так же какой-нибудь query может возвращать тоже NodeList который не Node.

По идее это ведь может быть один Node, разве нет?
Re[5]: Зависимости между классами и интерфейсами
От: · Великобритания  
Дата: 15.09.20 16:30
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>>> Мне как раз интересно — что плохого будет от такого запрета. Что важное нельзя будет написать? Сразу приходит на ум паттерн "родитель и его потомки" со ссылками друг на друга. Не самый лучший паттерн, на мой взгляд, так что и пофиг на него. Но наверняка есть что-то еще.

ARK>·>Что первое в голову пришло. Типы Node и NodeList. У Node есть имя, т.п. и список детей NodeList, а так же какой-нибудь query может возвращать тоже NodeList который не Node.
ARK>По идее это ведь может быть один Node, разве нет?
class Node
{
  string name;
  int data;
  ...
  Node parent;//ссылка на себя
  NodeList children;
  NodeList ancestors; //вот даже так
}
class NodeList
{
  int size;
  Node get(int index);
...
}

class Document
{
  Node root;
  NodeList findNodesByName(...);// вот тут список нодов, но не нода, т.к. имени и прочего нет.
}
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Зависимости между классами и интерфейсами
От: Bill Baklushi СССР  
Дата: 15.09.20 16:31
Оценка:
AlexRK:

ARK>В мейнстримовых языках программирования разрешены произвольные взаимодействия между классами и интерфейсами — класс А может иметь поле типа "класс Б", и одновременно в классе Б может быть поле типа "класс А". То же самое с интерфейсами.


Интерфейс не может иметь полей.
Если подразумевается просто использование в сигнатуре методов, то не вижу ничего предосудительного.
Модератор-националист Kerk преследует оппонентов по политическим мотивам.
Re[6]: Зависимости между классами и интерфейсами
От: AlexRK  
Дата: 15.09.20 17:10
Оценка:
Здравствуйте, ·, Вы писали:

·>
·>class Node
·>{
·>  string name;
·>  int data;
·>  ...
·>  Node parent;//ссылка на себя
·>  NodeList children;
·>  NodeList ancestors; //вот даже так
·>}
·>class NodeList
·>{
·>  int size;
·>  Node get(int index);
·>...
·>}

·>class Document
·>{
·>  Node root;
·>  NodeList findNodesByName(...);// вот тут список нодов, но не нода, т.к. имени и прочего нет.
·>}
·>


А нельзя так?

class Node
{
  string name;
  int data;
  ...
  Node parent;
  List<Node> children;
  List<Node> ancestors;
}

class Document
{
  Node root;
  List<Node> findNodesByName(...);
}
Re[2]: Зависимости между классами и интерфейсами
От: AlexRK  
Дата: 15.09.20 17:19
Оценка:
Здравствуйте, Bill Baklushi, Вы писали:

ARK>>В мейнстримовых языках программирования разрешены произвольные взаимодействия между классами и интерфейсами — класс А может иметь поле типа "класс Б", и одновременно в классе Б может быть поле типа "класс А". То же самое с интерфейсами.


BB>Интерфейс не может иметь полей.


Во-первых, это неважно в контексте текущего вопроса. Во-вторых, есть языки, в которых интерфейсы могут иметь поля (Java, Eiffel).

BB>Если подразумевается просто использование в сигнатуре методов, то не вижу ничего предосудительного.


Мой вопрос не в том, предосудительно это или нет. Вопрос в том, насколько критичен отказ от циклов в объявлениях типов. Есть ли какие-то очевидные шоу-стопперы. Или есть полтора сценария, которые нужны в очень специфических случаях.
Re: Зависимости между классами и интерфейсами
От: vsb Казахстан  
Дата: 15.09.20 17:27
Оценка: 2 (1)
Ну мне циклы редко нужны. Если есть возможность обойти ограничение через что-то вроде void*, не думаю, что такой дизайн меня сильно бы ограничил. Правда непонятно из чего вытекает такое ограничение.

Вообще если у нас есть класс A и класс B с методами a() и b(), которые, для простоты, рекурсивно вызывают друг друга, т.е. код вида:

class A {
    B b;
    void a() {
        b.b();
    }
}

class B {
    A a;
    void b() {
        a.a();
    }
}


То это разруливается через промежуточный интерфейс и становится нерекурсивным:

interface IA {
    void a();
}

interface IB {
    void b();
}

class A implements IA {
    IB b;
    void a() {
        b.b();
    }
}

class B implements IB {
    IA a;
    void b();
        a.a();
    }
}

При этом этот случай достаточно общий, чтобы такого подхода хватило на разруливание рекурсивной структуры типов любой степени сложности, насколько я могу представить. Иногда это даже может приводить к более чистому дизайну.
Отредактировано 15.09.2020 17:33 vsb . Предыдущая версия . Еще …
Отредактировано 15.09.2020 17:33 vsb . Предыдущая версия .
Re: Зависимости между классами и интерфейсами
От: C0s Россия  
Дата: 15.09.20 20:13
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Вопрос у меня таков: насколько сильным вы считаете запрет на любые циклы в графе классов и интерфейсов?


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

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

В последний раз, что мне доводилось реализовывать подобную модель, я нашёл очень удобным подход с retriever'ом, предоставляющим find-методы по индексам сущностей разных типов, которые выдавали объекты с lazy-разрешаемыми переходами по графу физической или логической навигации (реализация на java, переходы через Memoize Supplier, гарантированно невидимые для потребляющей стороны через методы того же retriever'а). В частности, это не только позволило избежать конструкторов с лишь частичной проверкой инвариантов, но и не всегда дешёвой загрузкой связанных данных в ситуациях, когда это не было просто не нужно. Более того, это позволило избежать императивной нагрузки на реализациях разных бизнес-операций, где было достаточно вызвать один find-метод и далее использовать навигацию по модели без возврата к другим finder'ам. На всякий случай добавлю, что речь была совсем не про ORM/реляционную БД и модель с 200+ сущностей.
Re[7]: Зависимости между классами и интерфейсами
От: fmiracle  
Дата: 15.09.20 20:21
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>·> NodeList children;

ARK>А нельзя так?
ARK> List<Node> children;

Можно-то можно, но это неэквивалентно. NodeList это может быть с одной стороны расширенный относительно List<Node> класс с дополнительными свойствами и методами, а с другой стороны — инкапсулировать способ хранения нод, он может быть совсем не List<T>, или сейчас List, а потом захочется поменять на другой способ, и не хотелось бы отлавливать все вхождения по всему приложению (его применений может быть куда больше чем только в классе Node).
Re[8]: Зависимости между классами и интерфейсами
От: AlexRK  
Дата: 15.09.20 20:28
Оценка:
Здравствуйте, fmiracle, Вы писали:

ARK>>·> NodeList children;

ARK>>А нельзя так?
ARK>> List<Node> children;

F>Можно-то можно, но это неэквивалентно. NodeList это может быть с одной стороны расширенный относительно List<Node> класс с дополнительными свойствами и методами, а с другой стороны — инкапсулировать способ хранения нод, он может быть совсем не List<T>, или сейчас List, а потом захочется поменять на другой способ, и не хотелось бы отлавливать все вхождения по всему приложению (его применений может быть куда больше чем только в классе Node).


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

Впрочем, в данном случае вроде бы можно передать тип контейнера через генерик-параметр:
Не, похоже в жабе нельзя. Нужны higher-kinded types.
Отредактировано 15.09.2020 20:44 AlexRK . Предыдущая версия . Еще …
Отредактировано 15.09.2020 20:41 AlexRK . Предыдущая версия .
Отредактировано 15.09.2020 20:28 AlexRK . Предыдущая версия .
Re[9]: Зависимости между классами и интерфейсами
От: fmiracle  
Дата: 16.09.20 06:35
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Ну, так можно далеко зайти. Вдруг потом захочется и name чтобы был не string, и size чтоб не int.


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

ARK>Абстрагироваться от всего на всякий случай — это порочная практика, я считаю.


От всего на всякий случай — да. Но это не повод вообще убирать возможность подобного абстрагирования из языка.
Re[7]: Зависимости между классами и интерфейсами
От: · Великобритания  
Дата: 16.09.20 09:59
Оценка:
Здравствуйте, AlexRK, Вы писали:


ARK>А нельзя так?

ARK> List<Node> children;
Во-первых, шаблон List<Node> это таки отдельный тип, аналогичный NodeList с т.з. сабжа.
Во-вторых, впрочем иногда таки нельзя, если понадобится расширить List специфичными для NodeList методами.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Зависимости между классами и интерфейсами
От: kaa.python Ниоткуда РСДН профессионально мёртв и завален ватой.
Дата: 16.09.20 10:52
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Вопрос у меня таков: насколько сильным вы считаете запрет на любые циклы в графе классов и интерфейсов?


Мне больше всего нравится подход Go, когда есть запрет на циклические зависимости между модулями, что крайне хорошо сказывается на дизайне, и всё. Тот же запрет в Rust не от хорошей жизни, а от модели памяти.
Re[10]: Зависимости между классами и интерфейсами
От: AlexRK  
Дата: 16.09.20 18:33
Оценка:
Здравствуйте, fmiracle, Вы писали:

ARK>>Ну, так можно далеко зайти. Вдруг потом захочется и name чтобы был не string, и size чтоб не int.


F>И в некоторых случаях это вполне себе может оказаться оправдано.

F>А абстрагирование коллекции — еще чаще.

Не уверен за жабу, но абстрагироваться от коллекции можно чем-то типа алиаса: "NodeList = List;"

Главное, чтобы эта коллекция не была завязана на определение Node. Я хочу сказать, что не вижу в данном примере необходимости того, чтобы коллекция знала про тип своих элементов.
Re[2]: Зависимости между классами и интерфейсами
От: AlexRK  
Дата: 16.09.20 18:33
Оценка:
Здравствуйте, kaa.python, Вы писали:

ARK>>Вопрос у меня таков: насколько сильным вы считаете запрет на любые циклы в графе классов и интерфейсов?


KP>Мне больше всего нравится подход Go, когда есть запрет на циклические зависимости между модулями, что крайне хорошо сказывается на дизайне, и всё.


Да, согласен, это хороший вариант.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.