Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Поэтому всё-таки выделить конкретно тяжелое на оптимизацию (вплоть до переписывания всего куска) эффективнее, чем абсолютно все писать с расчетом на дикие нагрузки.
И вот тут начинается Пичаль, тоже с большой буквы. Из песни слова не выкинешь, слой абстракции из Фреймворка не уберешь — где-то в другом проекте на него кто-то завязался… LinkedList на ArrayList не заменишь — кто-то ведь и к его поведению мог привязаться, применений-то у Фреймворка не счесть…
Простите, но либо у вас уровни абстракции либо возможность для потребителя завязаться на конкретную реализацию коллекции. Вы уж определитесь. А то похоже, что Фреймворк хреново спроектирован с точки зрения ООП.
как люди любят тратить время на совещаниях на всякую ерунду, вместо того, чтобы обсуждать насущные проблемы. Конкретно, проектировщики атомной электростанции очень долго спорили, какой материал должен пойти на навес для велосипедов – bike-shed
Зен языка Python
Добавление классов уменьшает сложность.
Например, что если понадобиться логировать не только в файл, но и в память, в стандартный вывод, по сети на сервер логов, в syslog
Например, что если понадобиться ...
А если не понадобится?
Тем более, что выше автор говорит "… когда пришлось менять поведение".
ибо в каждом отдельно взятом модуле делается что-то одно и спорить там не о чем.
Или когда создается отдельный класс для очень узкой, специализированной задачи, которая вызывается ровно один раз ровно в одном месте. Реализуется простой процедурой, класс не нужен вообще, но он создается. Со всеми прелестями конструирования его и деинициализации.
вызывается ровно один раз ровно в одном месте
Как раз специализированная задача и должна быть вынесена в отдельный класс. С единственной целью избавить класс-потребитель от знания деталей этой специализации.
Уже много лет дубликация не является единственным, и даже основным, поводом выделения юнита.
Если хотите, могу подробно расписать, почему это так, без упоминания абрревиатур и ссылок на Гугл.
In plain dependency injection, we create components and we assemble these components together to form an application. Using the Cake Pattern, we create pieces of functionality and we assemble the functionality to form the application.
class CakeTestSpecification extends Specification with Mockito {
trait MockEntitManager {
val em = mock[EntityManager]
def expect(f: (EntityManager) => Any) {
f(em)
}
}
"findAll should use the EntityManager's typed queries" in {
val query = mock[TypedQuery[User]]
val users: java.util.List[User] = new ArrayList[User]()
val userService = new DefaultUserServiceComponent
with UserRepositoryJPAComponent
with MockEntitManager
userService.expect { em =>
em.createQuery("from User", classOf[User]) returns query
query.getResultList returns users
}
userService.userService.findAll must_== users
}
}
val userService = new DefaultUserServiceComponent
with UserRepositoryJPAComponent
with MockEntitManager
Чтобы изменить конкретную используемую реализацию, нужно изменить объявление класса.
то я не могу быть уверенным, что тот, который в продуктиве — протестирован
Откуда я знаю, что еще подменила условная компиляция?
Это только вариант. Система тестирования может подменять весь модуль, например, просто редирект на другую директорию, где хранится модуль с тестовыми заглушками.
где модель-под-тестированием бинарно идентичен тому
public class ClassUnderTest
{
private readonly ILogger _logger;
public ModuleUnderTest(ILogger logger)
{
_logger = logger;
}
public void MethodUnderTest()
{
_logger.Info("abc");
...
_logger.Error("def");
}
}
Тестируемый блок зависит от интерфейса ILogger, но что подставляется — логгер или заглушка — определяется на этапе линковки, верно?
В таком случае имеет смысл говорить об идентичном поведении, а не бинарно идентичных модулях
Потому что все равно есть код, который решает в зависимости от каких-то условий подставить заглушку или логгер.
Отнюдь. В моем случае модуль в тесте и в продуктиве бинарно идентичен, это уменьшает степень неуверенности.
Нет такого кода. Просто нет.
Composition root, вызываемый в продуктиве, всегда подставляет продуктивную реализацию логгера… В тестировании composition root находится прямо внутри теста
Вы говорите про тестируемый модуль, или про всю программу? Тестируемый модуль и в процедурной парадигме бинарно идентичен.
Чем это определяется, если, как вы утверждаете, не линкером и не динамически(рантайм)?
В чем тогда разница по сравнению с ситуацией, когда связь А-> прописана в виде прототипа, объектный файл модуля А бинарно идентичен в тесте и в релизе, но при сборке мы используем объектный модуль либо Б, либо В, причем это задано make файлом?
Это (условно) независимый бинарный файл.
и при этом заменять реализацию этого компонента при сборке, сохраняя компонент-пользователь бинарно неизменным, то ничем.
у вас хоть вся система может быть в одном модуле, на тестирование это не повлияет
Класс — чтобы объявить его реализующим интерфейс, и в потребитель передать именно этот интерфейс.
P.S. Чистая функция, пишущая в файл (в вашем исходном примере, с которого все началось)? Вы издеваетесь? :)
Прямая зависомость кода от юзер-функции в другом модуле — это тоже неявный контракт.
Хм. Потому что я не хочу перегружать
Я хочу прямо в конструкторе видеть все зависимости класса-потребителя
я хочу нормальное переиспользование кода за счет SRP
Слушайте, ну поищите уже в интернете, зачем нужны интерфейсы… Пожалуста, не обижайтесь за такой совет, но это азы и об этом написаны просто кучи материалов и книг.
P.S. Было бы интересно узнать, для чего по Вашему мнению нужны классы/объекты (или ООП).
в том числе неявным контрактом того куска кода, где они используются. А если у меня нет доступа к исходникам?
В нормальной горизонтальной команде эти все вещи просто не могут возникнуть, т.к. это всё требует жутких усилий для развития и поддержки.
Т.е. нет причин что бы «божественный объект» не разбился на несколько объектов, при очередном рефакторинге.
Согласен, но если в команде больше 2х человек, то то что кто-то думает о «сейчас», а кто-то о «потом» вполне нормально.
Вообще горизонтальная команда не может обойтись без рефакторинга, это по сути основной инструмент взаимодействия разработчиков.
Рефакторинг позволяет же привлекать объективные критерии, вроде соответствия SOLID.
А зачем из одни делать вторых?
Если зажимать разработчиков по каким-то критериям кроме способности выполнять свои задачи
Проблема в том что и речь, и код это всё субъективные вещи.
Рефакторинг позволяет же привлекать объективные критерии, вроде соответствия SOLID.
Сделай и рефакторинг и представь правильное.
Нормальная команда всегда оценит вычищенный и понятный класс.
Во-третьих, кэшируйте обращения к базе всегда.
В программировании есть две сложных вещи: инвалидация кэша и выбор, как правильно что-нибудь назвать.
5 Страх перед добавлением классов
Редкое лучше, чем густое
Тим Питерс, Зен языка Python
Слишком длинно, не читал
Большое число классов – не признак плохого дизайна
Все остальные пунктыЧто это
…
В чём сложность
9 анти-паттернов, о которых должен знать каждый программист