Комбинирование extensions
От: StatujaLeha на правах ИМХО
Дата: 13.10.16 21:07
Оценка:
Задача такая:
1. Есть ряд базовых операций, которые реализованы как extensions для интерфейсов ISimpleInterfaceXXX
2. Нужно уметь плодить различные классы, которые наследуют соответствующие интерфейсы и используют их в своей логике.
3. С использованием ISimpleInterfaceXXX проблем нет.
4. Вопрос: как лучше реализовывать новые extensions, которые требуют наличия уже нескольких реализованных ISimpleInterfaceXXX?
На данный момент это сделано так, как в примере ниже: IComplexInterface + IComplexInterfaceExtensions + GetSimpleInterfaceXXX + InitializeGuard в каждом extension.

Еще варианты?

    interface ISimpleInterfaceFirst
    {
        /* Mandatory members */
    }

    static class ISimpleInterfaceFirstExtensions
    {}

    interface ISimpleInterfaceSecond
    {
        /* Mandatory members */
    }

    static class ISimpleInterfaceSecondExtensions
    {}

    interface IComplexInterface
    {
        /* Mandatory members */
    }

    static class IComplexInterfaceExtensions
    {
        private static void InitializeGuard(this IComplexInterface s)
        {
            if (!(s is ISimpleInterfaceFirst && s is ISimpleInterfaceSecond))
                throw new Exception("Bad usage");
        }

        private static ISimpleInterfaceFirst GetSimpleInterfaceFirst(this IComplexInterface s)
        {
            return s as ISimpleInterfaceFirst;
        }

        private static ISimpleInterfaceSecond GetSimpleInterfaceSecond(this IComplexInterface s)
        {
            return s as ISimpleInterfaceSecond;
        }

        public static void ExtensionMethod(this IComplexInterface s)
        {
            s.InitializeGuard();

            //Logic using ISimpleInterfaceFirst and ISimpleInterfaceSecond via GetSimpleInterfaceFirst and GetSimpleInterfaceSecond
        }
    }

    //Делаем доступными операции из обоих интерфейсов
    class SimpleLogic: ISimpleInterfaceFirst, ISimpleInterfaceSecond
    {}

    //Делаем доступными операции из обоих интерфейсов + операции, требующий реализацию обоих интерфейсов
    class ComplexLogic: ISimpleInterfaceFirst, ISimpleInterfaceSecond, IComplexInterface
    {}
Re: Комбинирование extensions
От: Sinix  
Дата: 14.10.16 06:16
Оценка: 12 (1) +1
Здравствуйте, StatujaLeha, Вы писали:

SL>Еще варианты?


UPD: перечитал ответ — как-то резковато получилось, хотя не собирался даже. Расставьте розовых поней и прочих смайликов по вкусу, ок?

Если extensions требуют несколько разных интерфейсов — у вас проблема с дизайном иерархии типов (впрочем, это и так понятно, городить сложное API поверх интерфейсов имеет смысл только в очень ограниченном наборе сценариев). Если распишете подробнее — будет конкретика, если нет — см ниже.

1. Как насчёт варианта с простым наследованием?

2. Если нет — требуйте зависимости явно, ч/з
interface IComplexInterface : ISimpleInterfaceFirst, ISimpleInterfaceSecond
// или
public void Do<T>(T arg)
  where T: ISimpleInterfaceFirst, ISimpleInterfaceSecondExtensions


Неудобно? Ну а смысл тогда писать код, которым самому неудобно пользоваться, я уж про пользователей молчу?

3. Если снова нет — рассмотрите любой язык с динамической типизацией, в шарпе всё-таки падать в последний момент, без жалоб в момент компиляции, не принято

P.S.
Гадлайны таки читать стоит, имя класса не должно быть с I. По вопросу: FDG, Choosing Between Class and Interface

DO favor defining classes over interfaces.
Class-based APIs can be evolved with much greater ease than interface-based APIs because it is possible to add members to a class without breaking existing code.

" KRZYSZTOF CWALINA Over the course of the three versions of the .NET Framework, I have talked about this guideline with quite a few developers on our team. Many of them, including those who initially disagreed with the guideline, have said that they regret having shipped some API as an interface. I have not heard of even one case in which somebody regretted that they shipped a class.

" JEFFREY RICHTER I agree with Krzysztof in general. However, you do need to think about some other things. There are some special base classes, such as MarshalByRefObject. If your library type provides an abstract base class that isn't itself derived from MarshalByRefObject, then types that derive from your abstract base class cannot live in a different AppDomain.

...

DO use abstract classes instead of interfaces to decouple the contract from implementations.
Abstract classes, if designed correctly, allow for the same degree of decoupling between contract and implementation.

" BRIAN PEPIN Another sign that you've got a well-defined interface is that the interface does exactly one thing. If you have an interface that has a grab bag of functionality, that's a warning sign. You'll end up regretting it because in the next version of your product you'll want to add new functionality to this rich interface, but you can't.


Ну и заключительный:

" KRZYSZTOF CWALINA I often hear people saying that interfaces specify contracts. I believe this is a dangerous myth. Interfaces, by themselves, do not specify much beyond the syntax required to use an object. The interface-as-contract myth causes people to do the wrong thing when trying to separate contracts from implementation, which is a great engineering practice. Interfaces separate syntax from implementation, which is not that useful, and the myth provides a false sense of doing the right engineering. In reality, the contract is semantics, and these can actually be nicely expressed with some implementation.

Отредактировано 14.10.2016 6:38 Sinix . Предыдущая версия . Еще …
Отредактировано 14.10.2016 6:19 Sinix . Предыдущая версия .
Re[2]: Комбинирование extensions
От: StatujaLeha на правах ИМХО
Дата: 14.10.16 07:13
Оценка: 46 (1) +1
Здравствуйте, Sinix, Вы писали:

S>UPD: перечитал ответ — как-то резковато получилось, хотя не собирался даже. Расставьте розовых пони и прочих смайликов по вкусу, ок?


ок

S>Если extensions требуют несколько разных интерфейсов — у вас проблема с дизайном иерархии типов (впрочем, это и так понятно, городить сложное API поверх интерфейсов имеет смысл только в очень ограниченном наборе сценариев). Если распишете подробнее — будет конкретика, если нет — см ниже.

Есть набор веб-сервисов, которые надо тестировать.

Есть некоторый класс Base, который инкапсулирует в себя самую базовую логику, которая нужна в любом тесте.
Классы типа SimpleLogic и ComplexLogic наследуют от этого класса.

Логика в этих классах требует доступа к некоторым веб-сервисам(у каждого класса к своим).
ISimpleInterfaceХХХ, ISimpleInterfaceХХХExtensions — это обертка над операциями каждого веб-сервиса.
Т.е. если мне надо создать новый класс SuperLogic, у которого логика работы требует доступа к сервисам X и Y, то это просто:
class SuperLogic: Base, IInterfaceX, IInterfaceY {}


Иногда бывает так, что появляются новые сервисы: я просто добавляю новый интерфейс и extensions к нему.
Иногда бывает так, что надо доработать уже существующий класс и для этого требуется доступ к сервису, с которым этот класс ранее не работал: тут у меня тоже все просто.
class SuperLogic: Base, IInterfaceX, IInterfaceY, IInterfaceZ {}

Старая логика работает без изменений, новую можно спокойно добавить и отдать на тестирование(регрессионное тестирование старой логики при этом не требуется).

S>1. Как насчёт варианта с простым наследованием?

Он до меня был занят

S>2. Если нет — требуйте зависимости явно, ч/з

S>
S>interface IComplexInterface : ISimpleInterfaceFirst, ISimpleInterfaceSecond
S>// или
S>public void Do<T>(T arg)
S>  where T: ISimpleInterfaceFirst, ISimpleInterfaceSecondExtensions
S>

т.е. так? Выглядит лучше моей текущей идеи.
    static class IComplexInterfaceExtensions
    {
        public static void ExtensionMethod<T>(this T s) where T: ISimpleInterfaceFirst, ISimpleInterfaceSecond
        {}
    }


S>Неудобно? Ну а смысл тогда писать код, которым самому неудобно пользоваться, я уж про пользователей молчу?

В том-то и дело, что пользоваться им удобно: геморроя либо нет, либо он весь в этих extensions. Пользователь отнаследовался и использует.
За год работы с этим все ок было: добавить новый сервис без особых затрат — пожалуйста, переиспользовать логику доступа к сервису в нескольких классах — нет проблем, модифицировать старую логику, добавив работу с новым сервисом — ок.
Сейчас на проекте есть студент без опыта работы с .Net/C#, осваивает: у него все ок. Тупо наследуется, особо не вникает в детали, работу делает, сломать ничего не может

S>3. Если снова нет — рассмотрите любой язык с динамической типизацией, в шарпе всё-таки падать в последний момент, без жалоб в момент компиляции, не принято

InitializeGuard тут ок.
Если кто-то создаст что-то такое(т.е. нет необходимого ISimpleInterfaceSecond), то тестировщики оперативно возмутятся, что падает и у автора будет единственный вариант все исправить: добавить необходимый интерфейс.
    class ComplexLogic: ISimpleInterfaceFirst, IComplexInterface//ISimpleInterfaceSecond(так не надо делать :))
    {}

Но вариант с generic type constraint лучше будет. Там на этапе компиляции уже ничего сломать не дадут.

S>

S>P.S.
Гадлайны таки читать стоит, имя класса не должно быть с I. По вопросу: FDG, Choosing Between Class and Interface
Да, я прочитал эту книжку.
Там выше ответил: от базового класса наследуется самая базовая логика, а от интерфейсов нужна возможность привносить/комбинировать работу с различными сервисами.
Re[2]: Комбинирование extensions
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 14.10.16 08:31
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Ну и заключительный:

S>

S>" KRZYSZTOF CWALINA I often hear people saying that interfaces specify contracts. I believe this is a dangerous myth. Interfaces, by themselves, do not specify much beyond the syntax required to use an object. The interface-as-contract myth causes people to do the wrong thing when trying to separate contracts from implementation, which is a great engineering practice. Interfaces separate syntax from implementation, which is not that useful, and the myth provides a false sense of doing the right engineering. In reality, the contract is semantics, and these can actually be nicely expressed with some implementation.


Откровение прям. Зато теперь понятно что менее тараканистым дизайн MEF в принципе быть не мог, с такими то теориями.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[3]: Комбинирование extensions
От: Sinix  
Дата: 14.10.16 09:06
Оценка:
Здравствуйте, StatujaLeha, Вы писали:

SL>Есть некоторый класс Base, который инкапсулирует в себя самую базовую логику, которая нужна в любом тесте.

SL>Классы типа SimpleLogic и ComplexLogic наследуют от этого класса.

А, ну тогда ничего проще, чем перечислять интерфейсы в where T: не получится. Не, можно ограничиться where T:IComplexInterface, но если у вас куча разных комбинаций интерфейстов, то проще перечислить нужное по месту использования, чем прописать все комбинации в классах, которые эти интерфейсы реализуют.
Re[3]: Комбинирование extensions
От: Sinix  
Дата: 14.10.16 09:25
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Откровение прям. Зато теперь понятно что менее тараканистым дизайн MEF в принципе быть не мог, с такими то теориями.


Не в порядке спора: а с MEF-то что не так? Я про свежий.
Re[4]: Комбинирование extensions
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 14.10.16 14:32
Оценка:
Здравствуйте, Sinix, Вы писали:

AVK>>Откровение прям. Зато теперь понятно что менее тараканистым дизайн MEF в принципе быть не мог, с такими то теориями.

S>Не в порядке спора: а с MEF-то что не так?

Архитектура у него какая то странноватая, мягко говоря.

S> Я про свежий.


Свежий не смотрел.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[5]: Комбинирование extensions
От: Sinix  
Дата: 14.10.16 15:52
Оценка:
Здравствуйте, AndrewVK, Вы писали:

S>>Не в порядке спора: а с MEF-то что не так?

AVK>Архитектура у него какая то странноватая, мягко говоря.
А. Я снаружи им в основном пользовался. С этой стороны более чем ок.
Re[3]: Комбинирование extensions
От: ionoy Эстония www.ammyui.com
Дата: 15.10.16 13:33
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Здравствуйте, Sinix, Вы писали:


S>>Ну и заключительный:

S>>

S>>" KRZYSZTOF CWALINA I often hear people saying that interfaces specify contracts. I believe this is a dangerous myth. Interfaces, by themselves, do not specify much beyond the syntax required to use an object. The interface-as-contract myth causes people to do the wrong thing when trying to separate contracts from implementation, which is a great engineering practice. Interfaces separate syntax from implementation, which is not that useful, and the myth provides a false sense of doing the right engineering. In reality, the contract is semantics, and these can actually be nicely expressed with some implementation.


AVK>Откровение прям. Зато теперь понятно что менее тараканистым дизайн MEF в принципе быть не мог, с такими то теориями.


Полагаться на FDG, как на истину в последней инстанции, ихмо, не стоит.
Судя по всему ребята из МС и сами не слишком то следуют этим указаниям. Что, на самом деле, хорошо.
www.livexaml.com
www.ammyui.com
www.nemerleweb.com
Отредактировано 15.10.2016 13:33 ionoy . Предыдущая версия .
Re[4]: Комбинирование extensions
От: Sinix  
Дата: 15.10.16 15:02
Оценка:
Здравствуйте, ionoy, Вы писали:

I>Полагаться на FDG, как на истину в последней инстанции, ихмо, не стоит.

I>Судя по всему ребята из МС и сами не слишком то следуют этим указаниям. Что, на самом деле, хорошо.

Ну так FDG — это не "делай так, и никак иначе", это "принимать решения по дизайну надо вот так" + "чтобы не изобретать своё — начни вот с этого, если нет явных доводов за любой из других вариантов".
Подразумевается, что любой читавший FDG это и так знает, там чуть ли не половина вступительной части этому посвящена.
Ну а если голову не включать, то никакая книжка не поможет, финал немного предсказуем получится.

На самом деле, совет в цитате абсолютно правильный. Если разделить ответственности не получается даже с базовым абстрактным классом, то выделять интерфейс и заявлять "вот, я изолировал" точно не следует
Обсуждалось кучу раз, как пример
Автор: Sinix
Дата: 05.12.13

Если контракт одинаков для нескольких реализаций — ничего не мешает выделить контракт в интерфейс и подменять реализации по мере необходимости. Однако само по себе наличие/отсутствие интерфейса ничего не гарантирует, за "качество" абстракции отвечает разработчик. Поэтому контракт Dictionary хорош сам по себе, вне зависимости от наличия/отсутствия IDictionary, а TraceSource (даже если его спрятать за ITraceSource) — нет.

Re[4]: Комбинирование extensions
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 15.10.16 19:55
Оценка:
Здравствуйте, ionoy, Вы писали:

I>Полагаться на FDG, как на истину в последней инстанции, ихмо, не стоит.


Да тут не столько FDG, сколько личное мнение конкретного персонажа. Там же рядом коммент Рихтера совсем иначе звучит. И не стоит особо преувеличивать его "в основном согласен" — Джеффри повышенным ЧСВ не страдает и старается особо не спорить, но свое мнение таки доносит.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.