2010-07-15 14 views
9

Ich muss Überladungen für Funktionen an einer vorhandenen Schnittstelle erstellen, ohne die Komponenten zu beeinflussen, die die Schnittstelle derzeit implementieren oder verwenden (im Idealfall).Hinzufügen neuer Funktionen zu einer Schnittstelle

Ich denke, ich habe ein paar Optionen:

Simplified Original-Schnittstelle:

public interface IServerComponent 
{ 
    bool Add(int a, int b); 
} 

kann ich die neuen überladenen Funktionen zur Schnittstelle hinzufügen und jede Klasse erzwingen, die die Schnittstelle implementiert zu implementieren Sie die neuen Funktionen.

public interface IServerComponent 
{ 
    bool Add(int a, int b); 
    bool Add(int a, int b, int c); 
} 

Oder ich kann eine neue Schnittstelle erstellen, die die ursprüngliche Schnittstelle implementiert. Dann werden auch andere Klassen, die von der ursprünglichen machen müssen nicht ändern und keine neuen Klassen können die neue Schnittstelle implementieren ...

public interface IServerComponent2 : IServerComponent 
{ 
    bool Add(int a, int b, int c); 
} 

Was ist die beste Praxis ist diese Situation? Gibt es andere Optionen?

Dank

Antwort

11

Wenn die neuen Methoden können in Bezug auf die alten Methoden zum Ausdruck gebracht werden, Sie Erweiterungsmethoden verwenden:

// Original interface 
public interface IServerComponent 
{ 
    bool Add(int a, int b, int c); 
} 

// New overload 
public static class MyServerMethods 
{ 
    public static bool Add(this IServerComponent component, int a, int b) 
    { 
    return component.Add(a, b, 0); 
    } 
} 

Wenn die Methoden nicht so ausgedrückt werden kann (dh sie müssen wirklich von den Komponenten selbst implementiert werden), dann würde ich empfehlen, eine neue Schnittstelle zu definieren. Dieser Ansatz ist maximal abwärtskompatibel.

+0

+1 für die Erwähnung Erweiterungsmethoden – ram

+1

Erweiterungsmethoden funktionieren wird, aber haben auch die Wirkung, dass die Funktionalität der implementierenden Klasse in anderen Klassen Verbreitung reduziert Kohäsionskraft. Ich sehe das bestenfalls als vorübergehende Lösung. Wenn Sie die Klasse nicht erweitern können, weil Sie keinen Zugriff auf die Quelle haben, sind Erweiterungsmethoden sinnvoll. Ich könnte es auf diese Weise für kleinere Aktualisierungen einer bestehenden Codebasis tun, aber ich würde es sicherlich auf meine Refactoring-Liste für die nächste Hauptversion setzen. – tvanfosson

+2

Das hängt ganz vom Design ab. Wenn Sie * neue Funktionalität * hinzufügen, würde ich zustimmen; Es gehört zu einer Schnittstelle. Wenn Sie * Überladungen * hinzufügen (z. B. fast identische Funktionalität, die immer mit anderen Funktionen ausgedrückt werden kann), stimme ich nicht zu. In diesem Fall erlauben Erweiterungsmethoden * Orthogonalität *, wodurch die Codeverdopplung reduziert wird. [Joe Duffy] (http://www.bluebytesoftware.com/blog/2010/02/10/ExtensionMethodsAsDefaultInterfaceMethodImplementations.aspx) hat einen guten Blogbeitrag zu diesem Thema. –

4

Wenn Ihre IServerComponent-Schnittstelle noch nicht ausgeliefert wurde oder eine interne Schnittstelle ist, die nur von Ihren eigenen Klassen implementiert wird und die neuen Schnittstellenelemente für alle vorhandenen Klassen, die die Schnittstelle implementieren, sinnvoll sind, ändern Sie die vorhandene Schnittstelle.

Erstellen Sie andernfalls eine IServerComponent2-Schnittstelle, die IServerComponent erweitert. IIRC Dies ist, was die Framework Design Guidelines empfehlen. Ein Beispiel hierfür findet sich im .NET Framework in Form der Klasse X509Certificate2.

Wenn jedoch die neuen Mitglieder in Bezug auf die ursprünglichen Mitglieder umgesetzt werden können, können Sie auch extension methods verwenden:

public interface IServerComponent 
{ 
    bool Add(int a, int b); 
} 

public static class ServerComponentExtensions 
{ 
    public static bool Add(this IServerComponent isc, int a, int b, int c) 
    { 
     return isc.Add(a, b) && isc.Add(b, c) && isc.Add(c, a); 
    } 
} 
0

Es hängt vom Kontext ab - wenn es viele Klassen, die die ursprüngliche Schnittstelle implementieren, oder wenn es veröffentlicht wird, dann ist der einzige praktische Weg, den neuen einzuführen. Andererseits ist es auf lange Sicht viel sauberer, wenn man nur eine Schnittstelle hat, also würde ich sagen, dass man die vorhandene refaktorieren soll, wenn man es sich leisten kann.

0

Wenn Sie die Klassen (jetzt oder in der Zukunft), die die alte Schnittstelle bereits implementieren, nicht ändern müssen oder wollen, sollten Sie nur eine zweite Schnittstelle erstellen. Es fühlt sich jedoch eher wie ein Hack an und könnte Ihnen weniger intuitiven Code geben.

Plus, ein Refactoring zu verschieben ist nie eine gute Idee in meiner Erfahrung. Also, wenn Sie vermuten, dass Sie die Schnittstelle später implementieren müssen, können Sie auch nur die vorhandene ändern und sich ein wenig Kopfschmerzen ersparen. Es ist definitiv der sauberere Weg, es zu tun.

0

Ich denke, eine einzige Schnittstelle Aufrechterhaltung ist einfacher, aber der zweite Ansatz mit 2-Schnittstelle ist besser, weil nicht jede Klasse bool Add(int a, int b, int c);

1

implementieren muss ich erkennen, Ihr Beispiel gekünstelt ist, aber ich möchte Ihnen eine erfundene Antwort geben, ist anders als das, was du ausdrückst, damit du vielleicht anders darüber nachdenken kannst. Anstatt Ihre Schnittstellenmethode auf etwas Konkretes anzuwenden, könnte es sinnvoll sein, stattdessen etwas Abstraktes zu bearbeiten.

Zum Beispiel

public interface IAddableThings 
{ 
    int a; 
    int b; 
} 

public interface IOtherAddableThings : IAddableThings 
{ 
    int a; 
    int b; 
} 

public interface IServerComponent 
{ 
    bool Add(IAddableThings things); 
} 

Wenn dies wirklich ein Add ist dann denke ich, das viel mehr Sinn macht, aber wenn es wirklich mehr wie berechnen() ist, würden Sie dann wollen einige oder alle bewegen diese Methoden-Operation bis zu den IAddableThings-Objekten.

public interface IAddableThings 
{ 
    int a; 
    int b; 
    bool CalculateMe(); 
} 
Verwandte Themen