С т.з. теории и практики методов программирования нормально ли обращение объекта
к собственным публичным методам и часто ли вы сталкиваетесь с таким/используете сами?
По-моему, это нарушает какой-то принцип. Может я ошибаюсь.
С другой стороны что делать если допустим апи-клиент имеет два метода:
public IEnumerable<int> GetIds();
public Detail GetInfo(int id);
напрашивается обобщение
public IEnumerable<Detail> GetInfos() => GetIds().Select(GetInfo);
Наткнулся на эти грабли когда делал прокси-объект.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re: Обращение объекта к собственным публичным методам
AA>Наткнулся на эти грабли когда делал прокси-объект.
И какую проблему это вызвало?
У меня возник данный вопрос в ракурсе использования сервисов через контракты, когда есть некий interface и 2 реализации: одна тестовая а другая реальная. IoC применен для выбора реализации сервиса (тестовая или реальная).
И когда хотите протестировать некий сервис (его реальную реализацию), подсовываете зависимые сервисы в их тестовой реализации, чтобы не тестировать все сразу, чтобы только один метод сервиса тестировать. И заноза в том, что методы других сервисов то вызываются через контракты (и будет вызвана тестовая реализация), а вот методы того же сервиса — дергаются напрямую
=сначала спроси у GPT=
Re[2]: Обращение объекта к собственным публичным методам
Здравствуйте, Shmj, Вы писали:
AA>>Наткнулся на эти грабли когда делал прокси-объект.
S>И какую проблему это вызвало?
Нужно допустим добавить логирование долготы вызова. с точки зрения логики все ок.
но если вызвать сложный методе через прокси то аспект применится только одиножды, а требуется фиксация всех вызовов и тут прокси спотыкается.
Но ведь нигде большими черными буквами не пишут: ТАК НЕ ДЕЛАЙ! Хотя я вижу тут очевидные грабли.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[3]: Обращение объекта к собственным публичным методам
Здравствуйте, varenikAA, Вы писали:
AA>но если вызвать сложный методе через прокси то аспект применится только одиножды, а требуется фиксация всех вызовов и тут прокси спотыкается. AA>Но ведь нигде большими черными буквами не пишут: ТАК НЕ ДЕЛАЙ! Хотя я вижу тут очевидные грабли.
И какое решение вы видите?
=сначала спроси у GPT=
Re: Обращение объекта к собственным публичным методам
AA>нормально ли обращение объекта AA>к собственным публичным методам AA>По-моему, это нарушает какой-то принцип
Ага. То есть если я готовлю еду моим детям, это нормально, а если готовлю себе — нет. Логично.
AA>По-моему, это нарушает какой-то принцип. Может я ошибаюсь.
Нет, не ошибаешься. Ошибиться можно только подумав. Ты же не думаешь, а просто несешь блажь.
"Больше 100кмч можно ехать на автобане в любом ряду кроме правого крайнего" (c) pik
"В германии земля в частной собственности" (c) pik
"Закрывать школы, при нулевой смертности среди детей и подростков, это верх глупости" (c) Abalak
Re[4]: Обращение объекта к собственным публичным методам
Здравствуйте, varenikAA, Вы писали:
AA>С т.з. теории и практики методов программирования нормально ли обращение объекта AA>к собственным публичным методам и часто ли вы сталкиваетесь с таким/используете сами?
А можно ли объекту обращаться к публичным методам только тех объектов, которые не обращаются к собственным публичным методам?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[3]: Обращение объекта к собственным публичным методам
Здравствуйте, varenikAA, Вы писали:
AA>Нужно допустим добавить логирование долготы вызова. с точки зрения логики все ок.
С точки зрения логики здесь возникает вопрос — долгота вызова на уровне контракта (набор API достуный внешним клиентам) или на уровне реализации (связи, которые не видны через API).
AA>но если вызвать сложный методе через прокси то аспект применится только одиножды,
Правильно. Это декоратор для внешнего контракта.
AA>а требуется фиксация всех вызовов и тут прокси спотыкается.
Правильно. Это полагается на особенности внутренней реализации. Допустим, вы потом захотите приватные методы тестировать, что делать? И нет, пример не надуманный. Потому что вызов "своего публичного" метода можно переделать в вызов "своего приватного метода" без особых проблем:
AA>Но ведь нигде большими черными буквами не пишут: ТАК НЕ ДЕЛАЙ! Хотя я вижу тут очевидные грабли.
Вроде когда разговаривают про инкапсуляцию подобные вопросы затрагивают. Напирмер, разницу между контрактом и реализацией. И то, что наследование — очень сильная связность и под нее нужно проектирвать специально (это если ваш аспект вдруг через наследование начнет работать). А еще грабли в аспектах — они очень хрупкие и нормально в абстракции не вписываются. Вот если бы вы ручной декоратор написали, возник бы у вас подобный вопрос?
Re[4]: Обращение объекта к собственным публичным методам
Здравствуйте, maxkar, Вы писали:
M>Вроде когда разговаривают про инкапсуляцию подобные вопросы затрагивают. Напирмер, разницу между контрактом и реализацией. И то, что наследование — очень сильная связность и под нее нужно проектирвать специально (это если ваш аспект вдруг через наследование начнет работать). А еще грабли в аспектах — они очень хрупкие и нормально в абстракции не вписываются. Вот если бы вы ручной декоратор написали, возник бы у вас подобный вопрос?
50 на 50, ведь пришлось бы каждый вызов явно реализовать. но не уверено, что я бы заглянул в сложный метод.
Только знание что-то тут может быть косяк заставляет повнимательней присмотреться.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[6]: Обращение объекта к собственным публичным методам
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, varenikAA, Вы писали:
AA>>В рамках например C# только избегать подобных упрощений.
S>А как в вашем случае избежать упрощений? Создать новый класс, в котором ваш апи-клиент будет представлен полем и для каждого метода будет обертка?
либо да — матрешки оберток, либо клиентском коде делать последовательный вызов простых методов апи.
вероятно только макросы могут решить эту проблему наиболее просто.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[2]: Обращение объекта к собственным публичным методам
Здравствуйте, Shmj, Вы писали:
S>И заноза в том, что методы других сервисов то вызываются через контракты (и будет вызвана тестовая реализация), а вот методы того же сервиса — дергаются напрямую
Так это как раз правильно! Вы что, компилятору не доверяете? Т.е. "я написал вызов метода, но не уверен, что компилятор вызывает этот метод". Внутренние особенности вызовов могут произвольно меняться, тесты при этом падать не должны. И не только внутренние особенности. Например, класс может начать вызывать другие (эквивалентные) методы из своих зависимостей. Например, get(predicate) вместо getAll(). И тесты при этом не должны падать.
По описанию подобная проблема обычно возникает при неправильных абстракциях. Ваш "сервис" имеет низкую связность (делает несколько независимых вещей). Или по каким-то причинам у вас "несколько" уровней функциональности. Т.е. нужно разделить сервис как минимум на два. А "контракты" в таком коде обычно создаются механически и не представляют хороших/полезных абстракций.
Re: Обращение объекта к собственным публичным методам
Здравствуйте, varenikAA, Вы писали:
AA>С т.з. теории и практики методов программирования нормально ли обращение объекта к собственным публичным методам
Да.
AA> и часто ли вы сталкиваетесь с таким/используете сами?
Часто.
AA>По-моему, это нарушает какой-то принцип.
Какой?
AA> Может я ошибаюсь.
Ошибаешься.
AA>С другой стороны что делать если допустим апи-клиент имеет два метода: AA>
Здравствуйте, varenikAA, Вы писали:
AA>50 на 50, ведь пришлось бы каждый вызов явно реализовать. но не уверено, что я бы заглянул в сложный метод.
Вы на правильном пути! Я вижу здесь половину отличного вопроса. Но для начала — а зачем вам глубоко внутрь метода смотреть? Вам нужно начало и/или конец метода. Если очень нужно, можно в try/finally обернуть (чтобы в finally писать время выхода). Или там более высокоуровневую абстракцию сделать (Scala, pass-by-name в сенсоре вычисляется при использовании).
trait BlockSensor[T] {
def apply(block: => T): T
}
// somewhere else
// block sensor is injected
val myMethodSensor: BlockSensor[Int] = ...
def myMethod(): Int =
myMethodSensor {
??? // calculate int
}
И это все — чисто механически делается. А если вдруг вам нужно "смотреть" в методы, вы переходите на следующий уровень. Это анализ того, зачем производится логирование/мониторинг. Потому что вы пытаетесь найти границы "интересной" функциональности, которую стоит мониторить. Эти границы коррелируют с методами. Но это не строгое соответствие. Может быть совсем тривиальные методы (например, URL форматируют для вызова внешнего сервиса). А иногда интересная логика может быть в одном методе (например, в зависимости от параметров мы можем базу данных по-разному спрашивать). Подобные границы более стабильны при рефакторинге, часто можно выделять/удалять методы без нарушения контракта мониторинга (ага, все injection points создают контракт).
AA>Только знание что-то тут может быть косяк заставляет повнимательней присмотреться.
Для этого есть более мощное кунг-фу. Спрашивайте, "зачем я это делаю?", "что хочу добиться в результате?". Практически любое действие, основанное на структуре кода должно вызывать подозрение. Все хотелки должны основываться на логике программы или реализации. Учитесь отличать артефакты реализации от требований (и требований, вызванных конкретной реализацией). Они часто идут вместе, но при этом являются разными понятиями.
Re[2]: Обращение объекта к собственным публичным методам
AA>>По-моему, это нарушает какой-то принцип.
НС>Какой?
Знать бы... AA>>Наткнулся на эти грабли когда делал прокси-объект.
НС>И в чем грабли?
Уже привел пример с логированием длительности вызовов путем создания прокси-объекта.
Прокси ничего не знает о внутренних вызовах публичных методов, поэтому логирование выполняется только для комплексного метода.
Допустим нужно не только логирование, а проверка прав доступа. Во многих случаях важно чтобы прокси отработал на всех публичных методах.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[3]: Обращение объекта к собственным публичным методам
Здравствуйте, varenikAA, Вы писали:
НС>>Какой? AA>Знать бы...
AA>>>Наткнулся на эти грабли когда делал прокси-объект. НС>>И в чем грабли? AA>Уже привел пример с логированием длительности вызовов путем создания прокси-объекта.
Грабли в нем — это прокси объект для логгирования.
AA>Допустим нужно не только логирование, а проверка прав доступа. Во многих случаях важно чтобы прокси отработал на всех публичных методах.
Это делается при помощи стека мидлверов, а не созданием проксей.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[2]: Обращение объекта к собственным публичным методам
Здравствуйте, ·, Вы писали:
·>А можно ли объекту обращаться к публичным методам только тех объектов, которые не обращаются к собственным публичным методам?
Можно, но нужно использовать такой ЯП который будет гарантировать последнее.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re: Обращение объекта к собственным публичным методам
Здравствуйте, varenikAA, Вы писали:
AA>С т.з. теории и практики методов программирования нормально ли обращение объекта AA>к собственным публичным методам и часто ли вы сталкиваетесь с таким/используете сами? AA>По-моему, это нарушает какой-то принцип. Может я ошибаюсь. AA>С другой стороны что делать если допустим апи-клиент имеет два метода: AA>
AA>Наткнулся на эти грабли когда делал прокси-объект.
Тут есть вот какой момент: если ваш метод целиком реализуется в терминах публичных методов, то делать его членом класса противопоказано.
Этим вы увеличиваете обязанности для потенциальной альтернативной реализации. То есть кто-то хочет сделать свою реализацию вашего класса — он будет обязан реализовать ещё и GetInfos.
А реализация через extension метод позволит ему избежать этой обязанности.
С другой стороны, альтернативная реализация может предложить более эффективный способ выполнить GetInfos, без N+1 вызовов (а что, если этот код вызывается через какой-нибудь Remoting?).
В старые времена в таких случаях заводили абстрактный базовый класс, типа
public abstract IEnumerable<int> GetIds();
public abstract Detail GetInfo(int id);
public virtual IEnumerable<Detail> GetInfos() => GetIds().Select(GetInfo);
Тогда наследникам достаточно перекрыть только GetIds() и GetInfo(). А GetInfos — только если очень хочется.
Но этот способ ограничивает выбор иерархий наследования. Метод расширения в этом плане гораздо гибче:
public interface IDetailProvider
{
IEnumerable<int> GetIds();
Detail GetInfo(int id);
}
...
public static IEnumerable<Detail> GetInfos(this IDetailProvider provider) => provider.GetIds().Select(provider.GetInfo);
Но тут возникает как раз затруднение для тех, кто захочет реализовать более эффективное тело для GetInfos. Если в месте вызова мы знаем конкретный класс, который имеет свой собственный метод GetInfos(), то будет вызван он.
Однако, такое счастье бывает не всегда. А при вызове через интерфейс "своя" реализация будет проигнорирована, и вызван общий extension.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Это делается при помощи стека мидлверов
По-моему, только если это заложено в архитектуре приложения. Примеры есть?
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re: Обращение объекта к собственным публичным методам
Здравствуйте, varenikAA, Вы писали:
AA>С т.з. теории и практики методов программирования нормально ли обращение объекта AA>к собственным публичным методам и часто ли вы сталкиваетесь с таким/используете сами?
1. Это может быть нарушением SRP, а может и не быть
Класс с публичным интерфейсом можно рассматривать как композицию двух модулей — приватного (из приватных методов) и публичного (логика которого опирается на интерфейс приватного модуля).
Интерфейс модуля должен быть ортогонален, т.е. допускать решение каждой задачи единственным образом. Неортогональные интерфейсы крупнее и требуют больше усилий при понимании, поддержке и создании альтернативных реализаций.
Если мы вызываем один публичный метод из другого, то либо у нас неудачный приватный модуль, либо эта логика не должна принадлежать этому классу.
Когда нам нужно много "сахара" для удобного API, он делается в отдельном модуле, а реализация опирается на нижележащий модуль.
2. Есть такая техника для объектных иерархий
Публичные методы всегда final, реализация делегируется в protected методы в том же классе.
Позволяет зафиксировать контракт и при этом дает свободу реализации.