web-dev-qa-db-fra.com

Deux définitions contradictoires du principe de ségrégation d'interface - lequel est correct?

Lors de la lecture d'articles sur le fournisseur de services Internet, il semble y avoir deux définitions contradictoires de la FAI:

Selon la première définition (voir 1 , 2 , -), ISP indique que la mise en œuvre de l'interface ne doit pas être forcée de mettre en œuvre des fonctionnalités qui Ils n'ont pas besoin. Ainsi, l'interface graisse IFat

interface IFat
{
     void A();
     void B();
     void C();
     void D();
}

class MyClass: IFat
{ ... }

devrait être divisé en interfaces plus petites ISmall_1 et ISmall_2

interface ISmall_1
{
     void A();
     void B();
}

interface ISmall_2
{
     void C();
     void D();
}

class MyClass:ISmall_2
{ ... }

depuis cette façon, mon MyClass est capable de ne mettre en œuvre que les méthodes nécessaires (D() et C()), sans être forcée de fournir également des implémentations factices pour A(), B() et C():

Mais selon la deuxième définition (voir - 1 , 2 , Réponse de Nazar Merza ), ISP stipule que les méthodes d'appel MyClient sur MyService ne devrait pas être au courant des méthodes sur MyService que cela n'a pas besoin. En d'autres termes, si MyClient n'a besoin que de la fonctionnalité de C() et D(), puis au lieu de

class MyService 
{
    public void A();
    public void B();
    public void C();
    public void D();
}

/*client code*/      
MyService service = ...;
service.C(); 
service.D();

nous devrions séparer les méthodes MyService's en spécifique au client Interfaces:

public interface ISmall_1
{
     void A();
     void B();
}

public interface ISmall_2
{
     void C();
     void D();
}

class MyService:ISmall_1, ISmall_2 
{ ... }

/*client code*/
ISmall_2 service = ...;
service.C(); 
service.D();

Ainsi, avec la définition antérieure, l'objectif de la FAI est de " Faire la vie des classes implémentant une interface IFAT plus facile", alors que le but de l'ISP est " faire La vie des clients appelle des méthodes de myservice plus facile ".

Laquelle des deux définitions différentes de l'ISP est réellement correcte?

@ Marjan Venema

1.

Ainsi, lorsque vous allez diviser l'IFAT en une interface plus petite, quelles méthodes se retrouvent dans lesquelles IsmallInterface doit être décidée en fonction de la cohésive des membres.

Bien qu'il soit logique de mettre des méthodes cohérentes dans la même interface, j'ai pensé avec le motif de la FAI les besoins du client prenant la priorité sur la "cohésivité" d'une interface. En d'autres termes, j'ai pensé avec la FAI que nous devrions mener dans la même interface ces méthodes nécessaires à des clients particuliers, même si cela signifie que cela signifie de sortir de cette interface ces méthodes qui doivent également être placées à l'intérieur de cette même interface?

Ainsi, s'il y avait beaucoup de clients qui n'auront jamais besoin d'appeler que CutGreens, mais pas aussi GrillMeat, puis à adhérer au motif de la FIAP, nous ne devons que mettre CutGreens à l'intérieur ICook, mais pas aussi GrillMeat, même si les deux méthodes sont hautement cohérentes ?!

2.

Je pense que votre confusion découle de l'hypothèse cachée dans la première définition: les classes de mise en œuvre suivent déjà le principe de responsabilité unique.

Par "Mise en œuvre de classes non suivant SRP", faites-vous référence à ces classes qui implémentent IFat ou aux classes qui implémentent ISmall_1/ISmall_2? Je suppose que vous parlez de classes qui implémentent IFat? Si oui, pourquoi supposez-vous qu'ils ne suivent pas déjà SRP?

merci

14
EdvRusj

Les deux sont corrects

La façon dont je le lise, le but du principe de ségrégation des ISP (principe d'interface) est de conserver des interfaces petites et ciblées: tous les membres de l'interface devraient avoir une cohésion très élevée. Les deux définitions sont destinées à éviter les interfaces "Jack-of-All-Trades-Master-of-None".

Séparation de l'interface et SRP (principe de responsabilité unique) ont le même objectif: assurer des composants logiciels très cohérents hautement cohérents. Ils se complètent. La ségrégation d'interface garantit que les interfaces sont petites, ciblées et hautement cohérentes. Suite au principe de responsabilité unique garantit que les classes sont petites, ciblées et hautement cohérentes.

La première définition que vous mentionnez se concentre sur les implémentations, la seconde sur les clients. Ce qui, contrairement à @ user61852, je prends pour être les utilisateurs/appelants de l'interface, pas les implémentaux.

Je pense que votre confusion découle de l'hypothèse cachée dans la première définition: les classes de mise en œuvre suivent déjà le principe de responsabilité unique.

Pour moi, la deuxième définition, avec les clients que les appelants de l'interface, constitue un meilleur moyen d'arriver à l'objectif prévu.

Séparer

Dans votre question que vous indiquez:

depuis cette façon, mon myClass est capable de ne mettre en œuvre que les méthodes dont il a besoin (D() et C()), sans être obligé de fournir des implémentations factices pour a (), B() et c ():

Mais cela transformera le monde à l'envers.

  • Une classe mettant en œuvre une interface ne dicte pas ce dont il a besoin dans l'interface qu'il met en œuvre.
  • Les interfaces dictent quelles méthodes une classe d'exécution devrait fournir.
  • Les appelants d'une interface sont vraiment ceux qui dictent les fonctionnalités dont ils ont besoin l'interface pour les fournir et donc ce qu'un agent de mise en œuvre devrait fournir.

Ainsi, lorsque vous allez diviser IFat dans une interface plus petite, quelles méthodes se retrouvent dans lesquelles ISmall interface doit être décidée en fonction de la cohésive des membres.

Considérez cette interface:

interface IEverythingButTheKitchenSink
{
     void DoDishes();
     void CleanSink();
     void CutGreens();
     void GrillMeat();
}

Quelles méthodes mettriez-vous dans ICook et pourquoi? Souhaitez-vous mettre CleanSink avec GrillMeat Juste parce que vous avez une classe qui fait exactement cela et quelques autres choses, mais rien comme les autres méthodes? Ou le feriez-vous en deux interfaces plus cohérentes, telles que:

interface IClean
{
     void DoDishes();
     void CleanSink();
}

interface ICook
{
     void CutGreens();
     void GrillMeat();
}

Note de déclaration d'interface

Une définition d'interface doit de préférence être par elle-même dans une unité distincte, mais si elle doit absolument vivre avec l'appelant ou la mise en œuvre, elle devrait vraiment être avec l'appelant. Sinon, l'appelant reçoit une dépendance immédiate sur la mise en œuvre qui vaincre l'objectif des interfaces. Voir aussi: Déclarant l'interface dans le même fichier que la classe de base, est-ce une bonne pratique? sur les programmeurs et pourquoi devrions-nous placer des interfaces avec des classes qui les utilisent plutôt que celles qui les appliquent ? sur Stackoverflow.

6
Marjan Venema

Vous confondez le mot "client" comme utilisé dans le gang de quatre documents avec un "client" comme dans le consommateur d'un service.

Un "client", comme prévu par le gang de quatre définitions, est une classe qui implémente une interface. Si la classe A implémente l'interface B, ensuite, ils disent que A est un client de B. Sinon, les clients de la phrase "ne doivent pas être forcés d'implémenter des interfaces qu'ils n'utilisent pas" N'aurait pas senti logiciel puisque "les clients" (comme dans les consommateurs) ne montent rien. La phrase n'a de sens que lorsque vous voyez "client" comme "implémentatrice".

Si "Client" signifiait une classe qui "consomme" (appels) les méthodes d'une autre classe qui implémente la grande interface, alors en appelant les deux méthodes que vous aimez et en ignorant le reste, suffirait à vous garder découplé du reste de la suite les méthodes que vous n'utilisez pas.

L'esprit du principe évite le "client" (la classe implémentant l'interface) ayant à mettre en œuvre des méthodes factices afin de se conformer à l'ensemble de l'interface lorsqu'il ne se soucie que d'un ensemble de méthodes liées.

Il vise également à avoir moins de couplage que possible afin que les modifications apportées à un endroit entraînent moins d'impact. En séparant les interfaces, vous réduisez le couplage.

Ces problèmes apparaissent lorsque l'interface fait trop et que des méthodes doivent être divisées en plusieurs interfaces au lieu d'une seule.

Les deux exemples de code sont ok. C'est seulement que dans la seconde que vous assumez "client" signifie "une classe qui consomme/appelle les services/méthodes offertes par une autre classe".

Je ne trouve aucune contradiction dans les concepts expliqués dans les trois liens que vous avez donnés.

Gardez simplement clair que "Client" est implémentuateur, In SOLID Talk.

14
Tulains Córdova

L'ISP consiste à isoler le client d'en savoir plus sur le service qu'il n'a besoin de savoir (la protéger contre des modifications non liées, par exemple). Votre deuxième définition est correcte. À ma lecture, un seul de ces trois articles suggère autrement ( le premier ) et c'est tout simplement faux. (Edit: Non, pas mal, juste trompeur.)

La première définition est beaucoup plus étroitement liée au LSP.

5
pdr