¿Casos de uso para interfaces "privadas"?

8

Me preguntaba si había un caso de uso válido para poder definir adecuadamente las propiedades y funciones internas específicas de una clase de manera similar a cómo una interfaz define las propiedades y funciones públicas de una clase.

Imagine la tarea que tiene que construir una clase que describe a un ser humano.

Obviamente, cada humano es una criatura humanoide, pero no todas las criaturas humanoides son humanas, por lo que probablemente tenga una interfaz IHumanoid con funciones como estas (ya que no es útil codificar el plan corporal en la clase):

public interface IHumanoid {
    function get head():IHead;
    function get torso():ITorso;
    function get leftArm():IArm;
    function get rightArm():IArm;
    function get leftLeg():ILeg;
    function get rightLeg():ILeg;
}

Además, y obviamente, cada humano es un mamífero, pero no todos los mamíferos son humanos, por lo que probablemente haya otra interfaz IMammal con dos definiciones para machos y hembras flotando en algún lugar:

public interface IMammal {
    function procreate(partner:IMammal):void;
}

public interface IMaleMammal extends IMammal {
    function inseminate(female:IFemaleMammal):void;
}

public interface IFemaleMammal extends IMammal {
    function conceive(partner:IMaleMammal):Boolean;
    function giveBirth():IMammal;
    function nurse(offspring:IMammal):void;
}

Por lo tanto, nuestra clase probablemente se parece a esto ahora:

public class Human implements IHumanoid, IMammal {
    private var _head:IHead;
    private var _torso:ITorso;
    private var _leftArm:IArm;
    private var _rightArm:IArm;
    private var _leftLeg:ILeg;
    private var _rightLeg:ILeg;

    public function Human() {
        // ctor...
    }

    public function get head():IHead {
        return _head;
    }

    public function get torso():ITorso {
        return _torso;
    }

    public function get leftArm():IArm {
        return _leftArm;
    }

    public function get rightArm():IArm {
        return _rightArm;
    }

    public function get leftLeg():ILeg {
        return _leftLeg;
    }

    public function get rightLeg():ILeg {
        return _rightLeg;
    }

    public function procreate(partner:IMammal):void {
        // "abstract" function
    }
}

public class MaleHuman extends Human implements IMaleMammal {
    override public function procreate(partner:IMammal):void {
        if (partner is IFemaleMammal) {
            inseminate(partner);
        }
    }

    public function inseminate(female:IFemaleMammal):void {
        female.conceive(this);
    }
}

public class FemaleHuman extends Human implements IFemaleMammal {
    override public function procreate(partner:IMammal):void {
        if (partner is IMaleMammal) {
            conceive(partner);
        }
    }

    public function conceive(partner:IMaleMammal):Boolean {
        // ...
    }

    public function giveBirth():IMammal {
        // ...
    }

    public function nurse(offspring:IMammal):void {
        // ...
    }
}

A partir de esto, podemos implementar nuestras clases aún más y todo funciona bien hasta que tengamos la tarea de usar las interfaces existentes para implementar algunas otras clases. Quizás un gorila, una orca y un ornitorrinco.

Ignorando el problema masivo que el ornitorrinco planteará a nuestra estructura de interfaz actual (* tos * mamífero pone huevos * tos *), tenemos el "problema" de que nada nos impide darle al gorila 2 cerebros, la orca 8 pulmones y el ornitorrinco medio una docena de hígados Y si bien podríamos ser lo suficientemente disciplinados para seguir la estructura que los mamíferos suelen tener, no podemos garantizar lo mismo si abrimos la API para otros desarrolladores que podrían codificar algunas cosas muy jodidas que aún se ven bien para el mundo exterior.

Por lo tanto, me preguntaba si había un caso de uso válido para crear algo así como una "interfaz privada" que define funciones y propiedades no públicas. Quizás algo en este sentido:

public structure SMammal {
    function get brain():IBrain;
    function get liver():ILiver;
    function get leftLung():ILung;
    function get rightLung():ILung;
    function get leftKidney():IKidney;
    function get rightKidney():IKidney;
}

public class Human implements IHumanoid, IMammal follows SMammal {
    private function get brain():IBrain {
        // ...
    }

    private function get liver():ILiver {
        // ...
    }

    // etc. etc.
}

¿Existe tal característica en algún lenguaje de programación? ¿Se pueden usar clases abstractas para resolver esto? ¿O no deberíamos preocuparnos por esto siempre que la interfaz pública funcione de alguna manera como se esperaba?


fuente
La forma "IMumle" de denotar una interfaz es un mal hábito, en mi opinión
Dos comentarios En primer lugar, parece que podemos basar toda la discusión en el esquema XML, en lugar de los detalles de las interfaces de lenguaje. En segundo lugar, la "validación" a veces (pero no siempre) se separa mejor de la "estructura".
rwong

Respuestas:

3

Y si bien podríamos ser lo suficientemente disciplinados para seguir la estructura que los mamíferos suelen tener, no podemos garantizar lo mismo si abrimos la API para otros desarrolladores que podrían codificar algunas cosas muy jodidas que aún se ven bien para el mundo exterior.

¿Qué ganas exactamente al obligarlos a seguir tu estructura? Posiblemente rompa algunos usos avanzados, tal vez haya un caso para un animal con múltiples cerebros. Y el programador del cliente puede estropear la implementación de una clase de tantas maneras locas que tratar de evitar esto se siente como tratar de cerrar una ventana en un intento de mantener un caballo en un establo.

No trate a sus programadores clientes como prisioneros. Si siguen la interfaz pública, deje que se ejecute el código.

Winston Ewert
fuente
No quiero tratar a los que usan una API como prisioneros, pero estaba pensando en algo similar a proporcionar pautas sobre cómo estructurar, por ejemplo, un complemento para un marco existente. La interfaz del complemento solo le dice cómo el marco usará su extensión, pero no le dice nada sobre cómo se supone que los complementos se comportarán internamente.
Tiene un punto sobre los usos avanzados que no están cubiertos por un enfoque de corte de cookies, pero también puede encontrarse fácilmente con aquellos con la interfaz pública (por ejemplo, en C # necesita / desea devolver un IList<T>pero la interfaz exige que regrese an IEnumerable<T>) Y por último, pero no menos importante, hay un afaik animal no conocido con múltiples cerebros (redundantes, como todos nuestros órganos duplicados). Entonces, aunque puede haber un caso para algo como esto (por ejemplo, arañas), casi con certeza no será un mamífero, por lo que sería una interfaz / estructura completamente diferente de todos modos.
@arotter, estoy a favor de proporcionar pautas sobre la mejor manera de estructurar las cosas internamente. Pero no veo ninguna razón para requerir una estructura interna particular. El objetivo de la interfaz es definir la interfaz pública, no la implementación.
Winston Ewert
1

Como un tipo de interfaz es por defecto un miembro público, todo lo que contiene debe ser público, pero he visto esto:

/programming/792908/what-is-a-private-interface

de msdn:

Las interfaces consisten en métodos, propiedades, eventos, indexadores o cualquier combinación de esos cuatro tipos de miembros. Una interfaz no puede contener constantes, campos, operadores, constructores de instancias, destructores o tipos. No puede contener miembros estáticos. Los miembros de las interfaces son automáticamente públicos y no pueden incluir ningún modificador de acceso.

http://msdn.microsoft.com/en-us/library/ms173156.aspx

Ali Jafer
fuente
Sí, los miembros de una interfaz son públicos. Eso no significa que no pueda tener un miembro privado de algún tipo de interfaz que no exponga en su propia interfaz. La implementación de una interfaz es pública. Usar una interfaz puede ser tan privado como quieras. Básicamente, OP pregunta cómo (s) puede declarar una clase para usar interfaces específicas de forma privada, asegurando así que cualquier clase de implementación realmente implemente esos métodos.
Marjan Venema
Sí, estoy de acuerdo con usted, por eso publiqué un enlace que muestra una interfaz con miembros privados.
Ali Jafer
Ah ok Bien escondido ... :)
Marjan Venema
La pregunta no era sobre interfaces en C # (o en cualquier otro lenguaje existente) sino sobre el concepto fundamental. Esta publicación no lo aborda en absoluto.
Peter Taylor
0

¿Existe tal característica en algún lenguaje de programación? ¿Se pueden usar clases abstractas para resolver esto? ¿O no deberíamos preocuparnos por esto siempre que la interfaz pública funcione de alguna manera como se esperaba?

Seguro que sí. Prácticamente cualquier característica existe en algún idioma.

Por ejemplo, en Objective-C ++ (programación iOS / Mac) es un procedimiento estándar para que una clase tenga al menos dos interfaces. En eso es público, y uno que es privado. A veces también tendrán interfaces adicionales que se definen en otro lugar (por ejemplo, la clase de cadena de bajo nivel tiene una interfaz para realizar operaciones de dibujo de pantalla GUI en la cadena, que se define en un marco / biblioteca completamente separado del que define la cadena clase).

Básicamente, la forma en que trato público / privado es simple:

Los elementos de la interfaz pública nunca se pueden cambiar. Cada vez que refactoriza o mejora su código, la interfaz pública aún debe comportarse exactamente igual que antes.

Las interfaces privadas, por otro lado, se pueden cambiar o eliminar por completo cuando lo desee. Siempre y cuando actualice todo el código de la clase para comprender el nuevo comportamiento, está bien. A menudo, la interfaz pública estará llena de métodos de una o dos líneas que entregan el trabajo real a la interfaz privada.

Objective-C ++ también tiene el concepto de "protegido" en la interfaz, donde hay algo disponible para las subclases pero no para las clases externas.

Por lo general, mi código se divide aproximadamente a la mitad entre público y privado. No uso mucho protegido, aunque es útil de vez en cuando.

Abhi Beckert
fuente
0

Al menos en los lenguajes .NET, es posible aplicar un especificador de acceso a una interfaz. Esto puede ser útil cuando algunas combinaciones de las clases internas de una asamblea comparten habilidades comunes, pero esas combinaciones y habilidades no se ajustan a una relación jerárquica. El hecho de que las habilidades no se ajusten a una relación jerárquica hace que sea necesario usar interfaces para representarlas, pero de ninguna manera implica que se debe permitir que el código externo defina tipos que afirman implementar las interfaces. Además, las interfaces no pueden contener ningún método cuyos tipos de parámetros o tipos de retorno tengan un acceso más estrecho que las interfaces mismas. Si Fredse declara la clase internaly una interfaz IFredtiene un método que devuelve a Fred, entonces la interfaz debe declararseinternal. Si la clase anidada Fred.Joees privatey la interfaz anidada Fred.IJoetiene un método que devuelve a Fred.Joe, Fred.IJoetambién debe declararse private.

Super gato
fuente