Pull to refresh
235
0
Сергей Тепляков @SergeyT

Пользователь

Send message
Напомню, что данную «теорию» предложил Мейер, у которого с теорией все в порядке. К сожалению, не все мы можем сделать на этапе проектирования. Именно поэтому так рулит итеративная разработка.
Вставить обобщение между проектированием и разработкой сложно по нескольким причинам. Во-первых, эти этапы слишком сильно связаны. Хоть сейчас даже agile адепты признают важность архитектуры, но слишком уж формального этапа проектирования никто все равно не делает. Обычно на этом этапе рисуются диаграммы классов, расписываются типовые юз-кейсы, указываются контракты.

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

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

Кроме того, бывает что решение влияет на исходную задачу. Это тоже бывает, ведь в процессе решения заказчик и команда могут лучше понять, что же на самом деле нужно. Тогда попытка обобщить до этого момента просто провалится по определению.
Если тестируется абстракция (т.е. то, что должен делать класс, его контракт), а не то имплементация (то, как он реализован), то тесты на пустом месте не ломаются. Они в таком случае ломаются только тогда, когда нарушена логика, которую они призваны защищать.

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

З.Ы. Если у вас в проектах не удавалось добиться адекватного покрытия (я, кстати не говорю за 100%), то это не значит, что это невозможно, это говорит лишь о том, что вы не умеете это дело готовить.
У нас, видимо, очень разные взгляды на то, что означает термин «архитектура» и «фреймворк».

Во-первых, рекомендую полистать Framework Design Guidelines, чтобы появилось ощущение того, что принципы, лежащие в основе фреймворков отличаются от стандартных принципов проектирования. Поскольку основная цель фреймворков — это их повторное использование, то там подходят очень по другому к публичному (точнее, в этом случае к «публикуемому» интерфейсу), юзабилити, производительности и т.д. Зачастую эти принципы настолько важны, что разработчики фреймворков могут жертвовать такими общепринятыми принципами, как SRP, только в угоду юзабилити или производительности.

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

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

>>… ключевым элементом успеха в enterpirse, является глубокие знания в прикладной области…

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

Как всегда рулит прагматизм; понимание задачи очень важно, не спорю, но без нормального отношения к дизайну и коду тоже успеха не добиться.
Это, все же, не совсем одно и тоже. Я бы сказал, что трясина — это частный случай обобщения в данной конкретной области (в области языков программирования). Само же преждевременное обобщение (сори, сей академичный термин придумал не я) является более общим понятием, и, в то же время, более близким к программированию, а точнее, к проектированию ПО.

З.Ы. Скучное понятие паттерна нельзя заменить другим более теплым понятием «идиома», даже если в каком-то контексте эти понятия могут пересекаться. У каждого понятия свой смысл и свое место, так что не вижу причин, почему бы не использовать каждое понятие по назначению.
Конечно, если бы я был полностью согласен с камрадом Страуструпом, то первого раздела в этой подборке вообще бы не было.

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

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

Сам факт того, что вы не можете что-то потестить в большинстве своем говорит о проблемах (а не о сложностях) архитектуры. Есть типы задач, которые протестировать сложнее, но они точно не находятся в back end-е, который по своей природе может быть разбит на отдельные тестируемые куски.
На самом деле, у Рихтера об этом все написано, включая магию генерации классов замыканий при захвате внешних переменных из анонимного метода. Просто страшных терминов, типа closure у него нет.

См. главу 17. Delegates раздел Syntactical Shortcut #3: No Need to Wrap Local Variables in a Class Manually to Pass Them to a Callback Method
Это фича именно компилятора языка C# 5.0, причем не важно, на какую платформу мы таргетимся, т.е. при запуске этого кода из VS11 будет результат «1 2 3», даже если мы таргетимся на .NET Framework 3.5.

Поэтому ситуация, когда из VS10 будет один результат, а из VS11 — другой, вполне возможна. В данном случае решением является использования «общего» подхода, которое подойдет для всех версий — т.е. явное использование темповой переменной внутри цикла.
Нет, нигде. Так, на всяк случай напомнил.

Мой (точнее разработчиков C#) rationale во втором абзаце.
А цикл for никак не изменился. В нем как была одна переменная на весь цикл, так и осталась.

Здесь был вопрос компромисса: что лучше, пожертвовать согласованностью с циклом for или устранить одно из самых назойливых непониманий языка C#. При создании C# 2.0 решили отдать предпочтение согласованности с циклом for, но со временем решили, что этой согласованностью стоит пожертвовать.
Поведение будет таким же «согласованным» как и ранее, но для этого нужно понимать две вещи: (1) экземпляр класса замыкания для каждой области видимости и (2) каждая итерация цикла foreach содержит новую переменную i.

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

Т.е. этот код можно рассматривать таким образом:

var actions = new List<Action>();
var jjClosure = new jjClosure();
jjClosure.jj = 0;

foreach(var i in Enumerable.Range(1, 3))
{
  // внутренний объект замыкания содержит ссылку на внешний объект замыкания
   var iClosure = new iClosure() {i = i, jjClosure = jjClosure};

   // () => Console.WriteLine(i, jj) теперь перехало в метод Action замыкания iClosure.
  // Тело iClosure.Action выглядит так:
  // Console.WriteLine(this.i, this.jjClosure.jj);
   actions.Add(iClosure.Action);
}



В результате чего мы и получим 1 3, 2 3, 3 3, поскольку всеми iClosure будет использоваться один и тот же объект jjClosure.
Это вытекает из того, какое количество переменных существует в цикле foreach: одна на весь foreach или на каждую итерацию создается новая переменная.
Впервые эту проблему поднял Эрик в конце 2009-го года и уже тогда обсуждались решения, как это можно пофиксить. Сейчас было принято решение, что нет никакого разумно корректного кода, который бы использовал эту фичу именно таким образом (т.е. чтобы намеренно замкнулся на переменную цикла, в надежде получить сотню одинаковых результатов).
Несколько сотен постов на stackoverflow говорят о том, что очень-но многие разработчики считают именно «1 2 3» естественным результатом.

Естественно, после того, как вы узнали как это дело *устроено*, то все вопросы отпадают. Но ведь помимо нас с вами, есть еще пара миллионов индусов (это я про мировозрение, а не рассовую принадлежность), которые с нами не согласятся, поскольку они не знают да и не хотят знать, как это дело внутри устроено.
Да, большинство изложений методологий разработки тяжело назвать формальным, но у Мейера очень формальный подход. Так что рекоменую хотя бы ознакомиться с этой книгой. Мейер показывает, что ООП методология может быть весьма формальной.
Я рекомендую полистать эту книгу; вот что что, а назвать формальное изложение Мейером ООП гуманитарным подходом язык никак не поворачивается.

Information

Rating
Does not participate
Location
Washington, США
Registered
Activity