Pull to refresh
1
0
Сергей Асеев @Serg046

.net

Send message

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

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


Do not elide by default. Use the async and await for natural, easy-to-read code.
Do consider eliding when the method is just a passthrough or overload.

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

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

Есть класс, у которого метод Initialize, который всегда надо вызывать сразу после создания класса — применяешь паттерн «состояние»

Суть паттерна ясна, а можно подробнее именно про этот рефакторинг? Можно ссылкой.
То, что тело метода инкапсулируется внутрь "состояния" — понятно. Пытаюсь понять, кто и когда дернет этот Initialize у "состояния". А если оно почему-то переедет в конструктор "состояния", то тоже самое можно бы было сделать и с изначальным объектом (но не нужно).

Не уверен, что правильно понял идею комментария, но такой цикл — это довольно часто плохая идея. Сам цикл выполняется синхронно, каждая итерация будет ждать предыдущую. Не хочется придумывать пример, чтобы описать свои мысли лучше. Вот нагуглился http://gigi.nullneuron.net/gigilabs/avoid-await-in-foreach/


Правда c# 8 обещает убрать эту проблему.

Да, я про дженерики и вспомнил, reified к сожалению применим только к инлайн ф-циям, ну оно и понятно почему. Ну а насколько Kotlin временно обошел C# не буду спорить. Если бы не грядущий c# 8 с nullability, то сказал бы намного. Классная фича, которая должна была быть с рождения, как в Kotlin.

Я думаю похожие эмоции были бы при переходе с Java на C#. Kotlin, пока что, немного приятнее, но имхо совсем немного. Но зато у C# нет проблем, которые идут только из-за jvm и не могут быть нормально решены без изменений именно там. Так что проба F# кажется более внушительным шагом. В jvm стеке, полагаю, стоит называть Scala, а не Kotlin.

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

Что значит не дает контроля за выполнением? Проверка типов есть, интерфейсы есть. Испортил реализацию — приложение не соберется.

Соблюдение SRP не проверить даже статическим анализом.
Соблюдение OCP в теории частично можно автоматизировать, но не встречал.
Соблюдение LSP проверяемо.
Соблюдение ISP только частично. В теории конечно есть контроль на уровне статического анализа. Но анализатор не в состоянии подсказать, что ты используешь абстракцию слишком широко, что есть возможность использовать другой метод и уточнить абстракцию.
DIP — окей.
Итого только две состовляющие, но все равно с некой натяжкой. Какого-то 100% контроля не видно. К тому же из коробки этого нет. Нужно цеплять статический анализ.


Почему быстро бросится в глаза, а в SOLID значит не быстро? Это разве не от строгости языка зависит?

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


Мое понимание статьи примерно такое.

Насколько я понимаю, автор не совсем это хотел сказать.Вроде бы он сразу обозначил, что говорит про некие границы (порт/адаптер) и что этому способствуют и SOLID и чистота ФП.


По-моему он намекал не на проблемы SOLID в-прицнипе, а на проблемы его поддержки. Среда разработки не дает контроля за выполением этого самого SOLID. А вот в случае с чистыми функциями это контролируется. Чтобы что-то испортить тебе нужно явно это указать. А это очень быстро бросится в глаза на код-ревью.


Ну примерно как кто-то где-то изменил тип возвращаемого значения с int на object. Понижение уровня контроля сразу же бросается в глаза и ты спешишь проверить, а действительно ли оно требовалось… Действительно ли в данном конкретном месте и т.д.

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

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


T SomeMethod(Func<SomeType, T> callback)
{
    ...
    return callback(_someObjOfSomeType);
}

void Main()
{
    var anonObj = SomeMethod(someType => new {X = someType.SomeProp});
    Console.WriteLine(anonObj.X);
}

В приведенном примере, в случае отсутствия элемента в кол-ции, FirstOrDefault() все равно не спасает.
Тогда уж его хотя бы нужно переписать:


collection.FirstOrDefault()?.DoSomething();

Так что вариант 1 просто вносит дополнительную ненужную проверку и портит колстек в случае ошибки.

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


IA i = new C();

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


P.S. Немного погуглил и похоже так и планируется сделать.
Вот полный пример


interface IA
{
    void M() { WriteLine("IA.M"); }
}

class C : IA { } // OK

IA i = new C();
i.M(); // prints "IA.M"
new C().M(); // error: class 'C' does not contain a member 'M'

https://github.com/dotnet/csharplang/blob/1918cb06e1c8b79e024c688d1c4159046479276d/proposals/default-interface-methods.md


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

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

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

К счастью, это может поменяться — Champion "Method Contracts"

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

Information

Rating
Does not participate
Location
Ростов-на-Дону, Ростовская обл., Россия
Date of birth
Registered
Activity