Как создать расширяемый API и по-прежнему использовать синтаксис инициализатора объекта? - PullRequest
0 голосов
/ 04 декабря 2010

У меня есть библиотека классов, которая упаковывает клиент командной строки для Mercurial.

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

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

На данный момент синтаксис команды выглядит следующим образом:

Repo.Execute(new CommitCommand
{
    Message = "Your commit message",
    AddRemove = true,
});

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

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

var cmd = new CommitCommand
{
    Message = "Your commit message",
    AddRemove = true,
};
cmd.Arguments.Add("--my-commit-extension");
Repo.Execute(cmd);

Кажется, мне нелегко добавить это дополнительное расширение таким образом, чтобы его можно было установить как часть инициализатора объекта.

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

Repo.Execute(new CommitCommand()
    .Message("Your commit message")
    .AddRemove()
    .MyCommitExtension());

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

Какие еще есть варианты?

Что я хочу, в основном:

  • Один общий стиль синтаксиса
    • Для обеих встроенных вещей
    • А также расширения, добавленные пользователями моей библиотеки

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

Любые идеи приветствуются.

Ответы [ 3 ]

1 голос
/ 05 декабря 2010

Я тоже не очень знаком с Mercurial, но один из вариантов - выбросить intellisense и использовать анонимные типы:

Repo.Execute(new 
{
    Message = "Your commit message",
    AddRemove = true,
    MyCommitExtension = null
});

Вы не можете использовать дефисы в именах свойств, поэтому вам нужно заменить PascalCase на дефис в нижнем регистре. В качестве альтернативы вы можете просто заменить подчеркивание гипсами.

Я не уверен, что рекомендую такой подход, не зная больше о частоте, с которой люди будут использовать эти расширения. Этот подход хорошо работает в среде ASP.NET MVC при работе с атрибутами HTML, но этот сценарий отличается тем, что среда не должна обрабатывать значения, она просто записывает их непосредственно в выходные данные. Если вы выполняете условные действия, основанные на значениях, предоставленных таким образом, или вы хотите, чтобы ваш API был более доступным для тех, кто не знает синтаксис командной строки Mercurial, тогда вы можете попробовать другой подход.

1 голос
/ 05 декабря 2010

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

var cmd = new CommitCommand 
{ 
    Message = "Your commit message", 
    AddRemove = true, 
}; 
cmd.Arguments.Add("--my-commit-extension"); 
Repo.Execute(cmd); 

Если CommitCommand.Arguments равен IList<T>, у вас уже есть возможностьиспользовать синтаксис инициализатора:

class CommitCommand
{
    public string Message { get; set; }
    public bool AddRemove { get; set; }
    public List<string> Arguments = new List<string>();
}

Repo.Execute(new CommitCommand
{
    Message = "Your commit message",
    AddRemove = true,
    Arguments = { "--my-commit-extension", "--my-other-commit-extension" }
});
0 голосов
/ 26 января 2011

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

interface IExtension { ... }

class SomeExtension1 : IExtension { ... }
class SomeExtension2 : IExtension { ... }

class CommitCommand
{
    public string Message;
    public bool AddRemove;
    public readonly IList<IExtension> Extensions = new List<IExtension>();
}

Это позволит использовать команды следующим образом:

new CommitCommand
{
    Message = "",
    AddRemove = true,
    Extensions = {new SomeExtension1(), new SomeExtension2()}
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...