Подтвердить что ты не робот

Какая точка DSL/свободного интерфейса

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

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

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 - Является ли этот свободный интерфейс просто заменой неименованных параметров метода в С#? Именованные параметры упрощают доступность интерфейсов, например. что-то подобное objective-C предлагает:

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

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

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

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

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

4b9b3361

Ответ 1

2 - Является ли этот свободный интерфейс просто замена для не существующих параметров метода С#? Именованные параметры станут свободными интерфейсы устарели, например. что нибудь аналогичный objective-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"); 

Ответ 2

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

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

Ваш пример кажется немного переполненным.

В последнее время я сделал небольшой интерфейс поверх 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);

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

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

Ответ 3

Рассмотрим:

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();

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

Ответ 4

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

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

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

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

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

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

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