Привет (давно не виделись, кстати). Мне пониманию сути описанного не хватает деталей (ну, и сообразительности, конечно):
1) почему для клиента этой конструкции важно, что IFoo и IBar реализованы одним объектом? Ситуацию можно развести, введя на стороне сервиса промежуточный интерфейс IBarFactory, и указав в внутренней документации подсистемы, что некоторые классы, имплементирующие IFoo, имплементируют также либо сам IBar, либо (в противном случае) IBarFactory. Далее понятно: производством IBar из IFoo по документации (для клиента) подсистемы занимается универсальная утилита (extension method IFoo.GetIBar, скажем), которая сперва пытается запросить IBar с самого объекта, а при неудаче запрашивает IBarFactory и (при успехе) вызывает единственный метод этого интерфейса (что она делает и при этой неудаче, оставим за рамками рассмотрения). Таким образом, ценой лёгкого неудобства для клиента (повышающего гибкость) "реализация по умолчанию" на стороне сервиса сводится к перекрытию единственного метода IBarFactory (делегацией к стандартной внутренней утилите CreateIBarFromIFoo, которя знает про FooBar) - что гораздо удобнее перекрытия ВСЕХ методов IBar (делегацией к FooBar). И язык модифицировать не надо (в нём и так уже слишком много наворотов, на мой вкус). Цена клиенту: необходимость прочитать в документации инструкцию "не пытайтесь запрашивать IBar с IFoo встроенными средствами языка. Вместо этого пользуйтесь утилитой IFoo.GetIBar."
2) неужели это так уж часто встречающаяся у тебя ситуация? Хотелось бы конкретных примеров.
я сейчас типа сильно устал, поэтому подробнее позже отвечу. пока, если интересно, посмотри, что делает "implements" в дельфи. мощная штука, аналогов которой нигде нет (вроде).
я абсолютно за пивка попить... приезжайте в субботу? или в воскресенье на худой конец :)
В субботу вечером у нас вроде пока ничего не было запланировано — можем и приехать! В какое время удобнее?
Про implements тоже посмотрел — насколько я понял, это языковая поддержка реализации доп. интерфейсов объекта через агрегацию (реализация отношения “is-a” через “has part”). У меня основная проблема с таким подходом — философического свойства. Я понимаю, что такие языковые навороты могут быть удобнее при определённом стиле ОО программирования — но я не уверен, что этот стиль является хорошей практикой. Конечно, я с этим стилем знаком (программировал в модели COM, где шаг не ступишь, не вызвав QI; изредка пользовался средствами RTTI C++а — причём чаще самопальными, чем встроенными в язык), как и, например, со стилем, порождающим богатые иерархии наследования — но в последние годы рефлексирую на тему того, что мои ООП-навыки, приобретённые 15-20 лет назад, устарели, и нынче *правильное* ООП выглядит значительно по-другому. Так вот, я не уверен, что неявное представление информации (клиенту) о том, как с одного интерфейса ("роли") данной сущности получить другой (другую роль, состоящую в каком-то один-к-одному отношении с первой ролью) — вещь сомнительная, потому что:
1. Информация представлена неявно, в лучшем случае, в документации ("а вот, типа, в копилку эрудита — имея на руках интерфейс IFoo его можно закастить к интерфейсу IBar, потому как у нас в реализации оба реализованы на одном классе — вы в public интерфейсе системы никакого упоминание этого не увидите, но это так"). Если клиент начинает этой информацией пользоваться, появляется неявная зависимость в коде (implicit dependency on a legacy client) — глядя на исходники внешнего интерфейса, программист новой версии сервиса может захотеть переделать реализацию так, что IFoo и IBar разъезжаются на разные объекты. Если он это сделает, возникнет неприятность, обнаруживаемая только during execution time (not a build error / warning) — нехорошо.
2. Предполагая (даже указывая в документации), что касты такого типа — OK, сервис открывает в некотором смысле backdoor для “creative” клиента — клиент начнёт кастить направо и налево, эксплуатируя неявные зависимости такого типа и в случаях, когда связь «IFoo239 и IBar45 на самом деле имплементированы на одном объекте» не указаны в документации и на самом деле являются деталью имплементации. Если клиент этим злоупотребит, у сервиса начнутся серьёзные проблемы при будущих рефакторингах.
3. Вообще, реализация нескольких интерфейсов на одном объекте, мне кажется, противоречит принципу separation of concerns, хотя и может оказать удобной и полезной (как в смысле быстроты разработки сервиса, так и в смысле скорости выполнения некоторых частых операций и размера потребляемой памяти). Если это всё-таки сделано, следует этот факт от клиента скрывать, мне кажется — прописав касты (правильные в данной версии сервиса) в явных утилитах вида GetIBarFromIFoo, и строго-настрого запретив пользоваться для этого встроенными в язык кастами (не уверен, что в языке есть средство запретить встроенный каст… На C++ в библиотеке, реализующий «самодельный» RTTI такое сделать в принципе можно — обязав код, вызывающий каст, предъявить некий «допуск», которым будет обладать только реализация GetIBarFromIFoo и никто другой). Дополнительный бонус от сокрытия каста в GetIBarFromIFoo — утилиты такого типа в принципе могут использоваться не только для отношений один-к-одному, но и для один-ко-многим (добавлением дополнительных параметров кроме IFoo*).
Н-да, но судя по сайту (http://www.shtopor.us/shtopor.htm) тот Штопор (2008) был как раз и последним пока... Ещё приятель напомнил, что в конце месяца выступает "Наутилус" (что бы это ни значило), но я их не люблю и не пойду.
(no subject)
Date: 2010-02-04 02:57 am (UTC)a piece of code
the Decorator pattern
the Delphi "implements" keyword
(no subject)
Date: 2010-02-04 03:42 am (UTC)(no subject)
Date: 2010-02-04 09:10 am (UTC)1) почему для клиента этой конструкции важно, что IFoo и IBar реализованы одним объектом? Ситуацию можно развести, введя на стороне сервиса промежуточный интерфейс IBarFactory, и указав в внутренней документации подсистемы, что некоторые классы, имплементирующие IFoo, имплементируют также либо сам IBar, либо (в противном случае) IBarFactory. Далее понятно: производством IBar из IFoo по документации (для клиента) подсистемы занимается универсальная утилита (extension method IFoo.GetIBar, скажем), которая сперва пытается запросить IBar с самого объекта, а при неудаче запрашивает IBarFactory и (при успехе) вызывает единственный метод этого интерфейса (что она делает и при этой неудаче, оставим за рамками рассмотрения). Таким образом, ценой лёгкого неудобства для клиента (повышающего гибкость) "реализация по умолчанию" на стороне сервиса сводится к перекрытию единственного метода IBarFactory (делегацией к стандартной внутренней утилите CreateIBarFromIFoo, которя знает про FooBar) - что гораздо удобнее перекрытия ВСЕХ методов IBar (делегацией к FooBar). И язык модифицировать не надо (в нём и так уже слишком много наворотов, на мой вкус). Цена клиенту: необходимость прочитать в документации инструкцию "не пытайтесь запрашивать IBar с IFoo встроенными средствами языка. Вместо этого пользуйтесь утилитой IFoo.GetIBar."
2) неужели это так уж часто встречающаяся у тебя ситуация? Хотелось бы конкретных примеров.
(no subject)
Date: 2010-02-05 04:06 am (UTC)я абсолютно за пивка попить... приезжайте в субботу? или в воскресенье на худой конец :)
(no subject)
Date: 2010-02-05 09:20 pm (UTC)Про implements тоже посмотрел — насколько я понял, это языковая поддержка реализации доп. интерфейсов объекта через агрегацию (реализация отношения “is-a” через “has part”). У меня основная проблема с таким подходом — философического свойства. Я понимаю, что такие языковые навороты могут быть удобнее при определённом стиле ОО программирования — но я не уверен, что этот стиль является хорошей практикой. Конечно, я с этим стилем знаком (программировал в модели COM, где шаг не ступишь, не вызвав QI; изредка пользовался средствами RTTI C++а — причём чаще самопальными, чем встроенными в язык), как и, например, со стилем, порождающим богатые иерархии наследования — но в последние годы рефлексирую на тему того, что мои ООП-навыки, приобретённые 15-20 лет назад, устарели, и нынче *правильное* ООП выглядит значительно по-другому. Так вот, я не уверен, что неявное представление информации (клиенту) о том, как с одного интерфейса ("роли") данной сущности получить другой (другую роль, состоящую в каком-то один-к-одному отношении с первой ролью) — вещь сомнительная, потому что:
1. Информация представлена неявно, в лучшем случае, в документации ("а вот, типа, в копилку эрудита — имея на руках интерфейс IFoo его можно закастить к интерфейсу IBar, потому как у нас в реализации оба реализованы на одном классе — вы в public интерфейсе системы никакого упоминание этого не увидите, но это так"). Если клиент начинает этой информацией пользоваться, появляется неявная зависимость в коде (implicit dependency on a legacy client) — глядя на исходники внешнего интерфейса, программист новой версии сервиса может захотеть переделать реализацию так, что IFoo и IBar разъезжаются на разные объекты. Если он это сделает, возникнет неприятность, обнаруживаемая только during execution time (not a build error / warning) — нехорошо.
2. Предполагая (даже указывая в документации), что касты такого типа — OK, сервис открывает в некотором смысле backdoor для “creative” клиента — клиент начнёт кастить направо и налево, эксплуатируя неявные зависимости такого типа и в случаях, когда связь «IFoo239 и IBar45 на самом деле имплементированы на одном объекте» не указаны в документации и на самом деле являются деталью имплементации. Если клиент этим злоупотребит, у сервиса начнутся серьёзные проблемы при будущих рефакторингах.
3. Вообще, реализация нескольких интерфейсов на одном объекте, мне кажется, противоречит принципу separation of concerns, хотя и может оказать удобной и полезной (как в смысле быстроты разработки сервиса, так и в смысле скорости выполнения некоторых частых операций и размера потребляемой памяти). Если это всё-таки сделано, следует этот факт от клиента скрывать, мне кажется — прописав касты (правильные в данной версии сервиса) в явных утилитах вида GetIBarFromIFoo, и строго-настрого запретив пользоваться для этого встроенными в язык кастами (не уверен, что в языке есть средство запретить встроенный каст… На C++ в библиотеке, реализующий «самодельный» RTTI такое сделать в принципе можно — обязав код, вызывающий каст, предъявить некий «допуск», которым будет обладать только реализация GetIBarFromIFoo и никто другой). Дополнительный бонус от сокрытия каста в GetIBarFromIFoo — утилиты такого типа в принципе могут использоваться не только для отношений один-к-одному, но и для один-ко-многим (добавлением дополнительных параметров кроме IFoo*).
(no subject)
Date: 2010-02-06 08:56 am (UTC)(no subject)
Date: 2010-02-09 01:47 am (UTC)(no subject)
Date: 2010-02-09 01:51 am (UTC)