Советы по написанию свободных интерфейсов в C # 3 - PullRequest
43 голосов
/ 22 октября 2008

Я после нескольких хороших советов по беглым интерфейсам в C #. Я просто узнаю об этом сам, но стремлюсь услышать, что другие думают за пределами статей, которые я читаю. В частности я после:

  1. когда слишком много говорит?
  2. Есть ли беглые узоры?
  3. что в C # делает текучие интерфейсы более свободными (например, методы расширения)
  4. является ли сложный свободный интерфейс все еще свободным?
  5. рефакторинг для получения свободного интерфейса или рефакторинг существующего интерфейса
  6. Какие-нибудь хорошие примеры, с которыми вы работали или могли бы порекомендовать?

Если бы вы могли опубликовать один совет или мысль, или что-то еще на пост. Я тоже хочу посмотреть, как за них проголосовали.

Заранее спасибо.

Ответы [ 8 ]

27 голосов
/ 01 февраля 2009

Единственная самая большая проблема, с которой я столкнулся как потребителя беглых интерфейсов, заключается в том, что большинство из них не являются действительно беглыми интерфейсами - они действительно являются примерами того, что я обычно называю «разборчивыми интерфейсами».

Свободный интерфейс подразумевает, что его основная цель состоит в том, чтобы облегчить его ГОВОРИТЬ, в то время как разборчивый интерфейс подразумевает, что его основная цель состоит в том, чтобы его было легко ПРОЧИТАТЬ. Большинство свободно распространяемых интерфейсов, как правило, нелепо трудны для кодирования, но, наоборот, невероятно легко ЧИТАТЬ позже другими.

Assert().That().This(actual).Is().Equal().To(expected).
    Except().If(x => x.GreaterThan(10));

... намного легче читать позже, чем на самом деле писать в коде!

18 голосов
/ 22 октября 2008

по вашему 4-му баллу;

Да, я думаю, что сложный свободный интерфейс все еще может быть свободным.

Я думаю, что свободные интерфейсы являются чем-то вроде компромисса. (хотя и хороший!) Было проведено много исследований по использованию естественного языка для программирования, и, как правило, естественный язык недостаточно точен для выражения программ.

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

Если вы посмотрите на макеты носорога, например, часть написания была более сложной по сравнению с обычной библиотекой. Я потратил больше времени на изучение, в основном из-за свободного интерфейса, но это делает код намного проще для чтения. Поскольку программы обычно пишутся один раз и читаются много раз, это хороший компромисс.

Итак, чтобы немного уточнить мою точку зрения. Свободный интерфейс, который сложно написать, но легко читаемый, все еще может быть свободным.

11 голосов
/ 16 февраля 2009

При использовании наследования наряду с текучими интерфейсами вы столкнетесь с проблемой, потому что полиморфные методы разрывают цепочки вызовов, и вы определенно не хотите, чтобы ваши интерфейсы оставались свободными, используя уродливое приведение и парантез там, где они не нужны. Я написал статью о шаблоне, который предоставляет вам обходной путь, используя универсальные компоновщики и универсальные методы расширения с универсальными ограничениями: http://liviutrifoi.wordpress.com/2009/02/16/fluent-interfaces-constraints-at-compile-time/

8 голосов
/ 25 августа 2009

Moq скрывает неопубликованные методы, такие как equals, ToString и т. Д., Чтобы сделать их плавный интерфейс еще проще в использовании.

Скрытие системного объекта - статья, объясняющая преимущества этого.

7 голосов
/ 25 ноября 2009

Я тоже только начинаю изучать, как написать свободный интерфейс для небольшого приложения на работе. Я немного поинтересовался и немного исследовал, и обнаружил, что хороший подход к написанию свободного интерфейса - это использование «шаблона Builder», подробнее об этом здесь .

По сути, так я и начал свой:

public class Coffee
{
    private bool _cream;
    private int _ounces;

    public Coffee Make { get new Coffee(); }

    public Coffee WithCream()
    {
        _cream = true;
        return this;
    }

    public Coffee WithOuncesToServe(int ounces)
    {
        _ounces = ounces;
        return this;
    }
}

Вот кросс-пост к аналогичный вопрос У меня есть для реализации замыкания в свободном интерфейсе.

7 голосов
/ 22 октября 2008

А на ваш 2-й и 3-й вопрос;

Три беглых рисунка, которые я заметил

Первый использует оператор using (C # 2.0) для запуска кода в определенном контексте, например:

using(var transaction = new Transaction())
{
  // ..
  // ..
}

При этом используется конструктор и средство удаления Transaction для настройки транзакции, а затем запускается код в этом контексте.

Второй делает почти то же самое, но с лямбдами, это часто используется в Rhino Mocks, например.

(new Transaction()).Run( () => mycode(); );

Самый известный свободный интерфейс - это использование возвращаемых типов для цепочки вызовов методов. В основном методы возвращают это, так что вы можете соединять вызовы для одного и того же объекта. Но вы также можете вернуть разные объекты, чтобы изменить контекст в зависимости от вызванного метода. Если у вас есть объект, который может быть запущен только в транзакции (извините, не могу представить другой пример), вы можете предоставить ему метод StartTransaction, который возвращает инициализированную транзакцию, в которой вы можете запустить call run и stoptransaction, в псевдокоде:

class Runner
{
  Transaction StartTransaction()
  {
    return new Transaction(this);
  }
}

class Transaction
{
  Transaction Run()
  Transaction StopTransaction()
}

где вызов выглядит как

var runner = new Runner();
runner
  .StartTransaction()
  .Run()
  .StopTransaction();

Конечно, вам нужно добавить все виды обработки ошибок и т. Д.

4 голосов
/ 20 августа 2013

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

// Snarky employees get a raise.
employees.WhereSnarky().GiveRaise();

против

// Depending on implementation, everyone may get a raise.
employees.GiveRaise().WhereSnarky();
2 голосов
/ 05 августа 2010

Некоторое время назад у меня были те же сомнения, что и у вас сейчас. Я провел некоторое исследование и сейчас пишу несколько постов, чтобы помочь в этих темах.

Проверьте это в моем блоге:

Руководство по разработке интерфейса Fluent в C # часть 1

И в следующих постах я расскажу о каждом из упомянутых вами пунктов.

С наилучшими пожеланиями Андре Вианна

...