0

J'ai une bibliothèque de classes qui enveloppe le client en ligne de commande pour Mercurial.Comment créer une API extensible et toujours utiliser la syntaxe d'initialisation d'objet?

Mon intention est d'implémenter la prise en charge de toutes les commandes intégrées, mais en plus de celles-ci, il y a une tonne d'extensions.

J'ai donc besoin de rendre ma bibliothèque extensible dans le sens où les autres et moi pouvons ajouter du support pour les extensions. Je prévois d'ajouter le support pour certaines des extensions les plus populaires et les plus populaires (à tout le moins quelques-unes de celles qui sont livrées avec Mercurial), mais je veux toujours pouvoir l'étendre de l'extérieur.

Actuellement, la syntaxe d'une commande ressembler à ceci:

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

cependant, ne se prête pas facilement à des extensions, ce, sans que le programmeur sentiment que l'extension est juste sur clouée partie.

Par exemple, supposons que j'expose une collection publique d'arguments de ligne de commande supplémentaires, que je pouvais faire manuellement ceci:

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

Il semble y avoir aucun moyen facile pour moi d'obtenir cette extension supplémentaire ajouté de telle sorte qu'il peut être défini comme faisant partie de l'initialiseur d'objet.

Je pensais à ajouter, ou peut-être à passer, à une syntaxe d'interface fluide. Dans ce cas, vous pourriez écrire quelque chose comme ceci:

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

Cependant, je vois des gens qui n'aiment pas les interfaces couramment, ils sentent qu'ils deviennent trop bavard.

Quelles autres options ai-je?

Ce que je veux, au fond:

  • Un style de syntaxe commune
    • Pour les deux intégrés dans les choses
    • ainsi que les extensions ajoutées par les utilisateurs de ma bibliothèque

Je prévois que les utilisateurs de ma bibliothèque l'étendraient en ajoutant de nouvelles classes, et des méthodes d'extension pour ge t le support intellisense, mais les méthodes d'extension ne peuvent pas être utilisées dans les initialiseurs d'objet, ce qui signifie que toutes les extensions ont l'air de quelque chose après coup. Ce n'est pas ce que je veux.

Toutes les idées sont les bienvenues.

Répondre

1

Je ne suis pas familier avec Mercurial et votre question semble trop générale pour répondre spécifiquement, mais je peux répondre à un commentaire particulier.

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

Si CommitCommand.Arguments IList<T> vous avez déjà la possibilité d'utiliser la syntaxe initialiseur:

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

Oui, mais désolé, mon erreur, je simplifiée . J'ai l'intention d'avoir des méthodes normales pour que le programmeur qui utilise la bibliothèque + les extensions n'ait pas besoin de connaître les arguments réels. –

+0

Semble un bon usage pour la dynamique. – Tergiver

1

Moi aussi, je ne suis pas familier avec Mercurial, mais une option est de jeter IntelliSense et utiliser des types anonymes:

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

Vous ne peut pas utiliser les traits d'union dans les noms de propriétés, vous devez donc remplacer PascalCase par trait-hyphen-lower-case. Vous pouvez également remplacer les traits de soulignement par des hypers.

Je ne suis pas sûr que je voudrais recommander cette approche sans en savoir plus sur la fréquence à laquelle les gens vont utiliser ces extensions. Cette approche fonctionne bien dans le framework ASP.NET MVC lorsqu'il s'agit d'attributs HTML, mais ce scénario est différent car le framework n'a pas besoin de gérer les valeurs, il les écrit directement dans la sortie. Si vous effectuez des actions conditionnelles basées sur les valeurs fournies de cette manière ou si vous souhaitez que votre API soit plus facilement détectable par ceux qui ne connaissent pas la syntaxe de la ligne de commande de Mercurial, vous pouvez essayer une autre approche.

0

Comme pour moi l'interface fluide est ok. Mais si vous voulez éviter, alors j'utiliser probablement smth comme ceci:

interface IExtension { ... } 

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

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

Il permettra d'utiliser les commandes de cette façon:

new CommitCommand 
{ 
    Message = "", 
    AddRemove = true, 
    Extensions = {new SomeExtension1(), new SomeExtension2()} 
}; 
Questions connexes