Ir para conteúdo
Fórum Script Brasil
  • 0

(Resolvido) Como implementar uma interface e uma classe abstrata em js


Rafael Laurindo

Pergunta

14 respostass a esta questão

Posts Recomendados

  • 0

Dá. Pra que você precisa disso ? É necessário instanciar um objeto que herda de uma classe abstrata realmente ?

Será que não quer apenas compartilhar algum método que já existe? Se este for o caso, então use interfaces, ao invés de um método abstrato.

Link para o comentário
Compartilhar em outros sites

  • 0

Eu só gostaria de implementar esse método de outra classe, eu posso fazer isso tanto com interface quanto com abstract. Fazer um método estático é mais simples. kakaroto, se for o caso, eu posso usar interface sim, mas como implementa-la, já que o js não possui tal encapsulamento formalmente?

Link para o comentário
Compartilhar em outros sites

  • 0

Então Rafael, eu ia postar isso no blog esses dias, mas já que adiantou e leu meus pensamentos vou tentar explicar uma forma que eu imaginei de usar interfaces em javascript. Raro esse tipo de dúvida aqui no fórum, estou gostando disso rsrs.

Eu tenho duas classes, uma é Carro e outra é Moto. As duas aceleram, fream e tem velocidade. Então eu utilizo uma interface que será "motor", pois ambos têm motor.

Classe carro:

var Carro = {
    Class : function(){
        var velocidade = 0
        Interface.motor.call(this, velocidade)

    }
}
Classe moto:
var Moto = {
    Class: function(){
        var velocidade = 0
        Interface.motor.call(this, velocidade)

    }
}
Agora a interface motor:
Interface = {
    motor : function(velocidade){
        this.acelerar = function(){
            velocidade += 10
        }
        this.frear = function(){
            velocidade -= 10
        }
        this.velocidade = function(){ return velocidade }
    }
}
Encapsulado no objeto Interface, motor está sendo chamado nas duas classes através do método call. Call irá chamar as funções passando dois argumentos. O primeiro é o escopo, que definimos this nas classes. O segundo parâmetro de call irá passar a velocidade como argumento para a chamada da função motor(). É a princípio complicado entender, porque javascript é prototipal e funciona sempre com funções. Imagine que a função motor está sendo chamada de dentro das duas classes usando o próprio escopo delas, já setando as funções e usando a variável privada velocidade, passada como argumento do call da função motor. Então:
var mercedes = new Carro.Class
mercedes.acelerar()
mercedes.velocidade() // 10

var honda = new Moto.Class
honda.acelerar()
honda.velocidade() // 10

Usando as mesmas funções para os dois objetos. E você pode também nas classes reimplementar os métodos herdados da interface se precisar.

A lógica é complicada, mas a estrutura bem simples de se usar, e na minha opinião uma forma elegante de se usar interfaces dinâmicamente com javascript.

Abraço.

Link para o comentário
Compartilhar em outros sites

  • 0

Não meu camarada, a lógica não é complicada. A falta de conhecimento complica. Eu não conhecia essas formas de se declarar classes em javascript. Definido-as com o ": function(){}". O "var Carro =..." É para colocar a classe carro como private? Pois o var só pode ser acessado numa mesma function certo? O "call" pode chamar tanto um método quanto uma function? Eu não entendi o atributo this.velocidade na interface motor, já que o declarou com o "var", colocando-o automaticamente como private. A não ser que "velocidade" seja "public" em interface "motor" e nas demais classes, não. Seria isso?

Link para o comentário
Compartilhar em outros sites

  • 0
Eu não conhecia essas formas de se declarar classes em javascript. Definido-as com o ": function(){}".
A forma de declarar com o "classe : {nome1:valor1,nome2:valor2}" é apenas questão de gosto (está relacionada a JSON)

É o mesmo que:

classe=function(){
  this.nome1=valor1
  this.nome2=valor2
  this.metodo1=function(){
    alert("bom dia")
  }
}

É possível fazer o mesmo de várias formas diferentes.

O "var Carro =..." É para colocar a classe carro como private?
Sim, exatamente. Como em JS não existe uma maneira formal de declarar atributos/métodos privados utiliza-se o "var" para definir o escopo.

O "call" pode chamar tanto um método quanto uma function?
Sim. Não há muita diferença entre funções e métodos. É como se funções fossem variáveis (na verdade são) e métodos fossem atributos.

Eu não entendi o atributo this.velocidade na interface motor, já que o declarou com o "var", colocando-o automaticamente como private. A não ser que "velocidade" seja "public" em interface "motor" e nas demais classes, não. Seria isso?
Na verdade o this.velocidade (método) é uma coisa e o velocidade (variável local - na verdade um parâmetro da função) é outra.

Como já disse, Javascript não implementa formalmente o conceito de atributo privado e é necessário simular isso com variáveis locais, que são independentes dos atributos (inclusive podem ter o mesmo nome, que é o caso). No código do Eduardo, há um método e uma variável com mesmo nome, o que também é possível (métodos são atributos cujo tipo é "function", funções são variáveis cujo tipo é "function").

Aquele método "velocidade" ali só serve pra retornar a variável local "velocidade" (isso foi feito pra simular um atributo readonly, se você quiser pode obter o valor, mas não modificá-lo)

Resumo:

Naquele código que o Eduardo postou, a classe "Carro" deriva da classe "Interface.motor" de tal maneira que a classe "Interface.motor" possui acesso ao valor da variável privada (e não à própria variável) "velocidade". Isso acontece porque o valor é passado como argumento para a classe base e é utilizado depois.

Espero que tenha entendido. Qualquer coisa posta aí !

Link para o comentário
Compartilhar em outros sites

  • 0

Só complementando, eu apenas utilizei os var nos nomes das classes porque é uma boa prática. Elas vão ser globais porque estão no contexto global, mas eu utilizo var pra dizer que as variáveis estão iniciando naquele ponto, e não estou simplesmente sobrescrevendo a variável.

A explicação do Jonathan foi perfeita, eu não conseguiria fazer melhor.

E mostra o quão complicado é a lógica de um paradigma funcional e prototipal como a do javascript.

Os closures e os escopos podem causar um tilt no cérebro fácil fácil se não estiver acostumado com a linguagem.

Rafael, no caso de Javascript, Interfaces são simuladas de forma muito mais funcional do que nas linguagens clássicas orientadas à objetos. Então para a maioria dos problemas, não é necessário reproduzir certas coisas de forma tão fiel.

Uma classe abstrata é um tipo de objeto não instanciável, se precisa reutilizar os seus métodos, deve fazer uma herança apenas se sua classe for do tipo da classe abstrata.

Se ela não for do mesmo tipo da classe abstrata, não há necessidade de fazer essa herança e de implementar uma classe abstrata, basta usar as interfaces da forma como mostrei, assim você consegue o reuso, sem fazer heranças desnecessárias, ou criar classes apenas para conseguir reproduzir a herança de métodos abstratos.

Lembre-se que uma classe abstrata não pode ser instanciável, o que nos leva à outro problema, como fazer isso em javascript?

Eu sei uma forma, mas duvido que esteja usando sua classe de forma que ela seja realmente abstrata. Se tiver difícil a compreensão, poste o código, porque aí podemos rebater nele as dúvidas.

Bele? Abraço. ^_^

Link para o comentário
Compartilhar em outros sites

  • 0

Você fez uma colocação perfeita, o que eu preciso mesmo é uma interface, e não uma classe abstrata. A classe abstrata é para ser usada com herança, eu havia me esquecido disto, e um método abstrato, implica em uma classe abstrata. E fazendo um adendo, as interfaces também não são instanciáveis. Só não entendi uma coisa, você implementou o método na interface, a interface não deveria conter só o esqueleto? Ou seja, o método acelerar e freiar não deveria ser implementado em carro e moto?

Link para o comentário
Compartilhar em outros sites

  • 0
Só não entendi uma coisa, você implementou o método na interface, a interface não deveria conter só o esqueleto? Ou seja, o método acelerar e freiar não deveria ser implementado em carro e moto?

Boa pergunta.

Nas linguagens fortemente tipadas as interfaces são implementadas assim. Na verdade o conceito de interface foi criado a partir dessas linguagens.

Mas se formos pensar, há muito mais vantagem de se utilizar a interface em javascript do que nessas outras linguagens.

Oras, qual o grande reuso de código que tem se apenas definir o protótipo da função e ter que reescrevê-las em todas as classes filhas ?!??!?!

Você consegue reestringir o programador e consegue criar classes de forma um pouco mais dinâmica, e só.

No javascript você consegue estender esse propósito fazendo com que de certa forma, se aquela classe implementa uma interface, ela já consegue usar os métodos sem que fosse necessário reescrevê-los toda hora.

Você poderia reproduzir a idéia de interface de forma um pouco mais fiel fazendo isso :

Interface = {
    motor : function(velocidade){
        this.acelerar = function(){ throw new Error("implemente o método acelerar") }
        this.frear = function(){ throw new Error("implemente o método frear") }
        this.velocidade = function(){ throw new Error("implemente o método velocidade") }
    }
}

Ou seja, você força a utilização dos métodos na chamada da interface e ao mesmo tempo diz para o programador implementar o método se chamar internamente em algum método dessa classe, enviando exatamente o erro.

Mas aí você estaria restringindo e forçando uma inflexibilidade que não é a característica da linguagem.

Na minha opinião, a interface é muito melhor utilizada em javascript.

Link para o comentário
Compartilhar em outros sites

  • 0

Na minha também meu amigo, concordo fortemente com você e sua colocação, e agora que me explicou seu pensamento... Eu não simplesmente sigo padrões, mas o que faz sentido pra mim, eu gosto de OO porque faz sentido e não simplesmente porque é o futuro, ou exigência do mercado. Eu gosto de conhecimento e de saber o que outras pessoas pensam, e não simplesmente fechar os olhos e seguir exatamente o que manda o script (fiz até trocadilho).

Me responda por favor, para que serve o throw, perdoe minha ignorância.

Editado por Rafael Laurindo
Link para o comentário
Compartilhar em outros sites

  • 0

throw interrompe imediatamente o fluxo do javascript, gerando um erro. Você pode passar uma string detalhando o erro ou gerando um novo objeto do tipo Erro como eu fiz. É usado para gerar um erro de maneira proposital, porém, ele tem a vantagem de ser mais específico.

Já deve ter acontecido com você de montar vários métodos, vários objetos e no decorrer da execução o script quebra, dando um erro dizendo que tal objeto é undefined.

Muitas vezes você consegue rapidamente saber o que acontece. Outras vezes é difícil depurar.

No caso do throw, se você o utiliza usando uma mensagem mais amigável, facilita para outro programador saber onde está o problema.

No caso da nossa interface, se o programador tentar envocar um método que teóricamente existe na interface sem o sobreescrever, ele gera um erro, dizendo que aquele método existe, porém é necessário ser sobrescrito.

Que bom que pensa assim, não se deve apenas seguir as coisas por seguir. Seja criativo, leia diversas fontes e mais importante que isso, as conteste.

Abraço ;)

ps: Curti o trocadilho! huahuahauhauahu

Link para o comentário
Compartilhar em outros sites

  • 0

A explicação do Kakarotto foi excelente !

Só faltou citar que é possível utilizar try e catch para capturar as exceções.

Quando vi que o Eduardo tinha postado, já havia terminado meu post... Então vai assim mesmo.

O throw serve pra lançar uma exceção, que pode ser recuperada depois.

É utilizado para separar o tratamento de erros no código.

Por exemplo, observe esse código:

function PedeInteiros_E_Divide() {
    //Função para auxiliar a validação
    var valida = function (numero, pode_ser_nulo) {
        if (numero == "") {
            alert("Digite algo !")
            return null //se é inválido, retorna null
        }else if (isNaN(numero)) {
            alert("O valor digitado deve ser numérico")
            return null //se é inválido, retorna null
        } else if (numero != parseInt(numero)) {
            alert("O valor digitado deve ser inteiro")
            return null //se é inválido, retorna null
        } else if (numero == 0 && !pode_ser_nulo) {
            alert("O valor digitado não pode ser nulo")
            return null //se é inválido, retorna null
        }
        return numero //Se é válido, retorna o próprio número
    }
    //Primeiro número
    var num1 = valida(prompt("Digite um número inteiro:"),true)
    if (num1==null) {
        return null //indica que houve um erro retornando "null"
    }
    //Segundo número
    var num2 = valida(prompt("Digite um número inteiro não-nulo:"),false)
    if (num2 == null) {
        return null //indica que houve um erro retornando "null"
    }
    //Divide os dois
    return num1/num2
}
function teste() {
    while (1) {
        var result = PedeInteiros_E_Divide()
        if (result == null) {
            alert("Vamos tentar de novo !\nVê se não erra mais...")
        } else {
            alert("O resultado da divisão dos números é: " + result)
            break;
        }
    }
}
A validação está muito complicada. Poderíamos deixar mais simples. Observe o código melhorado:
function PedeInteiros_E_Divide() {
    //Função para auxiliar a validação
    var valida = function (numero, pode_ser_nulo) {
        if (numero == "") {
            throw "Digite algo !"
        } else if (numero == null) {
            throw "Dê OK ao invés de CANCEL"
        }else if (isNaN(numero)) {
            throw "O valor digitado deve ser numérico"
        } else if (numero != parseInt(numero)) {
            throw "O valor digitado deve ser inteiro"
        } else if (numero == 0 && !pode_ser_nulo) {
            throw "O valor digitado não pode ser nulo"
        }
        return numero //Se é válido, retorna o próprio número
    }
    //Primeiro número
    var num1 = valida(prompt("Digite um número inteiro:"),true)
    //Segundo número
    var num2 = valida(prompt("Digite um número inteiro não-nulo:"),false)
    //Divide os dois
    return num1/num2
}
function teste() {
    while (1) {
        try {
            alert("O resultado da divisão dos números é: " + PedeInteiros_E_Divide())
            break;
        } catch (erro) {
            alert(erro +
                "\n\nVamos tentar de novo !\nVê se não erra mais !")
        }
    }
}

Bem mais simples, e o tratamento do erro está separado da parte funcional. Qualquer coisa posta aí !

Obs.:

Aquela função valida ali foi só pra não repetir o código pros dois números.

Link para o comentário
Compartilhar em outros sites

  • 0

Huahuah. Obrigado galera, fui contemplado por pessoas que venceu desafio e é monitor, que honra, véio. Está tudo resolvido. A propósito, eu também curti algo, o comentário do kakaroto sobre ele mesmo.

Além de gostoso, simpático e inteligente.., modesto, acima de tudo
. Fecho, não falta mais nada. Ainda bem que o modesto é acima de tudo não é?, já pensou se não fosse?, hahauhau, você é o cara, mermo, to rachando de rir aqui. Até a próxima. Akeleabrass ;) Editado por Rafael Laurindo
Link para o comentário
Compartilhar em outros sites

Participe da discussão

Você pode postar agora e se registrar depois. Se você já tem uma conta, acesse agora para postar com sua conta.

Visitante
Responder esta pergunta...

×   Você colou conteúdo com formatação.   Remover formatação

  Apenas 75 emoticons são permitidos.

×   Seu link foi incorporado automaticamente.   Exibir como um link em vez disso

×   Seu conteúdo anterior foi restaurado.   Limpar Editor

×   Você não pode colar imagens diretamente. Carregar ou inserir imagens do URL.



  • Estatísticas dos Fóruns

    • Tópicos
      152,3k
    • Posts
      652,3k
×
×
  • Criar Novo...