Casos de uso para interfaces "privadas"?

8

Fiquei me perguntando se havia um caso de uso válido para poder definir corretamente as propriedades e funções internas específicas de uma classe de maneira semelhante à maneira como uma interface define as propriedades e funções públicas de uma classe.

Imagine a tarefa que você precisa para criar uma classe que descreva um ser humano.

Obviamente, cada humano é uma criatura humanóide, mas nem toda criatura humanóide é um humano, então você provavelmente teria uma interface IHumanoid com funções como estas (já que não é útil codificar o plano do corpo na classe):

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

Além disso, e obviamente, cada humano é um mamífero, mas nem todo mamífero é um humano, então provavelmente há outra interface IMammal com duas definições para machos e fêmeas flutuando em algum 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;
}

Portanto, nossa classe provavelmente se parece com isso agora:

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 disso, podemos implementar nossas classes ainda mais e tudo está funcionando bem até conseguirmos a tarefa de usar as interfaces existentes para implementar outras classes. Talvez um gorila, uma orca e um ornitorrinco.

Ignorando o enorme problema que o ornitorrinco colocará em nossa atual estrutura de interface (* tosse * mamífero que põe ovos * tosse *), temos o "problema" de que nada nos impede de dar ao cérebro do gorila 2, ao pulmão da orca 8 e à metade do ornitorrinco uma dúzia de fígados. E, embora possamos ser disciplinados o suficiente para seguir a estrutura que os mamíferos normalmente têm, não podemos garantir o mesmo se abrirmos a API para outros desenvolvedores que possam codificar algumas coisas seriamente erradas que ainda parecem boas para o mundo exterior.

Portanto, eu queria saber se havia um caso de uso válido para criar algo como uma "interface privada" que define funções e propriedades não públicas. Talvez algo nesse 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.
}

Esse recurso existe em alguma linguagem de programação? Classes abstratas podem ser usadas para resolver isso? Ou não deveríamos nos preocupar com isso desde que a interface pública funcione de alguma forma como o esperado?


fonte
A maneira "IMumle" de denotar uma interface é um mau hábito, na minha opinião
Dois comentários Primeiramente, parece que podemos basear toda a discussão no XML Schema, em vez de especificidades das interfaces de linguagem. Em segundo lugar, a "validação" às vezes (mas nem sempre) é melhor separada da "estrutura".
rwong

Respostas:

3

E, embora possamos ser disciplinados o suficiente para seguir a estrutura que os mamíferos normalmente têm, não podemos garantir o mesmo se abrirmos a API para outros desenvolvedores que possam codificar algumas coisas seriamente erradas que ainda parecem boas para o mundo exterior.

O que exatamente você ganha forçando-os a seguir sua estrutura? Você possivelmente quebra alguns usos avançados, talvez haja um caso para um animal com múltiplos cérebros. E o programador cliente pode atrapalhar a implementação de uma classe de tantas maneiras malucas que tentar impedir isso parece tentar fechar uma janela na tentativa de manter um cavalo em um celeiro.

Não trate seus programadores clientes como prisioneiros. Se eles seguirem a interface pública, deixe o código executar.

Winston Ewert
fonte
Não quero tratar aqueles que usam uma API como prisioneiros, mas estava pensando em algo como fornecer diretrizes sobre como estruturar, por exemplo, um plugin para uma estrutura existente. A interface do plug-in apenas informa como sua extensão será usada pela estrutura, mas não informa nada sobre como os plug-ins devem se comportar internamente.
Você tem um argumento sobre usos avançados que não são cobertos por alguma abordagem de corte de cookies, mas também pode facilmente encontrar aqueles com a interface pública (por exemplo, em C # você precisa / deseja retornar um, IList<T>mas a interface exige que você retorne an IEnumerable<T>). E por último, mas não menos importante, não há um animal conhecido com cérebros múltiplos (redundantes, como todos os nossos órgãos duplicados). Portanto, embora possa haver um argumento para algo assim (por exemplo, aranhas), quase certamente não será um mamífero, portanto seria uma interface / estrutura completamente diferente.
@ arotter, sou a favor de fornecer diretrizes sobre a melhor maneira de estruturar as coisas internamente. Mas não vejo motivo para exigir uma estrutura interna específica. O ponto principal da interface é definir a interface pública, não a implementação.
Winston Ewert
1

Como um tipo de interface é por padrão um membro público, tudo dentro dele deve ser público, mas eu já vi isso:

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

de msdn:

As interfaces consistem em métodos, propriedades, eventos, indexadores ou qualquer combinação desses quatro tipos de membros. Uma interface não pode conter constantes, campos, operadores, construtores de instância, destruidores ou tipos. Não pode conter membros estáticos. Os membros das interfaces são automaticamente públicos e não podem incluir nenhum modificador de acesso.

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

Ali Jafer
fonte
Sim, os membros de uma interface são públicos. Isso não significa que você não pode ter um membro privado de algum tipo de interface que você não expõe em sua própria interface. A implementação de uma interface é pública. O uso de uma interface pode ser tão privado quanto você desejar. O OP está essencialmente perguntando como ele pode declarar uma classe para usar interfaces específicas em particular, garantindo assim que qualquer classe de implementação realmente implemente esses métodos.
Marjan Venema
Sim, eu concordo com você, portanto, por que eu postei um link que mostra uma interface com membros privados.
Ali Jafer 19/08/12
Ah ok. Bem escondido ... :)
Marjan Venema
A questão não era sobre interfaces em C # (ou em qualquer outra linguagem existente), mas sobre o conceito fundamental. Esta postagem não aborda nada.
Peter Taylor
0

Esse recurso existe em alguma linguagem de programação? Classes abstratas podem ser usadas para resolver isso? Ou não deveríamos nos preocupar com isso desde que a interface pública funcione de alguma forma como o esperado?

Com certeza sim. Praticamente qualquer recurso existe em algum idioma.

Por exemplo, no Objective-C ++ (programação iOS / Mac), é um procedimento padrão para uma classe ter pelo menos duas interfaces. Em que é público, e que é privado. Às vezes, eles também têm interfaces extras definidas em outro local (por exemplo, a classe de string de baixo nível tem uma interface para executar operações de desenho de tela da GUI na string, que é definida em uma estrutura / biblioteca completamente separada daquela que define a string classe).

Basicamente, a maneira como trato público / privado é simples:

Os elementos da interface pública nunca podem ser alterados. Sempre que você refatorar ou melhorar seu código, a interface pública ainda precisará se comportar exatamente da mesma maneira que antes.

As interfaces privadas, por outro lado, podem ser alteradas ou completamente removidas sempre que você desejar. Contanto que você atualize todo o código da classe para entender o novo comportamento, você estará bem. Freqüentemente, a interface pública estará cheia de um ou dois métodos de linha que entregam o trabalho real à interface privada.

O Objective-C ++ também possui o conceito de "protegido" na interface, onde algo está disponível para subclasses, mas não para classes externas.

Normalmente, meu código é dividido entre meio público e privado. Eu não uso muito protegido, apesar de ser útil de vez em quando.

Abhi Beckert
fonte
0

Pelo menos nas linguagens .NET, é possível aplicar um especificador de acesso a uma interface. Isso pode ser útil quando algumas combinações de classes internas de uma montagem compartilham habilidades comuns, mas essas combinações e habilidades não se encaixam em um relacionamento hierárquico. O fato de as habilidades não se ajustarem a um relacionamento hierárquico torna necessário o uso de interfaces para representá-las, mas de forma alguma implica que o código externo deva ter permissão para definir tipos que pretendem implementar as interfaces. Além disso, as interfaces não podem conter métodos cujos tipos de parâmetro ou retorno tenham acesso mais restrito do que as próprias interfaces. Se a classe Fredfor declarada internale uma interface IFredtiver um método que retorne a Fred, a interface deverá ser declaradainternal. Se a classe aninhada Fred.Joeé privatee a interface aninhada Fred.IJoepossui um método que retorna a Fred.Joe, Fred.IJoetambém deve ser declarado private.

supercat
fonte