Явная реализация интерфеса без необходимости
От: Pek2014 Россия  
Дата: 24.03.17 16:24
Оценка:
Ситуация: Класс A предназначен для использования другим (другими) классом
B (классами) через внедрение зависимости. Т.е. класс-клиент B будет иметь
переменную (поле) типа интерфейс IA и вызывать класс A, только через интерфейс.

Вопрос: Стоит ли использовать при определении класса А явную реализацию
методов интерфейса IA?

public interface IA
{
    void Do();
}

public class A : IA
{
    void IA.Do() {}
    
    // Реализация IA.Do()
    public void Do() {}
}

public class B 
{
    public B(IA a)
    {
        _a = a;
    }

    void Do()    
    {
        _a.Do();
    }
    private IA _a;
}


Необходимости в явной реализации здесь нет. Но соблазнительно использовать явную
реализацию по таким причинам.

1. Выразительность синтаксиса.

Отпадает необходимость комментировать определение
метода как метода необходимого для реализации интерфейса. Теперь эта мысль
выражается средствами языка. Плюс — не надо (и не получится) писать "public"
для этого метода. Ясно и так, что метод интерфейса публичный... Код становится
более кратким и более понятным.

2. Лучшая диагностика проблем.

Если вы ошиблись в сигнатуре метода, то при неявной реализации, компилятор и решарпер
вам скажут, что интерфейс и нужный ему метод не нерализован. Но они не укажут
пальцем на метод, в сигнатуре которого вы ошиблись. В случае явной реализации —
всё сразу понятно, где ошибка, с какой строчке. И решарпер поможет написать правильно.

Повторюсь, у меня нет не необходимости в явной реализации
(нет конфликта имён методов разных интерфейсов), но нет и расплаты за
явную реализацию (клиенты всё равно всегда работают через интерфейс).

Вопрос: удержаться и сделать неявную реализацию? Что посоветуете?
Re: Явная реализация интерфеса без необходимости
От: Sinix  
Дата: 24.03.17 17:48
Оценка: +1
Здравствуйте, Pek2014, Вы писали:

P>Вопрос: Стоит ли использовать при определении класса А явную реализацию

FDG, Framework Design Fundamentals:

DO NOT require users to explicitly instantiate more than one type in the most basic scenarios
DO NOT require that users perform any extensive initialization before they can start programming basic scenarios.

* instantiate тут == не требовать возиться с несколькими сущностями, там из контекста дальше понятно.

Сценарий использования (предположим, юнит тест):
var a = new A();
a.Do(); // WTF???


Для меня вопрос закрыт уже в этот момент. По крайней мере пока не появятся реальные доводы за явную реализацию.
И "5.1.2 Implementing Interface Members Explicitly", чтоб сомнений не оставалось:

AVOID implementing interface members explicitly without having a strong reason to do so.
Explicitly implemented members can be confusing to developers because such members don't appear in the list of public members and can also cause unnecessary boxing of value types.

...

STEVEN CLARKE
One common observation we have made in the API usability studies is that many developers assume that the reason members have been implemented explicitly is that they are not supposed to be used in common scenarios. Thus, they sometimes have a tendency to avoid using these members and spend time looking for some other way to accomplish their task.


Не, есть и аргументы за, но они относятся в первую очередь к инфраструктурным методам типа ICollection<T>.IsReadOnly.


P>Отпадает необходимость комментировать определение

P>метода как метода необходимого для реализации интерфейса.
ghostdoc + Ctrl-Shift-D, миссия выполнена
Решарпер показывает подсказки, какой метод что переопределяет. Ещё в расширениях студии было что-то похожее.


P>выражается средствами языка. Плюс — не надо (и не получится) писать "public"

P>для этого метода.
Борьба с синтаксическим оверхедом — это не наш метод
Модификаторы автоматом должны при сохранении расставляться, не надо тратить время на ручной труд.


P>Если вы ошиблись в сигнатуре метода, то при неявной реализации, компилятор и решарпер

P>вам скажут, что интерфейс и нужный ему метод не нерализован. Но они не укажут
P>пальцем на метод, в сигнатуре которого вы ошиблись.
Из текста ошибки имя метода видно. Если стоит решарпер — Alt-\ + пару заглавных букв из имени метода. К примеру, GetStoredTimings() находится по "gt".
* шорткат советую на что-то приятнее поменять. Я обычно вешаю поиск по имени local member на Ctrl-Alt-Q, глобальный поиск (тот, что Ctrl-T по дефолту) — на Ctrl-Q.


P>Вопрос: удержаться и сделать неявную реализацию? Что посоветуете?

Проектировать API от реальных сценариев использования, конечно же.
Re[2]: Явная реализация интерфеса без необходимости
От: Pek2014 Россия  
Дата: 24.03.17 18:22
Оценка:
Здравствуйте, Sinix, Вы писали:

P>>Вопрос: Стоит ли использовать при определении класса А явную реализацию

S>FDG, Framework Design Fundamentals:

S>Сценарий использования (предположим, юнит тест):

S>
S>var a = new A();
S>a.Do(); // WTF???
S>


В моём случае точно известно, что клиенты не будут использовать класс на прямую. Они будут получать его интерфейс и работать только через него.

S>

S>AVOID implementing interface members explicitly without having a strong reason to do so.
S>Explicitly implemented members can be confusing to developers because such members don't appear in the list of public members and can also cause unnecessary boxing of value types.


FDG — это рекомендации по написанию, широко повторно-используемого кода (кода библиотек, Framework'ов, etc). Там свои законы. Они во многом универсальные, но не на 100%.
А для написания конкретного кода, кода приложений и конкретных конечных сервисов, могут и должны применяться несколько другие правила подходы и правила.
Это к тому, что вряд ли в моём случае эта рекомендация абсолютно закрывает тему.

S>Решарпер показывает подсказки, какой метод что переопределяет. Ещё в расширениях студии было что-то похожее.


Это помогает...

P>>выражается средствами языка. Плюс — не надо (и не получится) писать "public" для этого метода.

S>Модификаторы автоматом должны при сохранении расставляться, не надо тратить время на ручной труд.

Написать вручную лишнее слово мне не трудно. Не хочется потом читать, это не несущее никакой информации слово.
Всё-таки мне нравится "выразительность" синтаксиса явной реализации интерфейса (чисто эстетически).

P>>Если вы ошиблись в сигнатуре метода, то при неявной реализации, компилятор и решарпер вам скажут, что интерфейс и нужный ему метод нерализован. Но они не укажут пальцем на метод, в сигнатуре которого вы ошиблись.


S>Из текста ошибки имя метода видно. Если стоит решарпер — Alt-\ + пару заглавных букв из имени метода. К примеру, GetStoredTimings() находится по "gt".

S>* шорткат советую на что-то приятнее поменять. Я обычно вешаю поиск по имени local member на Ctrl-Alt-Q, глобальный поиск (тот, что Ctrl-T по дефолту) — на Ctrl-Q.

Это помогает... Но в случае с явной реализацией всё ещё удобнее.

P>>Вопрос: удержаться и сделать неявную реализацию? Что посоветуете?

S>Проектировать API от реальных сценариев использования, конечно же.

Реальный сценарий использования в моём случае такой, что от явной реализации никто не страдает.
Никому никаких неудобств, один сплошной профит (как мне кажется). Кроме нарушения рекомендаций...
Re[3]: Явная реализация интерфеса без необходимости
От: Mystic Artifact  
Дата: 24.03.17 18:59
Оценка:
Здравствуйте, Pek2014, Вы писали:

P>Реальный сценарий использования в моём случае такой, что от явной реализации никто не страдает.

P>Никому никаких неудобств, один сплошной профит (как мне кажется). Кроме нарушения рекомендаций...
Я реализую класс и у него есть explicit интерфейс. Интерфейс — это единственное, что видно снаружи (как у тебя). При этом именно explicit реализация интерфейса не мешает реализовывать сам класс (реализация не должна звать ничего от интерфейса). А вот реализация интерфейса — напротив должна звать методы/проперти этого класса. Да, класс сделал ессно partial, что бы легче было отделить мух от котлет. А отделять очень нужно, потому что класс такой не один и интерфейсы немножко жирненькие (100+ членов).
В принципе у меня интерфейс — это чистый прокси, я сначала и городил прокси объекты, но их городить... затем понадобились двусторонние маппинги — а оно и без этого хлопот хватает. А внешний потребитель объектов вдобавок требует "reference equality", так что плеваться просто так новыми объектами-проксиками неполучается.
Главное — что бы в итоге это было удобно.
Re[3]: Явная реализация интерфеса без необходимости
От: Sinix  
Дата: 24.03.17 19:02
Оценка: 10 (2) +2
Здравствуйте, Pek2014, Вы писали:

P>Написать вручную лишнее слово мне не трудно. Не хочется потом читать, это не несущее никакой информации слово.

Как-то оно наоборот получается:
public void Do(){ ... }
// vs
void ISomeSpecificThing.Do() { ... }



P>Никому никаких неудобств, один сплошной профит (как мне кажется). Кроме нарушения рекомендаций...

До момента, когда к вам в команду придёт человек, которого засадят за тесты.
Или потребуется создать наследника от A.
Или вызвать из одного метода-реализации другой метод.
Или обратиться к методам через TypeDescriptor/рефлексию.
Или прибиндить свойство к контролу.
Или вызвать через dynamic (к примеру, для "дармового" double dispatch).
Или перебрать типы через паттерн-матчинг.
Или заюзать A как генерик-параметр, к примеру чтобы собрать expression tree или скормить mapper-у.

Ну и тыды и тыпы. В общем, пока сам не понаступаешь на грабли (особенно обидно, когда они чужие и исправить их уже в принципе нельзя) — не поймёшь, что без веских оснований велосипед изобретать не надо. Удачи в этом нелёгком деле
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.