Quais são as boas razões para usar a implementação explícita da interface com o único objetivo de ocultar membros?

11

Durante um dos meus estudos sobre os meandros do C #, deparei-me com uma passagem interessante sobre a implementação explícita da interface.

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.

A diferença entre permitir o uso object.method()ou exigir o lançamento de algo ((Interface)object).method()parece uma ofuscação mesquinha para meus olhos inexperientes. O texto observou que este irá esconder o método de Intellisense no nível do objeto, mas por que você iria querer fazer isso se não fosse necessário para evitar conflitos de nome?

Nathanus
fonte

Respostas:

12

Imagine uma situação em que uma interface força uma classe a implementar métodos que, na verdade, não fazem sentido como parte da classe. Isso geralmente pode acontecer quando as interfaces são particularmente grandes e violam o Princípio de Segregação de Interface.

Essa classe precisa implementar a interface, mas em vez de poluir sua interface pública altamente visível com métodos que não fazem sentido, você pode implementar explicitamente os métodos que você não deseja que estejam obviamente disponíveis. A interface pública altamente visível da classe é como você deseja que a classe seja usada e a implementação explícita existe quando a classe é acessada através da interface.

Chris Pitman
fonte
6
Sou apenas eu ou isso soa como uma péssima ideia?
precisa
5
@ Kevin, como assim? Às vezes, a interface está fora de seu controle e você precisa usá-la. Atenuar os danos de uma interface dominante parece razoável.
Chris Pitman
11
+1 por apontar que muitas vezes o mundo real atrapalha a maneira correta de fazer as coisas. @ Kevin, sim, é uma péssima idéia, mas às vezes você não tem escolha e precisa trabalhar com o lixo - é melhor esconder isso e fazer uma fachada de fazer as coisas corretamente, quando você realmente não pode fazê-lo corretamente.
Wayne Molina
@Wayne, eu entendo o seu raciocínio para querer / usar esse sistema. Inferno, eu provavelmente o usaria exatamente no caso em que você declara. Acho que meu breve comentário estava realmente apenas indicando que isso é impróprio, então por que permitir isso no idioma.
precisa
11
A separação excessiva de interfaces pode ser um grande obstáculo à composição e agregação. Considere, por exemplo, que se deseja ter uma implementação IEnumerable<T>que se comporte como a concatenação de duas outras. Se IEnumerable<T>incluísse uma propriedade para dizer se sua contagem era conhecida, potencialmente infinita ou nenhuma, juntamente com um método para perguntar qual era sua contagem, uma classe que agrega vários IEnumerable<T>e se comporta como uma concatenação poderia relatar sua contagem de maneira eficiente no caso de alguns as coleções encapsuladas conheciam suas contagens.
22714
4

A aplicação mais útil que encontrei é na implementação de fábricas. Em muitos casos, é útil criar classes mutáveis ​​dentro da fábrica, mas imutáveis ​​para classes externas. Isso pode ser facilmente implementado em Java usando classes internas, tornando os campos e seus setters privados e apenas expondo os getters como membros públicos. No entanto, em C #, eu tive que usar interfaces explícitas para conseguir a mesma coisa. Vou explicar melhor:

Em Java, a classe interna AND e a classe externa podem acessar membros privados um do outro, o que faz total sentido, pois as classes estão intimamente relacionadas. Eles estão no mesmo arquivo de código e provavelmente são desenvolvidos pelo mesmo desenvolvedor. Isso significa que os campos e métodos privados da classe interna ainda podem ser acessados ​​pela fábrica para modificar seus valores. Mas as classes externas não poderão acessar esses campos, exceto por meio de getters públicos.

No entanto, em C #, as classes externas não podem acessar membros particulares de classes internas, para que o conceito não seja diretamente aplicável. Eu usei uma interface explícita como solução alternativa, definindo uma interface privada na classe externa e implementando-a explicitamente na classe interna. Dessa forma, somente a classe externa pode acessar os métodos nessa interface da mesma maneira que é feita em Java (mas eles devem ser métodos, não campos).

Exemplo:

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 isso que eu usaria interfaces explícitas.

Elias
fonte
3

Se uma classe implementar duas interfaces que contêm um membro com a mesma assinatura, a implementação desse membro na classe fará com que ambas as interfaces usem esse membro como sua implementação. No exemplo a seguir, todas as chamadas para Paintinvocar o mesmo 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 outro lado, a implementação explícita de um (ou de ambos) permite especificar um comportamento diferente para cada um.

Muhammad Tayyeb
fonte
1

Interfaces explícitas são úteis quando um objeto possui duas ou mais formas muito distintas de comunicação.

Por exemplo, um objeto que mantém uma coleção de objetos filho que precisam se comunicar com o pai.

Existem algumas ações que queremos apenas acessíveis a chamadores externos e outras que queremos apenas acessíveis aos objetos filho.

Interfaces explícitas ajudam a garantir essa divisão.

    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
fonte
0

Talvez se você tivesse uma funcionalidade que fosse útil para os usuários, mas apenas se você realmente souber o que está fazendo (o suficiente para ler a documentação para aprender sobre a função), mas que provavelmente quebrará as coisas se não o fizer.

Por que você faria isso em vez de fornecer, diga uma segunda "interface de recursos avançados" que eu não conheço.

Malcolm
fonte
0

Geralmente, o código é lido mais do que está escrito e eu só uso o Intellisense para escrever código rapidamente. Eu não escreveria código de uma maneira específica apenas para tornar o Intellisense mais agradável.

Eu particularmente não vejo como

((Interface) objeto) .method () é mais legível que object.method ().

Se eu tivesse muitos métodos em um objeto específico, eu poderia questionar se é um objeto deus ou não e, na verdade, deveria ser dividido em vários objetos mais simples.

Peter Smith
fonte