Qu'est-ce que « programme d'interfaces, et non mises en œuvre » signifie?
-
01-10-2019 - |
Question
Une tombe sur cette phrase lors de la lecture sur les modèles de conception.
Mais je ne comprends pas, quelqu'un pourrait-il expliquer cela pour moi?
La solution
Interfaces sont des contrats ou signatures seulement et ils ne savent pas rien implémentations.
Codage contre des moyens d'interface, le code client tient toujours un objet d'interface qui est fournie par une usine. Toute instance retournée par l'usine serait de type d'interface qui doit avoir mis en œuvre une classe candidat usine. De cette façon, le programme client ne craint pas la mise en œuvre et la signature d'interface détermine ce qui peut être fait toutes les opérations. Cela peut être utilisé pour modifier le comportement d'un programme à l'exécution. Il vous permet également d'écrire des programmes beaucoup mieux du point de vue de l'entretien.
Voici un exemple de base pour vous.
public enum Language
{
English, German, Spanish
}
public class SpeakerFactory
{
public static ISpeaker CreateSpeaker(Language language)
{
switch (language)
{
case Language.English:
return new EnglishSpeaker();
case Language.German:
return new GermanSpeaker();
case Language.Spanish:
return new SpanishSpeaker();
default:
throw new ApplicationException("No speaker can speak such language");
}
}
}
[STAThread]
static void Main()
{
//This is your client code.
ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English);
speaker.Speak();
Console.ReadLine();
}
public interface ISpeaker
{
void Speak();
}
public class EnglishSpeaker : ISpeaker
{
public EnglishSpeaker() { }
#region ISpeaker Members
public void Speak()
{
Console.WriteLine("I speak English.");
}
#endregion
}
public class GermanSpeaker : ISpeaker
{
public GermanSpeaker() { }
#region ISpeaker Members
public void Speak()
{
Console.WriteLine("I speak German.");
}
#endregion
}
public class SpanishSpeaker : ISpeaker
{
public SpanishSpeaker() { }
#region ISpeaker Members
public void Speak()
{
Console.WriteLine("I speak Spanish.");
}
#endregion
}
Ceci est juste un exemple de base et explication réelle du principe est au-delà de la portée de cette réponse.
EDIT
J'ai mis à jour l'exemple ci-dessus et a ajouté une classe de base abstraite Président. Dans cette mise à jour, j'ai ajouté une fonctionnalité à tous Spakers à « SayHello ». Tous parlent haut-parleur « Bonjour tout le monde ». Donc, c'est une caractéristique commune avec fonction similaire. Référez-vous au diagramme de classes et vous trouverez que le président classe abstraite et implémenter l'interface iSpeaker marque le Speak () comme abstraites qui signifie que la mise en œuvre de chaque Président est responsable de la mise en œuvre de la méthode Speak car elle varie d'une enceinte à. Mais tout orateur dire « Bonjour » à l'unanimité. Ainsi, dans la classe abstraite Président, nous définissons une méthode qui dit: « Bonjour tout le monde » et chaque implémentation Président dérivera la méthode SayHello.
Prenons un cas où SpanishSpeaker ne peut pas dire Bonjour donc dans ce cas, vous pouvez remplacer la méthode SayHello pour Président espagnol et raise exception appropriée.
S'il vous plaît noter que, nous avons apporté aucune modification à l'interface ISpeaker. Et le code client et SpeakerFactory sont aussi maintenues inchangé. Et voici ce que nous obtenons par Programmation à l'interface .
Et nous pourrions obtenir ce comportement en ajoutant simplement une classe abstraite de base et une modification Président mineur dans chaque mise en œuvre laissant ainsi le programme original inchangé. Ceci est une caractéristique souhaitée de toute application et rend votre application facilement maintenable.
public enum Language
{
English, German, Spanish
}
public class SpeakerFactory
{
public static ISpeaker CreateSpeaker(Language language)
{
switch (language)
{
case Language.English:
return new EnglishSpeaker();
case Language.German:
return new GermanSpeaker();
case Language.Spanish:
return new SpanishSpeaker();
default:
throw new ApplicationException("No speaker can speak such language");
}
}
}
class Program
{
[STAThread]
static void Main()
{
//This is your client code.
ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English);
speaker.Speak();
Console.ReadLine();
}
}
public interface ISpeaker
{
void Speak();
}
public abstract class Speaker : ISpeaker
{
#region ISpeaker Members
public abstract void Speak();
public virtual void SayHello()
{
Console.WriteLine("Hello world.");
}
#endregion
}
public class EnglishSpeaker : Speaker
{
public EnglishSpeaker() { }
#region ISpeaker Members
public override void Speak()
{
this.SayHello();
Console.WriteLine("I speak English.");
}
#endregion
}
public class GermanSpeaker : Speaker
{
public GermanSpeaker() { }
#region ISpeaker Members
public override void Speak()
{
Console.WriteLine("I speak German.");
this.SayHello();
}
#endregion
}
public class SpanishSpeaker : Speaker
{
public SpanishSpeaker() { }
#region ISpeaker Members
public override void Speak()
{
Console.WriteLine("I speak Spanish.");
}
public override void SayHello()
{
throw new ApplicationException("I cannot say Hello World.");
}
#endregion
}
Autres conseils
Pensez à une interface comme un contrat entre un objet et ses clients. C'est l'interface spécifie les choses qu'un objet peut faire, et les signatures d'accès à ces choses.
Implémentations sont les comportements réels. Disons, par exemple, vous avez une sorte de méthode (). Vous pouvez mettre en œuvre QuickSort ou mergesort. Cela ne devrait pas question au code client appelant sorte aussi longtemps que l'interface ne change pas.
Les bibliothèques comme l'API Java et .NET Framework utilisent énormément d'interfaces parce que des millions de programmeurs utilisent les objets fournis. Les créateurs de ces bibliothèques doivent être très prudents qu'ils ne changent pas l'interface aux classes dans ces bibliothèques, car cela affectera tous les programmeurs utilisant la bibliothèque. D'autre part, ils peuvent changer la mise en œuvre autant qu'ils aiment.
Si, en tant que programmeur, vous codez contre la mise en œuvre alors dès qu'il change votre code cesse de fonctionner. Alors, pensez aux avantages de l'interface ainsi:
- il cache les choses que vous n'avez pas besoin de savoir faire l'objet plus simple à utiliser.
- il fournit le contrat de la façon dont l'objet se comportera de sorte que vous pouvez compter sur cela
Cela signifie que vous devriez essayer d'écrire votre code afin qu'il utilise une abstraction (classe abstraite ou interface) au lieu de la mise en œuvre directement.
Normalement, la mise en œuvre est injecté dans votre code par le constructeur ou un appel de méthode. Donc, votre code connaît l'interface ou classe abstraite et peut appeler tout ce qui est défini sur ce contrat. Comme un objet réel (mise en œuvre de l'interface / classe abstraite) est utilisée, les appels fonctionnent sur l'objet.
Ceci est un sous-ensemble de la Liskov Substitution Principle
(LSP), le L de la les principes de SOLID
.
Un exemple dans .NET serait de code avec IList
au lieu de List
ou Dictionary
, vous pouvez utiliser une classe qui implémente IList
interchangeable dans votre code:
// myList can be _any_ object that implements IList
public int GetListCount(IList myList)
{
// Do anything that IList supports
return myList.Count();
}
Cette instruction est sur le couplage. L'une des raisons possibilité d'utiliser la programmation orientée objet est la réutilisation. Ainsi, par exemple, vous pouvez diviser votre algorithme entre les deux objets A et B. collaborateur Cela peut être utile pour la création ultérieure d'un autre algorithme, ce qui pourrait réutiliser une ou l'autre des deux objets. Cependant, lorsque ces objets communiquent (envoyer des messages - méthodes d'appel), ils créent des dépendances entre eux. Mais si vous voulez utiliser un sans l'autre, vous devez spécifier ce qui devrait faire un autre objet C faire pour objet A si l'on remplace B. Ces descriptions sont appelées interfaces. Cela permet à l'objet A de communiquer sans changement avec différents objets se fondant sur l'interface. La déclaration que vous avez mentionné dit que si vous avez l'intention de réutiliser une partie d'un algorithme (ou plus généralement un programme), vous devez créer des interfaces et compter sur eux, de sorte que vous pouvez changer la mise en œuvre concrète tout moment sans changer d'autres objets si vous utilisez la l'interface déclarée.
Comme d'autres l'ont dit, cela signifie que votre code d'appel ne devrait connaître un parent abstrait, pas la classe mise en œuvre réelle qui fera le travail.
Ce qui aide à comprendre c'est le pourquoi vous devriez toujours programme à une interface. Il y a de nombreuses raisons, mais deux des plus faciles à expliquer sont
1) Test.
Le mot Let je mon code toute la base de données dans une seule classe. Si mon programme connaît la classe concrète, je ne peux tester mon code en cours d'exécution vraiment contre cette classe. J'utilise -.> Signifie « parle à »
WorkerClass -> DALClass Cependant, nous allons ajouter une interface au mélange.
WorkerClass -> IDAL -.> DALClass
Ainsi, les DALClass implémente l'interface IDAL, et la classe des travailleurs appelle seulement à travers cela.
Maintenant, si nous voulons des tests d'écriture pour le code, nous pourrions faire à la place une classe simple qui agit comme simplement une base de données.
WorkerClass -> IDAL -.> IFakeDAL
2) Réutilisation
Suivant l'exemple ci-dessus, disons que nous voulons passer de SQL Server (qui nos DALClass en béton usages) à MonogoDB. Cela prendrait des travaux importants, mais pas si nous avons programmé une interface. Dans ce cas, nous écrivons simplement la nouvelle classe de DB, et le changement (par l'usine)
WorkerClass -> IDAL -> DALClass
à
WorkerClass -> IDAL -> MongoDBClass
interfaces décrivent les capacités. lors de l'écriture du code impératif, parler des capacités que vous utilisez, plutôt que des types ou des classes spécifiques.