Какой смысл использовать DSL / свободные интерфейсы - PullRequest
26 голосов
/ 26 февраля 2009

Недавно я смотрел веб-трансляцию о , как создать беглый DSL , и я должен признать, что я не понимаю причин, по которым можно было бы использовать такой подход (по крайней мере, для данного примера) .

В веб-трансляции был представлен класс изменения размера изображения, который позволяет указать входное изображение, изменить его размер и сохранить его в выходном файле, используя следующий синтаксис (с использованием C #):

Sizer sizer = new Sizer();
sizer.FromImage(inputImage)
     .ToLocation(outputImage)
     .ReduceByPercent(50)
     .OutputImageFormat(ImageFormat.Jpeg)
     .Save();

Я не понимаю, как это лучше, чем "обычный" метод, который принимает некоторые параметры:

sizer.ResizeImage(inputImage, outputImage, 0.5, ImageFormat.Jpeg);

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

sizer.ToLocation(outputImage).Save();

Итак, мои вопросы:

1 - Есть ли какой-нибудь способ улучшить удобство использования беглого интерфейса (т.е. сообщить пользователю, что он должен делать)?

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

sizer.Resize(from:input, to:output, resizeBy:0.5, ..)

3 - Используются ли текущие интерфейсы просто потому, что они в настоящее время популярны?

4 - Или это был просто плохой пример, который был выбран для веб-трансляции? В таком случае, скажите мне, в чем преимущества такого подхода, и где имеет смысл его использовать.

Кстати: я знаю о jquery и вижу, как легко это сделать, поэтому я не ищу комментариев об этом или других существующих примерах.

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

Ответы [ 7 ]

13 голосов
/ 26 февраля 2009

2 - это свободный подход интерфейса просто замена для не существующие параметры именованных методов в C #? Были бы названные параметры, чтобы бегло интерфейсы устарели, например что-то Подобные предложения target-C:

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

sizer.FromImage(i)
 .ReduceByPercent(x)
 .Pixalize()
 .ReduceByPercent(x)
 .OutputImageFormat(ImageFormat.Jpeg)
 .ToLocation(o)
 .Save();

FromImage, ToLocation и OutputImageFormat в флюидном интерфейсе меня немного пахнут. Вместо этого я бы сделал что-то в этом роде, что, мне кажется, намного понятнее.

 new Sizer("bob.jpeg") 
 .ReduceByPercent(x)
 .Pixalize()
 .ReduceByPercent(x)
 .Save("file.jpeg",ImageFormat.Jpeg);

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

var sb = new StringBuilder(); 
sb.AppendLine("Hello")
 .AppendLine("World"); 
8 голосов
/ 26 февраля 2009

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

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

Ваш пример выглядит как перебор.

В последнее время я сделал некоторый свободный интерфейс поверх SplitterContainer из Windows Forms. Возможно, семантическая модель иерархии элементов управления несколько сложна для правильного построения. Предоставляя небольшой свободный API, разработчик может теперь декларативно выразить, как должен работать его SplitterContainer. Использование идет как

var s = new SplitBoxSetup();
s.AddVerticalSplit()
 .PanelOne().PlaceControl(()=> new Label())
 .PanelTwo()
 .AddHorizontalSplit()
 .PanelOne().PlaceControl(()=> new Label())
 .PanelTwo().PlaceControl(()=> new Panel());
form.Controls.Add(s.TopControl);

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

Надеюсь, это поможет

5 голосов
/ 26 февраля 2009

Рассмотрим:

sizer.ResizeImage(inputImage, outputImage, 0.5, ImageFormat.Jpeg);

Что если вы использовали менее понятные имена переменных:

sizer.ResizeImage(i, o, x, ImageFormat.Jpeg);

Представь, что ты распечатал этот код. Труднее понять, что это за аргументы, так как у вас нет доступа к сигнатуре метода.

С беглым интерфейсом это становится понятнее:

 sizer.FromImage(i)
 .ToLocation(o)
 .ReduceByPercent(x)
 .OutputImageFormat(ImageFormat.Jpeg)
 .Save();

Кроме того, порядок методов не важен. Это эквивалентно:

 sizer.FromImage(i)
 .ReduceByPercent(x)
 .OutputImageFormat(ImageFormat.Jpeg)
 .ToLocation(o)
 .Save();

Кроме того, возможно, у вас могут быть значения по умолчанию для формата выходного изображения и сокращения, поэтому это может стать:

 sizer.FromImage(i)
 .ToLocation(o)
 .Save();

Это потребует перегруженных конструкторов для достижения того же эффекта.

2 голосов
/ 02 марта 2009

Вы должны прочитать Domain Driven Design Эрика Эванса, чтобы понять, почему DSL считается хорошим выбором дизайна.

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

2 голосов
/ 26 февраля 2009

Это один из способов реализации вещей.

Для объектов, которые ничего не делают, но манипулируют одним и тем же предметом снова и снова, в этом нет ничего действительно плохого. Рассмотрим потоки C ++: они являются окончательными в этом интерфейсе. Каждая операция снова возвращает поток, поэтому вы можете связать вместе другую операцию потока.

Если вы выполняете LINQ и манипулируете объектом снова и снова, это имеет некоторый смысл.

Однако в своем дизайне вы должны быть осторожны. Каким должно быть поведение, если вы хотите отклониться на полпути? (IE,

var obj1 = object.Shrink(0.50); // obj1 is now 50% of obj2
var obj2 = object.Shrink(0.75); // is ojb2 now 75% of ojb1 or is it 75% of the original?

Если obj2 составлял 75% от исходного объекта, то это означает, что вы делаете полную копию объекта каждый раз (и во многих случаях имеет свои преимущества, например, если вы пытаетесь сделать два экземпляра одного и того же объекта) вещь, но немного по-другому).

Если методы просто манипулируют исходным объектом, то этот вид синтаксиса несколько неискренен. Это манипуляции с объектом, а не манипуляции для создания измененного объекта.

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

1 голос
/ 25 января 2011

Можно использовать вариант интерфейса Fluent для принудительного применения определенных комбинаций необязательных параметров (например, требовать, чтобы присутствовал хотя бы один параметр из группы, и требовать, чтобы, если был задан определенный параметр, какой-то другой параметр не указывался) ). Например, можно обеспечить функциональность, аналогичную Enumerable.Range, но с синтаксисом, например IntRange.From (5) .Upto (19) или IntRange.From (5) .LessThan (10) .Stepby (2) или IntRange ( 3) .Count (19) .StepBy (17). Для обеспечения выполнения слишком сложных требований к параметрам во время компиляции может потребоваться определение раздражающего числа структур или классов промежуточных значений, но в некоторых случаях этот подход может оказаться полезным в более простых случаях.

0 голосов
/ 06 апреля 2013

В дополнение к предложению @ sam-saffron относительно гибкости интерфейса Fluent при добавлении новой операции:

Если нам нужно добавить новую операцию, такую ​​как Pixalize (), то в сценарии «метод с несколькими параметрами» это потребует добавления нового параметра в сигнатуру метода. Это может затем потребовать модификации для каждого вызова этого метода в кодовой базе, чтобы добавить значение для этого нового параметра (если используемый язык не допускает необязательный параметр).

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

...