solid segregation principle principios principio open liskov inversión example ejemplo diseño devexperto dependencias dependencia close oop design-patterns solid-principles isp-principle

oop - segregation - principios de diseño principio de inversion de dependencia



Principio de Segregación de Interfaz-Programa a una interfaz (6)

Estaba leyendo sobre SOLID y otros principios de diseño. Pensé que ISP era lo mismo que "Programa para una interfaz, no una implementación". Pero parece que estos son principios diferentes?

¿Hay una diferencia?


  1. Interfaz IWorker:

    public interface IWorker { public void work(); public void eat(); }

  2. Clase de desarrollador:

    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"); } }

  3. Clase 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"); } }

Para un ejemplo más completo vaya here .


Aquí hay un ejemplo del mundo real de este principio (en PHP)

Planteamiento del problema:

Quiero que varias formas de contenido tengan comentarios / discusiones asociadas con ellos. Ese contenido puede ser cualquier cosa, desde un tema del foro, un artículo de noticias, el perfil de un usuario o un mensaje privado al estilo de conversación.

Arquitectura

Queremos una clase DiscussionManager reutilizable que adjunte una Discussion a una entidad de contenido determinada. Sin embargo, los cuatro ejemplos anteriores (y muchos más) son todos conceptualmente diferentes. Si queremos que el DiscussionManager use, entonces los cuatro + necesitan tener una interfaz común que todos compartan. No hay otra forma en que DiscussionManager use a menos que desee que sus argumentos queden desnudos (por ejemplo, sin verificación de tipos).

Solución: Interfaz Discussable con estos métodos:

  • attachDiscussion($topic_id)
  • detachDiscussion()
  • getDiscussionID()

Entonces DiscussionManager podría verse así:

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 esta manera, DiscussionManager no se preocupa por ninguno de los comportamientos no relacionados de los diversos tipos de contenido que utiliza. SOLO se preocupa por los comportamientos que necesita, independientemente de con qué estén asociados esos comportamientos. Entonces, al proporcionar a cada tipo de contenido para el que desea tener discusiones, una interfaz de discusión, está utilizando el principio de segregación de la interfaz.

Este es también un buen ejemplo de una situación en la que una clase base abstracta no es una buena idea. Un tema del foro, un perfil de usuario y un artículo de noticias no son ni siquiera remotamente conceptuales lo mismo, por lo que intentar que hereden los comportamientos de discusión conduce a un extraño acoplamiento con un padre no relacionado. Al usar una interfaz específica que representa las discusiones, puede asegurarse de que las entidades que desea tener sean compatibles con el código del cliente que administrará esas discusiones.

Este ejemplo también podría ser un buen candidato para el uso de Rasgos en PHP, por lo que vale.


De acuerdo con las dos respuestas anteriores. Solo para dar un ejemplo del código de TrueWill que se menciona arriba, no deberías encontrarte haciendo esto:

@Override public void foo() { //Not used: just needed to implement interface }


El ISP se centra en la idea de que cada interfaz representa un comportamiento discreto y cohesivo.

Es decir, cada grupo lógico de cosas que debería hacer un objeto se asignaría a una única interfaz específica. Una clase podría querer hacer varias cosas, pero cada cosa se asignaría a una interfaz específica que representa ese comportamiento. La idea es que cada interfaz está muy enfocada.


Robert Martin tiene una muy buena explicación del principio de segregación de interfaz (ISP), en su libro "UML para programadores de Java". Basándome en eso, no creo que ISP se trate de una interfaz que esté "enfocada" en un grupo lógico y coherente de cosas. Porque, eso no hace falta decirlo; O, al menos, debería ir sin decirlo. Cada clase, interfaz o clase abstracta debe ser diseñada de esa manera.

Entonces, ¿qué es ISP? Déjame explicarlo con un ejemplo. Supongamos que tiene una clase A y una clase B, que es el cliente de la clase A. Supongamos que la clase A tiene diez métodos, de los cuales B. solo usa dos. Ahora, B necesita saber acerca de los diez métodos de A ? Probablemente no - el principio de la información oculta. Cuanto más exponga, más creará la posibilidad de acoplamiento. Por ese motivo, puede insertar una interfaz, llámela C, entre las dos clases (segregación). Esa interfaz solo declarará los dos métodos que usa B, y B dependerá de esa interfaz, en lugar de hacerlo directamente de A.

Y ahora,

class A { method1() method2() // more methods method10() } class B { A a = new A() }

se convertirá

interface C { method1() method2() } class A implements C{ method1() method2() // more methods method10() } class B { C c = new A() }

Esto, evita que B sepa más de lo que debería.


Supongamos que tiene una interfaz grande con muchos métodos para implementar.

Cualquier clase, que implemente esa interfaz fat, tiene que proporcionar implementación para todos estos métodos. Algunos de los métodos pueden no ser aplicables a esa clase concreta. Pero todavía tiene que proporcionar implementación en ausencia del principio de segregación de interfaz.

Veamos el código de ejemplo en ausencia de la segregación de interfaz .

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()); } }

salida:

java InterfaceNoSeggration Rectangle area:200.0 Square area:225.0 Circle area:12.56

Notas:

  1. Shape es una interfaz fat de propósito general, que contiene métodos requeridos para todas las implementaciones de Shape como Rectangle , Circle y Square . Pero solo se necesitan algunos métodos en los respectivos niños de Shape.

    Rectangle : getLength(), getWidth(), getArea() Square : getLength() and getArea() Circle : getRadius() and getArea()

  2. En ausencia de segregación, todas las Formas han implementado una interfaz completa de grasa: Forma.

Podemos lograr la misma salida con el principio de segregación de interfaz si cambiamos el código de la siguiente manera.

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()); } }

Notas:

Ahora, las formas individuales como Rectangle , Square y Circle han implementado solo las interfaces requeridas y se han deshecho de los métodos no utilizados.