All streams
Search
Write a publication
Pull to refresh
46
3.2
Василий Степченко @piton_nsk

C# Developer

Send message

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

Если это одна сборка (речь о дотнете), то ничего не мешает использовать класс из соседней папки)

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

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

Мне кажется ваш собеседник пытается объяснить следующее:

Это понятно, я к тому что если разбираться в деталях, то могут быть весьма разные ситуации. ЕМНИП в яве есть модификаторы доступа на уровне пакетов, в дотнете только на уровне сборки, в каком-нибудь php свои заморочки.

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

Является ли это проблемой? Я работал с проектами где разделение на слои было на уровне папок и неймспейсов, все было в одной сборке. Проблем от этого не было никаких. ИМХО если люди захотят сделать костыль они его сделают, не одним способом, так другим)

Только что проверил хелловорлд Win64 2,5 килобайта)

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

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

Зато кастетом или цепью велосипедной врезать просто потому что из другого района было как здрасте. 30 лет назад видеоигр по сути и не было, а уличная преступность была и еще какая. Это сказки из разряда басен про честных воров, которые воруют только у богатых)

Зачем путаться в этих относительных терминах, если смысл понятен без них?

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

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

А как тут поможет именно инверсия зависимостей? Интерфейс объявить можно и без всякой инверсии. Об этом, кстати, у Буча написано, на которого ссылается Мартин.

There is a clear separation of concerns between the interface and implementation of each layer, making it possible to change the implementation of a layer without violating the assumptions made by its clients

Видите? Разделение на интерфейс и реализацию есть, возможность изменения реализации тоже есть. Инверсии нет.

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

Верхний он владет этой абстракцией, он задаёт интерфейс и он предлагает изменения.

Как-то непонятно. В итоге интерфейс в "верхнем" модуле это хорошо или это плохо?

Теперь мы инвертировали зависимость, сделали её более мягкой. 

Где же тут инверсия? Как "runner.py - верхнеуровневый модуль" использовал Manager, так и использует, только теперь зависимость внедряется через конструктор. Еще добавлен интерфейс и его реализация. Это конечно лучше (как правило), но инверсии зависимостей нет. Я вовсе не утверждаю что она тут нужна)

Для инверсии базовый класс Manager (ну или интерфейс) должен быть определен в "runner.py - верхнеуровневый модуль", а уже "manager.py - какой-то модуль нижнего уровня" должен его имплементить.

Вот что пишет Гуру Солида

Each of the upper-level layers declares an abstract interface for the services that it needs. The lower-level layers are then realized from these abstract interfaces.

Чем выше масса и меньше расстояние, тем выше сила притяжения, поэтому я позволил себе обобщить, что земля притягивает яблоко сильнее, чем яблоко землю

Сила одинаковая.

Он использует только интерфейс, а знает то обо всем модуле.

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

Ну вот в том и дело, что "дефолтная" трехслойная архитектура изолирует нижний слой от верхнего

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

Вот из Фаулера (выделение мое)

A simple script to pull data from a database and display it in a Web page may all be one procedure. I would still endeavor to separate the three layers, but in that case I might do it only by placing the behavior of each layer in separate subroutines. As the system gets more complex, I would break the three layers into separate classes. As complexity increased I would divide the classes into separate packages.

А вот из того же Фаулера (выделение мое), про зависимости

Together with the separation, there’s also a steady rule about dependencies: The domain and data source should never be dependent on the presentation. That is, there should be no subroutine call from the domain or data source code into the presentation code. This rule makes it easier to substitute different presentations on the same foundation and makes it easier to modify the presentation without serious ramifications deeper down. The relationship between the domain and the data source is more complex and depends upon the architectural patterns used for the data source.

Вызывать код нельзя, но про реализацию интерфейса ничего не сказано)

Но зато сказано вот что

In this scheme the higher layer uses various services defined by the lower layer, but the lower layer is unaware of the higher layer

Понять можно и так и сяк. Со слоем данных там еще интереснее. Да и вообще говоря "слои" в "трехслойном" приложении Файлера это не совсем "модули" Мартина. У Мартина это

all well-structured object-oriented architectures have clearly defined layers, with each layer providing some coherent set of services though a well-defined and controlled interface

А еще же есть cross-cutting concept, пятислойная архитектура (не одна) и прочие гексагоны.

просто заинжектите через интерфейс реализацию и радуйтесь

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

Здесь все ясно: яблоко упало, потому что Земля, как более массивный объект, притягивает его. 

Я правильно понимаю, что у вас более массивный предмет притягивает менее массивный?

И к какой из десятка имплементаций оно улетит?

Выводит список имплементаций интерфейса и выбираете что смотреть. Другое дело что если криво сделана фабрика/DI бывает сложно понять как конкретная имплементация выбирается в рантайме.

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

Если следовать нарисованной схеме, то не можем, нижний модуль знает о верхнем только интерфейс, который ему надо реализовать.

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

Наша реализация теперь не только от абстракций зависит, но и от остальной кодовой базы.

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

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

Согласен, но такого я не встречал к сожалению.

SOLID по сути требовал только добавить сколько-нибудь абстракные интерфейсы

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

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

Или любой другой, в которой бизнесовой логики быть не должно.

Правильное разделение по слоям тоже важно, но это не относится к инверсии зависимостей.

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

Как изменения в верхнем модуле могут по всему проекту расползтись?

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

И если вы пишете проект в одно лицо

В этом случае можно очень много углов срезать)

Почему никого ге смущает, что модуль нижнего уровня зависит от модуля верхнего уровня? Зачем ему это?

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

Что хорошо иметь интерфейсы и возможность подменять классы, хотя-бы для тестирования?

Это действительно неплохо, но причем тут именно SOLID?

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

Золотые слова! Без этого и получается что используют инструмент для решения несуществующих в данном месте проблем.

Information

Rating
1,107-th
Registered
Activity

Specialization

Software Developer, Backend Developer
Senior
C#
ASP.Net
SQL
.NET Core