Principe de ségrégation de l'Interface-programme vers une interface
je lisais à propos de SOLID et d'autres principes de conception. Je pensais que ISP était la même chose que "programme à une interface, pas une implémentation". Mais il semble que ce sont des principes différents?
y a-t-il une différence?
6 réponses
ISP est axé sur l'idée de chaque interface représentant un comportement discret et cohérent.
C'est-à-dire que chaque groupe logique de choses qu'un objet devrait faire correspondrait à une interface spécifique unique. Une classe peut vouloir faire plusieurs choses, mais chaque chose correspond à une interface spécifique représentant ce comportement. L'idée est que chaque interface est très concentré.
Robert Martin a une très bonne explication du principe de ségrégation des interfaces (ISP), dans son livre "UML for Java Programmers". Basé sur cela, je ne pense pas ISP est au sujet d'une interface étant "concentré" sur un groupe logique et cohérent de choses. Parce que, cela va de soi; ou, du moins, cela devrait aller de soi. Chaque classe, interface ou classe abstraite devrait être conçue de cette façon.
alors, QU'est-ce que ISP? Permettez-moi de l'expliquer par un exemple. Dites, vous avez une classe A et a Classe B, qui est le client de la classe A. supposons, Classe A a dix méthodes, dont seulement deux sont utilisées par B. Maintenant, est-ce que B a besoin de connaître les dix méthodes de A? Probablement pas-le principe de la dissimulation de L'Information. Plus vous exposez, plus vous créez la chance de l'accouplement. Pour cette raison, vous pouvez insérer une interface, C, entre les deux classes (ségrégation). Cette interface déclarera seulement les deux méthodes qui sont utilisées par B, et B dépendra de cette Interface, à la place directement sur A. 151930920"
donc maintenant,
class A {
method1()
method2()
// more methods
method10()
}
class B {
A a = new A()
}
deviendra
interface C {
method1()
method2()
}
class A implements C{
method1()
method2()
// more methods
method10()
}
class B {
C c = new A()
}
ceci, empêche B de savoir plus qu'il ne devrait.
supposer que vous avez une interface fat avec de nombreuses méthodes à mettre en œuvre.
toute classe, qui implémente cette interface fat doit fournir l'implémentation de toutes ces méthodes. Certaines des méthodes peuvent ne pas être applicables à cette classe de béton. Mais il doit tout de même fournir la mise en œuvre en l'absence de principe de ségrégation d'interface.
regardons le code d'exemple dans absence de ségrégation D'Interface .
interface Shape{
public int getLength();
public int getWidth();
public int getRadius();
public double getArea();
}
class Rectangle implements Shape{
int length;
int width;
public Rectangle(int length, int width){
this.length = length;
this.width = width;
}
public int getLength(){
return length;
}
public int getWidth(){
return width;
}
public int getRadius(){
// Not applicable
return 0;
}
public double getArea(){
return width * length;
}
}
class Square implements Shape{
int length;
public Square(int length){
this.length = length;
}
public int getLength(){
return length;
}
public int getWidth(){
// Not applicable
return 0;
}
public int getRadius(){
// Not applicable
return 0;
}
public double getArea(){
return length * length;
}
}
class Circle implements Shape{
int radius;
public Circle(int radius){
this.radius = radius;
}
public int getLength(){
// Not applicable
return 0;
}
public int getWidth(){
// Not applicable
return 0;
}
public int getRadius(){
return radius;
}
public double getArea(){
return 3.14* radius * radius;
}
}
public class InterfaceNoSeggration{
public static void main(String args[]){
Rectangle r = new Rectangle(10,20);
Square s = new Square(15);
Circle c = new Circle(2);
System.out.println("Rectangle area:"+r.getArea());
System.out.println("Square area:"+s.getArea());
System.out.println("Circle area:"+c.getArea());
}
}
sortie:
java InterfaceNoSeggration
Rectangle area:200.0
Square area:225.0
Circle area:12.56
Notes:
-
Shape
est une interface fat à usage général, qui contient des méthodes requises pour toutes les implémentationsShape
commeRectangle
,Circle
etSquare
. Mais seulement quelques méthodes sont nécessaires dans les childs de forme respectiveRectangle : getLength(), getWidth(), getArea() Square : getLength() and getArea() Circle : getRadius() and getArea()
-
absence de ségrégation, toutes les formes ont mis en œuvre toute l'interface fat : Shape.
nous pouvons obtenir la même sortie avec le principe de ségrégation de l'interface si nous changeons le code comme suit.
interface Length{
public int getLength();
}
interface Width{
public int getWidth();
}
interface Radius{
public int getRadius();
}
interface Area {
public double getArea();
}
class Rectangle implements Length,Width,Area{
int length;
int width;
public Rectangle(int length, int width){
this.length = length;
this.width = width;
}
public int getLength(){
return length;
}
public int getWidth(){
return width;
}
public int getRadius(){
// Not applicable
return 0;
}
public double getArea(){
return width * length;
}
}
class Square implements Length,Area{
int length;
public Square(int length){
this.length = length;
}
public int getLength(){
return length;
}
public int getWidth(){
// Not applicable
return 0;
}
public int getRadius(){
// Not applicable
return 0;
}
public double getArea(){
return length * length;
}
}
class Circle implements Radius,Area{
int radius;
public Circle(int radius){
this.radius = radius;
}
public int getLength(){
// Not applicable
return 0;
}
public int getWidth(){
// Not applicable
return 0;
}
public int getRadius(){
return radius;
}
public double getArea(){
return 3.14* radius * radius;
}
}
public class InterfaceSeggration{
public static void main(String args[]){
Rectangle r = new Rectangle(10,20);
Square s = new Square(15);
Circle c = new Circle(2);
System.out.println("Rectangle area:"+r.getArea());
System.out.println("Square area:"+s.getArea());
System.out.println("Circle area:"+c.getArea());
}
}
Notes:
maintenant les formes individuelles comme Rectangle
, Square
et Circle
ont mis en œuvre seulement les interfaces requises et se sont débarrassés des méthodes non utilisées.
d'Accord avec les deux réponses ci-dessus. Juste pour donner un exemple de TrueWill 's code smell ci-dessus, vous ne devriez pas vous trouver à faire cela:
@Override
public void foo() {
//Not used: just needed to implement interface
}
voici un exemple réel de ce principe (en PHP)
Énoncé Du Problème:
je veux que diverses formes de contenu soient assorties de commentaires/discussions. Ce contenu pourrait être n'importe quoi d'un sujet de forum, à un article de nouvelles, au profil d'un utilisateur, à un message privé de style de conversation.
Architecture
nous allons vouloir une classe DiscussionManager
réutilisable qui attache une Discussion
à une entité de contenu donnée. Cependant, les quatre exemples ci-dessus (et bien d'autres) sont conceptuellement différentes. Si nous voulons que la DiscussionManager
à les utiliser, puis tous les quatre ont besoin d'avoir une interface commune qu'ils partagent tous. Il n'y a pas d'autre moyen pour DiscussionManager
de les utiliser à moins que vous ne vouliez à vos arguments d'aller nu (par exemple pas de vérification de type).
Solution: Discussable
interface avec ces méthodes:
-
attachDiscussion($topic_id)
-
detachDiscussion()
-
getDiscussionID()
puis DiscussionManager
pourrait ressembler à ceci:
class DiscussionManager
{
public function addDiscussionToContent(Discussable $Content)
{
$Discussion = $this->DiscussionFactory->make( ...some data...);
$Discussion->save() // Or $this->DiscussionRepository->save($Discussion);
$Content->attachDiscussion($Discussion->getID()); // Maybe saves itself, or you can save through a repository
}
public function deleteDiscussion(Discussable $Content)
{
$id = $Content->getDiscussionID();
$Content->detatchDiscussion();
$this->DiscussionRepository->delete($id);
}
public function closeDiscussion($discussion_id) { ... }
}
de cette façon, DiscussionManager
ne se soucie pas de l'un des comportements indépendants des différents types de contenu qu'il utilise. Il ne se soucie que des comportements dont il a besoin, indépendamment de ce à quoi ces comportements sont associés. Donc, en donnant chaque type de contenu pour lequel vous voulez avoir des discussions, une interface Discussable
, vous utilisez le principe de ségrégation de l'interface.
C'est aussi un bon exemple d'une situation où une classe de base abstraite n'est pas une bonne idée. Un sujet de forum, un profil d'utilisateur, et un article de nouvelles ne sont même pas de loin conceptuellement la même chose, donc essayer de leur faire hériter les comportements de discussion conduit à un couplage étrange à un parent non lié. En utilisant une interface spécifique qui représente les discussions, Vous pouvez vous assurer que les entités que vous voulez avoir des discussions, sont compatibles avec le code client qui gérera ces discussions.
cet exemple pourrait aussi être un bon candidat pour l'utilisation de Traits en PHP, pour ce que cela vaut.
-
IWorker Interface:
public interface IWorker { public void work(); public void eat(); }
-
Développeur De La Classe :
public class Developer implements IWorker { @Override public void work() { // TODO Auto-generated method stub System.out.println("Developer working"); } @Override public void eat() { // TODO Auto-generated method stub System.out.println("developer eating"); } }
-
Classe De Robot:
public class Robot implements IWorker { @Override public void work() { // TODO Auto-generated method stub System.out.println("robot is working"); } @Override public void eat() { // TODO Auto-generated method stub throw new UnsupportedOperationException("cannot eat"); } }
pour un exemple plus complet, allez ici .