Расширение класса внешними методами и переменными

Adelf

Administrator
Команда форума
и про final ImageUploader, который ничего не имплементит я там писал. Что в своём проекте, когда нет никаких практик unit-тестирования, можно использовать финальные классы и прямо их просить в DI. А вот если пишете тесты, то там автоматом первыми вырастают интерфейсы.

Он зависит от неких Storage и ThumbCreator и использует только их публичные методы. Разработчику, который работает в данный момент с этим кодом, всё равно классы это или интерфейсы. Контейнер внедрения зависимостей, убрав необходимость создавать объекты зависимостей, подарил нам супер-абстракцию: классам совсем неважно знать от чего именно он зависит - от интерфейса или класса. В любой момент, при изменении условий, класс может быть сконвертирован в интерфейс, а весь его функционал перенесен в новый класс, реализующий этот интерфейс (Storage => S3Storage). Это изменение, наряду с конфигурацией контейнера зависимостей будут единственными на проекте. Весь остальной код, использовавший класс, будет работать как прежде, только теперь он зависит от интерфейса. Используя суффиксы *Interface или другие отличительные признаки интерфейсов, мы лишаем себя этой абстракции, а также загрязняем наш код.
 

whirlwind

TDD infected, paranoid
Спасибо за ссылку. След раз буду просто ссылки постить, что бы не повторяться. Просто опять возвращаемся к тому, что вы не вычищали эти сраные файналы из проекта и не решали вопрос тестирования использования сторонних либ, которые убили этими файналами полиморфизм.
 

whirlwind

TDD infected, paranoid
кто блин тестит сторонние либы? :) нормальный путь - это wrapper создавать. тестить... $this->equals(4, 2+2); ?
whirlwind написал(а):
PS. Что бы тема опять не вернулась к примерам первого уровня типа ImageUploader. Речь идет не о дизайне CUT, а дизайне объектов, с которыми взаимодействует CUT. Протестировать ImageUploader легко. Протестируй того, кто юзает ImageUploader, что бы понять, насколько дизайн хорош.
Тестировать пользователей, а не стороннюю либу. Пользователей абстракции, яволь?
 

Вурдалак

Продвинутый новичок
Вообще, если уж говорить о моках, мокать чужие контракты — так себе затея («don't mock what you don't own»). Тебе может только казаться, что ты понимаешь контракт какой он есть, делая свой мок, а в реальности он может вести себя иначе. Не мокают, например, всякие DataMapper, а пишут интеграционные тесты с реальным MySQL. Если ты будешь придерживаться этого правила, то тебя не будет как минимум раздражать то, что содержится в чужих библиотеках. Они не твои, поддерживать обратную совместимость при изменениях в них не тебе, поэтому ныть по поводу присутствия final как-то даже неэтично, это как требовать бесплатно поработать.
 

Вурдалак

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

Поэтому если есть возможность, лучше всегда тестировать вполне конкретную реализацию, если она предполагается одна и там final. Иногда это просто неудобно, но если такая возможность есть — почему бы и нет.

final class на то и final, то реализация вот такая, какая она есть, она однозначная.
 

whirlwind

TDD infected, paranoid
Тот кто написал, что mockist style это плохой подход, полный тупень (даже если это сам Фаулер). Мокнутый класс/метод ничем не отличается от ЛЮБОЙ реализации. Все что требуется от мока - это соблюдение контракта. Каким образом это сделано on-the-fly using reflection или stubbing by inheritance или способом наклейки антикурительного пластыря на пятку - пользователя абстракции не волнует. Это краеугольный камень ООП и абстрактного мышления в принципе.

Насчет тестов MySQL. Да, не спорю -в базах есть возможность in memory/temporary. Но есть и такое понятие - алгоритмическая сложность. Из него можно примерно прикинуть сложность теста при использовании реальных реализаций. Щас лень считать, но прикинь вариативность класса 1 уровня. Вариативность класса 2 уровня. Посчитай суммарную сложность. Прикинь, что xDBC где то на третьем уровне абстракции. Про сложные диалекты в тестировании я тоже где то уже писал на этом форуме. Честно покрыть все варианты с задействованным SQL не представляется возможным из за сложности диалекта. Тут вопрос - либо 100% покрытие с моками, либо квантовые компы для возможности реально все тесты прогнать. Существующие CPU пока не в состоянии справиться.

PS. А насчет проблем неявности контрактов, так строгая типизация. И строгая не вплане что int, а PositiveInt. Я про это тоже говорил мульен раз.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
ух ты, милейший срач, хоть в юмор переноси

Если у вас 100% всего кода - это ваш собственный код
Потому что это мой API
- ты рыбу ловишь?
- нет, я рыбу ловлю
- аа! я-то думал, что ты рыбу ловишь

"это моя корова и я ее буду доить" (С)
 

Вурдалак

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

«Мой API» в значении «моя ответственность».
Человек просит точку расширения, которая усложняет BC promises для своих клиентов.
Требовать взять на себя больше ответственности (особенно когда речь про open source, когда точка нужна для очень сомнительных кейсов) — это неэтично.
Поэтому я лично с удовольствием наблюдаю, как final для таких людей — это как средний палец.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
прикольно наблюдать за двумя якодзунами, продолжайте!
я согласен насчет final - и что он мешает, и что он обязателен в публичном API, и убрать его на лету для тестов тоже можно

помню, как в yii1 свойства были частью интерфейса, хреново было,

И как в базовом классе active record при выборке данные из базы писались в private свойство с итерацией по геттеру, так что при работе с набором в 1000 строк по скорости это невозможно было использовать, а переделать на protected или экспортировать, чтобы данные проходить foreach без геттера раз в 100 быстрее, автор-баран отказался - это же api менять, bc нарушать...
final и private - инструменты серьезные, если выделываться в стиле Тяна "учитесь писать с учетом моих ошибок", можно убить проект, которому посвятил 10 лет жизни.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
у меня есть один практический вопрос
когда я реализую интерфейс, в методе другого класса параметру, в который приходит объект, пишу тип - интерфейс, а в PHPStorm хочу по Cmd🤟-click перейти в класс с реализацией, как это сделать? Оно ж в файл интерфейса переходит, блин
править PHPDoc, чтобы не соответствовал api, и выключать inspection?

P.S. можно попробовать множественный тип параметра, уже появился же ...
 

fixxxer

К.О.
Партнер клуба
Ставишь курсор на интерфейс и navigate-implementations. Ну или переходишь в интерфейс и в нем тыкаешь то же самое. Если реализация одна, сразу в нее перейдет, если несколько, выведет список. Какой дефолтный хоткей не помню, у меня все перебито на свои
 

fixxxer

К.О.
Партнер клуба
В Java интерфейсы появились с 1.0 (это было до (очепятка) после первых плюсов), но если на них посмотреть повнимательнее сегодня, они обратно превращаются в классы
Я специально перечислял extends abstract и implements в одном ряду, не различая. Ок, допустим, выкинем интерфейсы, сделаем множественное наследование, что изменится-то?
 

whirlwind

TDD infected, paranoid
Я специально перечислял extends abstract и implements в одном ряду, не различая. Ок, допустим, выкинем интерфейсы, сделаем множественное наследование, что изменится-то?
Попрошу не выражаться! Множественное наследование это совсем не то же самое, что сохранение полиморфности. Не надо его сюда приплетать. Использование interfaces для решения проблемы единой точки доступа это нормально. Сам так делаю.
Код:
public class QFReactor implements EventListener, DataProvider, SPRunnable, L1UpdateConsumer {
Такое помогает для критической секции, когда queue+command из топика 5-летней давности по каким то причинам не работает. Но это все опять multithreading и мало общего имеет с common PHP.
 

WMix

герр M:)ller
Партнер клуба
в данном случае множественное наследование подразумевает не наследование конкретных реализаций а наследование набора виртуальных методов
те. по использовать множественное наследование как интерфейсы
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
что такое проблема единой точки доступа?
 

fixxxer

К.О.
Партнер клуба
Попрошу не выражаться! Множественное наследование это совсем не то же самое, что сохранение полиморфности.
Я про частный случай с pure abstract classes, конечно же (впрочем, можно и не совсем pure, будет типа default methods в java)
 
Сверху