В субботу вечером у нас вроде пока ничего не было запланировано — можем и приехать! В какое время удобнее?
Про 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-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*).