¿Cuáles son buenas razones para usar la implementación explícita de la interfaz con el único propósito de ocultar miembros?

11

Durante uno de mis estudios sobre las complejidades de C #, me encontré con un pasaje interesante sobre la implementación explícita de la interfaz.

While this syntax is quite helpful when you need to resolve name clashes, you can use explicit interface implementation simply to hide more "advanced" members from the object level.

La diferencia entre permitir el uso object.method()o exigir la fundición ((Interface)object).method()parece una ofuscación mezquina para mis ojos inexpertos. El texto señaló que esto ocultará el método de Intellisense a nivel de objeto, pero ¿por qué querría hacerlo si no fuera necesario para evitar conflictos de nombres?

Nathanus
fuente

Respuestas:

12

Imagine una situación en la que una interfaz obliga a una clase a implementar métodos que en realidad no tienen sentido como parte de la clase. Esto a menudo puede suceder cuando las interfaces son particularmente grandes y violan el Principio de segregación de interfaz.

Esta clase necesita implementar la interfaz, pero en lugar de contaminar su interfaz pública altamente visible con métodos que no tienen sentido, puede implementar explícitamente los métodos que obviamente no desea que estén disponibles. La interfaz pública altamente visible de la clase es cómo desea que se use la clase, y la implementación explícita está ahí cuando se accede a la clase a través de la interfaz.

Chris Pitman
fuente
66
¿Soy solo yo, o esto suena como una idea terrible?
Kevin Peno
55
@ Kevin, ¿cómo es eso? A veces, la interfaz está fuera de tu control y tienes que usarla. Mitigar el daño de una interfaz dominante parece razonable.
Chris Pitman
1
+1 por señalar que muchas veces el mundo real se interpone en la forma correcta de hacer las cosas. @Kevin, sí, es una idea terrible, pero a veces no tienes otra opción y tienes que trabajar con la chatarra; es mejor esconderla y hacer una fachada de hacer las cosas correctamente cuando realmente no puedes hacerlo correctamente.
Wayne Molina
@Wayne, entiendo tu razonamiento para querer / usar dicho sistema. Demonios, probablemente lo usaría exactamente en el caso que tú digas. Supongo que mi breve comentario fue solo señalar que esto es incorrecto, entonces ¿por qué permitirlo en el idioma?
Kevin Peno
1
La separación excesiva de la interfaz puede ser un obstáculo importante para la composición y agregación. Considere, por ejemplo, que uno desea tener una implementación IEnumerable<T>que se comporte como la concatenación de otros dos. Si se IEnumerable<T>incluye una propiedad para decir si su recuento era conocido, potencialmente infinito, o ninguno, junto con un método para preguntar cuál era su recuento, una clase que agrega múltiples IEnumerable<T>y se comporta como una concatenación podría informar eficientemente su recuento en el caso de que algunos de Las colecciones encapsuladas conocían sus cuentas.
supercat
4

La aplicación más útil que encontré es con la implementación de fábricas. En muchos casos, es útil crear clases que sean mutables dentro de la fábrica pero inmutables a clases externas. Esto se puede implementar fácilmente en Java utilizando clases internas, haciendo que los campos y sus establecedores sean privados y solo exponiendo a los captadores como miembros públicos. Sin embargo, en C #, tuve que usar interfaces explícitas para lograr lo mismo. Explicaré más a fondo:

En Java, la clase interna Y la clase externa pueden acceder a miembros privados entre sí, lo que tiene mucho sentido ya que las clases están muy relacionadas. Están en el mismo archivo de código y probablemente los haya desarrollado el mismo desarrollador. Esto significa que la fábrica aún puede acceder a los campos y métodos privados en la clase interna para modificar sus valores. Pero las clases externas no podrán acceder a estos campos excepto a través de sus captadores públicos.

Sin embargo, en C #, las clases externas no pueden acceder a miembros privados de las clases internas, por lo que el concepto no es directamente aplicable. Utilicé una interfaz explícita como solución definiendo una interfaz privada en la clase externa e implementándola explícitamente en la clase interna. De esta manera, solo la clase externa puede acceder a los métodos en esta interfaz de la misma manera que se hace en Java (pero tienen que ser métodos, no campos).

Ejemplo:

public class Factory
{
    // factory method to create a hard-coded Mazda Tribute car.
    public static Car CreateCar()
    {
        Car car = new Car();

        // the Factory class can modify the model because it has access to
        // the private ICarSetters interface
        ((ICarSetters)car).model = "Mazda Tribute";

        return car;
    }

    // define a private interface containing the setters.
    private interface ICarSetters
    {
        // define the setter in the private interface
        string model { set; }
    }

    // This is the inner class. It has a member "model" that should not be modified
    // but clients, but should be modified by the factory.
    public class Car: ICarSetters
    {
        // explicitly implement the setter
        string ICarSetters.model { set; }

        // create a public getter
        public string model { get; }
    }
}

class Client
{
    public Client()
    {
        Factory.Car car = Factory.CreateCar();

        // can only read model because only the getter is public
        // and ICarSetters is private to Factory
        string model = car.model;
    }
}

Para eso usaría interfaces explícitas.

Elias
fuente
3

Si una clase implementa dos interfaces que contienen un miembro con la misma firma, la implementación de ese miembro en la clase hará que ambas interfaces usen ese miembro como su implementación. En el siguiente ejemplo, todas las llamadas para Paintinvocar el mismo método. C#

class Test 
{
    static void Main()

    {
        SampleClass sc = new SampleClass();
        IControl ctrl = (IControl)sc;
        ISurface srfc = (ISurface)sc;

        // The following lines all call the same method.
        sc.Paint();
        ctrl.Paint();
        srfc.Paint();
    }
}


interface IControl
{
    void Paint();
}
interface ISurface
{
    void Paint();
}
class SampleClass : IControl, ISurface
{
    // Both ISurface.Paint and IControl.Paint call this method. 
    public void Paint()
    {
        Console.WriteLine("Paint method in SampleClass");
    }
}

Por el contrario, la implementación explícita de uno (o ambos) le permite especificar un comportamiento diferente para cada uno.

Muhammad Tayyeb
fuente
1

Las interfaces explícitas son útiles cuando un objeto tiene dos o más formas de comunicación muy distintas.

Por ejemplo, un objeto que mantiene una colección de objetos secundarios que necesitan comunicarse con el padre.

Hay algunas acciones que solo queremos que sean accesibles para personas que llaman externas y algunas acciones que solo queremos que sean accesibles para los objetos secundarios.

Las interfaces explícitas ayudan a garantizar esta división.

    static void Main(string[] args)
    {
        var parent = new Parent();
        parent.DoExternalStuff();
    }

    interface IExternal {
        void DoExternalStuff();
    }

    interface IInternal {
        void DoInternalStuff();
    }

    class Parent : IExternal, IInternal
    {
        public Parent()
        {
            var child = new Child(this);
        }

        public void DoExternalStuff()
        {
           // do something exciting here for external callers
        }

        void IInternal.DoInternalStuff()
        {
            // do something exciting here for internal callers
        }
    }

    class Child
    {
        public Child(IInternal parent)
        {
            parent.DoInternalStuff();
        }
    }
lzcd
fuente
0

Tal vez si tuviera una funcionalidad que fuera útil para los usuarios, pero solo si realmente sabe lo que está haciendo (lo suficiente como para haber leído la documentación para aprender sobre la función) pero es probable que eso rompa las cosas si no lo hace.

Por qué haría eso en lugar de proporcionar, diga una segunda "interfaz de funciones avanzadas" No lo sé.

Malcolm
fuente
0

El código generalmente se lee más de lo que se escribe y solo uso Intellisense para escribir código rápidamente. No escribiría código de una manera particular solo para hacer que Intellisense sea más agradable.

No veo particularmente cómo

((Interface) object) .method () es más legible que object.method ().

Si tuviera muchos métodos en un objeto en particular, podría cuestionar si es o no un objeto de dios y si realmente debería dividirse en múltiples objetos más simples.

Peter Smith
fuente