, , , ,

OOP em javascript – Herança e Singleton pattern

Olá CAMAGADAS!

Iniciados em classes em javascript, gostaria de falar um pouco sobre as técnicas que utilizo para herdar classes e falar um pouco sobre dois métodos para implementar singletons.

Primeiro, a herança. Não quero esticar muito sobre esse assunto e imagino que saibam pelo menos  para que serve isso. Se alguém não sabe, em algum daqueles links que passei no post anterior à esse deve ter. Senão aqui vai uma breve explicação:

Herança é um princípio da Programação Orientada a Objetos que permite que as classes compartilhem atributos e operações baseados em um relacionamento, geralmente generalização. A herança permite a criação de subclasses que herdam alguns dos atributos e das operações (ou Métodos) da classe pai (super-classe ou classe base). A herança é um conceito aplicado no momento de criação das classes. Ela é usada na intenção de reaproveitar código ou comportamento generalizado ou especializar operações ou atributos.

fonte: Wikipédia

Herança

A idéia de Classe é gerar um template, um tipo de alguma coisa que queremos criar. A instância é nada mais que um objeto concretizado deste tipo. Vou utilizar um exemplo clássico e simples de se abstrair, vou tentar usar sempre os mesmos exemplos nos posts de Orientação à objetos em javascript.

Vamos criar um objeto Moto, e um objeto Carro. Os dois aceleram, fream e tem velocidade. Os dois fazem mais ou menos a mesma coisa, mas não são A MESMA COISA.

Podemos pensar que os dois são veículos. Então temos uma classe em comum, a classe Veiculo =).

var Veiculo = {
	Class : function(){
		var velocidade = 0
		this.acelerar = function(){ velocidade += 10}
		this.frear = function(){ velocidade -=10 }
		this.velocidade = function(){ return velocidade }
	}
}

Criamos então uma Classe em comum aos dois objetos, Moto e Carro. Como fazer a herança para os dois? Simples:

Moto:

var Motocicleta = {
	Class : function(){		
		Veiculo.Class.apply(this)
		
	}
}

Carro:

var Carro = {
	Class : function(){		
		Veiculo.Class.apply(this)
		
	}
}
Então:
var moto = new Motocicleta.Class()
	moto . acelerar()
	moto . velocidade() // 10
Olha que chupetinha..
Isso vale também para a instancia de carro. Ambas as classes herdam os métodos da classe mãe Veiculo.  Não existe apenas esta forma de se implementar a herança em javascript, mas eu considero essa a mais elegante de todas, e acho inclusive semântica. Porque em inglês, está praticamente dizendo :  “Classe Veiculo! Aplique-se à isto”.
O apply é um método herdado para todos os tipos Function. Ele te da o poder de chamar qualquer função determinando qual será o escopo e os parâmetros passados.
Quando chamamos Veiculo.Class.apply(this) nas duas classes, estamos chamando a função Veiculo.Class de dentro das Classes usando o próprio escopo delas. Ou seja, no momento em que a Veiculo.Class estiver sendo executada, quando chegar nas linhas this.acelerar,, this.etc,  ela vai saber que this se refere ao escopo da própria classe, portanto vai setar os valores para o escopo da classe Moto e Carro.
A variável privada velocidade é passada para o escopo das classes filhas graças ao Closure, porque a função Veiculo.Class inicia uma variável livre que pode ser vista através dos métodos públicos this.acelerar, this.frear e this.velocidade e por usar a palavra chave var a cada chamada da função Veiculo.Class se criará uma nova variável privada “velocidade”, uma para cada classe.
Design Pattern : Singleton
Algumas vezes precisamos de um objeto único que terá seus estados e métodos únicos e de maneira estática. Ou seja, queremos que haja apenas uma e somente única instancia daquele objeto.
Vi muitas implementações na internet de singleton em js, e todas eram péssimas. Se forem procurar, vão achar em sites brasileiros ou gringos muitas implementações parecidas com essa:
function Page(){
	var width = 800;
	this.getWidth = function(){
		return width;
	}
}
Page.instance = null;
Page.getInstance = function(){
	if (Page.instance == null) Page.instance = new Page();
	return Page.instance;
}
A primeira impressão é de que é uma boa implementação de singleton, porque segue um padrão semântico bem próximo aos usados em linguagens como o Java e C++.
Mas é uma abordagem péssima para javascript. Em javascript você não consegue definir constantes como em java por exemplo quando usa a palavra-chave final. Então, no caso acima, Page.instance pode ser a qualquer momento modificada para null e aí vc terá duas instâncias Singleton.
Eu particularmente uso dois tipos de singleton, um desenvolvi por conta própria e o outro é puramente usar o que a linguagem tem.
Se por algum motivo precisa fazer uma classe dinâmica Singleton, usando construtores, a forma que eu pensei foi essa:
var Deus = {
	Class : function(){
		var self = this
				
		Deus.getInstance = function(){ return self }
		delete Deus.Class
	},
	getInstance : function(){ return new Deus.Class	}
}
Digamos que Deus exista e que haja apenas um. A funcionalidade da Classe Deus está toda encapsulada em Deus.Class e a classe tem também um método estático getInstance. A minha idéia foi a seguinte:
Existem dois jeitos de instanciar Deus. Uma chamando new Deus.Class e a outra Deus.getInstance().
No primeiro, a função Deus.Class é envocada salva o escopo em uma variável, redefine o método estatico getInstance para que retorne o escopo salvo self e por fim, deleta a variável Deus.Class do objeto Deus. Pronto! você não consegue mais instanciar Deus.Class, porque essa variável não existe mais. Pode apenas recuperar o objeto através de Deus.getInstance.
No segundo, ao chamar Deus.getInstance sem usar o new,  ele retorna uma nova instancia da classe o que levará ao nosso primeiro caso acima.
De uma forma ou outra, sempre terá apenas uma instancia do objeto.
Essa maneira de criar Singletons não é muito comum. Pra falar a verdade até agora não precisei usá-la. Ela ainda pode ser burlada, mas não é tão fácil quanto setar uma variável, de forma que consegue disciplinar de maneira mais eficaz o programador que irá por ventura fazer a  manutenção do código.
E finalmente o modo mais simples mais perfeito mais fácil de se criar um Singleton.
var Singleton = {}
É meus caros… Singleton a grosso modo não é uma instância única de um objeto ? Então… A notação json do javascript nos permite criar um objeto dinamicamente de forma bem elegante, onde podemos criar propriedades e métodos estáticos para armazenar e/ou modificar os estados desse objeto.
Simples!
Você com certeza já deve ter usado um Singleton sem saber. Geralmente o povo usa os Singletons para formar namespaces em um objeto encapsulado. Por exemplo:
var Projeto = {
	version : '1.0',
	sidebar : function(){},
	util : {
		Analitics : function(){}
	}
}
	
Projeto.util.Analitics()
Eu não tinha tanta liberdade para usar Javascript Orientado à Objetos em outras empresas que trabalhei. Mas nessa atual ( F.biz)  tenho uma liberdade muito grande para mexer com orientação a objetos em javascript e tenho percebido que isso ajuda e MUITO no desenvolvimento, tanto na produtividade quanto na manutenção e por incrível que pareça no desempenho. Sem contar que fica mais divertido =).
O javascript evolui sempre e a cada dia que passa novas idéias e inovações aparecem.
Coisas que antes não eram viáveis devido à restrições de hardware, hoje já podem ser feitas sem comprometer o processamento da máquina. Funcionalidades que antes eram feitas em server-side, hoje podem ser feitas via client sem problemas, aliviando o tráfego e melhorando ainda mais o desempenho das páginas.
A medida que essa evolução acontece, conceitos de programação são cada vez mais exigidos, hoje mais do que nunca, javascript é uma linguagem de programação e não mais uma linguagem de script simples usada para fazer validações de formulários.
Se você ainda não tem conceitos de programação de qualquer paradigma e pretende ser um bom profissional em javascript, se quer um conselho de um amigo, eu sugiro que corra atrás o quanto antes, porque isso FAZ muita diferença e você certamente vai achar muito mais divertido programar nessa linguagem.
Galera,
NUNCA, JAMAIS, acredite em alguém que diga que isso não faz diferença. Já ouvi de alguns “Desenvolvedores web” que orientação à objetos em javascript não serve para nada, já que é possível fazer tudo funcionar de forma estrutural. Se algum dia ouvir isso de alguém que você imaginava ser uma pessoa experiente e com certo conhecimento, fica minha dica, ouça-o menos e estude mais.

Mais: Milfont.org/tech – Herança no Javascript

Um grande abraço!

13 respostas para “OOP em javascript – Herança e Singleton pattern”

  1. Mais uma vez excelente post.
    Já ví muitas maneiras de simular herança no javascript, só que geralmente escreve-se muito código complexo para que seja o mais próximo de linguagens como Java. Mas o problema pode ser justamente este “tentar simular”, não precisamos deixar o javascript igual a qualquer outra linguagens, acho que seremos mais produtivos se usarmos o potencial e flexibilidade do javascript pra criar nosso código. A herança por “apply” e usar objetos pra criar singletons deixam evidente a poder do js, as vezes pode ser difícil de admitir algumas coisas, mas não imagino motivos pra qualquer programador criticar o uso de json pra descrever objetos.

    Não para com esses post não rapaz.
    Abraços

  2. Parabéns pelo post Eduardo, vi ele através de um retweet e achei super interessante. O que foi legal é que agora de pouco estava comentando com um colega de trabalho da dificuldade achar algo legal sobre OOP em Javascript. Gostaria de saber se tem twitter, pois assim vou virar seu seguidor.

    Abraços,
    Alexandre

    • Obrigado Alexandre! Muita gentileza da sua parte =D

      Que bom que gostou do post, as vezes eu penso que só eu curto hahahahahha.

      Eu tenho twitter, e com muita vergonha digo que só criei a conta, não entrei muito na onda, mas pretendo entrar. Se quiser seguir é: Ojaviani

      Um abraço.

  3. Valew mesmo Javiani, curti demais e já to a uns dias testando implementações,

    vê se eu entendi certo:
    —–
    var Veiculo = {
    Class : function(){
    var velocidade = 0
    this.acelerar = function(){ velocidade += 10}
    this.frear = function(){ velocidade -=10 }
    this.velocidade = function(){ return velocidade }
    }
    }
    —-

    Toda a minha classe deve ser criada no escopo da funcao Class, correto? As funcoes serão herdadas tbm, entao blz.. mas qual vai ser o comportamento quando sobrescrevo as funcoes?

    Abraço, e continue cara, seus textos são top!! vlw

    • Boa pergunta Claudio. Bom, é apenas uma forma diferente de fazer classes, tudo o que vc pode fazer da forma tradicional vc pode fazer desta forma, com a diferença de que usa sempre o ‘.Class’ .

      Depende de o que quer dizer com sobrescrever as funções. Se precisar sobrescrever vc pode fazer atraves do próprio objeto instanciado ou mudando os métodos através do prototype, mas aí estaria fazendo na classe mãe ou na filha:

      Veiculo.prototype.acelerar = function(){}

      É importante lembrar que se quiser fazer um override dos métodos, não deve definí-los dentro da classe, mas sim através de prototype. Porque vc não vai conseguir sobrescrevê-los se estiverem dentro do corpo do construtor.

      Obrigado Claudio, muito boa a pergunta.

      Abraço.

  4. Então, isso é algo que tem me intrigado em javascript oo, aparentemente nao tem como eu fazer override de metodo como em PHP5:

    Class Veiculo{
    private $andar;
    public function andar(){
    $andar = true;
    }
    }

    Class Carro extends Veiculo{
    private $ligado;
    public function andar(){
    parent::andar();
    $this->ligar();
    }
    }

    Javascript até onde eu sei nao tem “parent” ou “super”, e o pior, eu tenho que declarar as funcoes que poderão ser sobrescritas direto nos objetos, o que não é lá muito bonito ^^,
    você visualizou alguma forma de contornar isso? agora penso em implementar essas funcoes ou classes num construtor.. algo como um Object Factory +ou-

    Abração,
    T+

    • E aí Claudio, suave?

      O javascript não precisa ter parent, simplesmente por causa do paradigma funcional que implementa, use o closure… pra que “super” ?

      var Veiculo = {
      Class : function(){
      var velocidade = 0

      this.acelerar = function(){ velocidade += 10}
      this.frear = function(){ velocidade -=10 }
      this.velocidade = function(){ return velocidade }
      }
      }

      var Moto = {
      Class : function(){
      Veiculo.Class.apply(this)

      var acelerar = this.acelerar

      this.acelerar = function(){
      acelerar()
      acelerar()
      }
      }
      }

      var moto = new Moto.Class
      moto.acelerar()
      alert( moto.velocidade() )

      Viu ? Sobrescrevi o método na própria classe moto, todos os objetos da classe moto aceleram agora duas vezes mais rápido =).

      Sacou a melosquência ?

      Abraços.

  5. Boa tarde, Javiani. Parabéns pelos artigos! Tenho os usados como referência para aprender javascript OO.

    Estou iniciando com programação em JS, considerando que eu a utilizava apenas para validações de formulários e alguns alerts rsrs.

    Ultimamente percebi o quão importante espaço javascript vem tomando na web e no desenvolvimento de software em geral. Agora Javascript está presente no server-side com NodeJS, em banco de dados como por exemplo no MongoDb, frameworks para aplicações RIAs, como ExtJs e nos dispositivos móveis se eu não me engano algumas novas tecnologias para esse seguimento está adotado o engine do JS para criação de aplicativos.

    Em relação ao seu exemplo das classes Veiculo carro e moto não estou conseguindo acessar atributos e métodos privados da classe pai. Por que? Ou o que estou fazendo de errado?

    Exemplo:

    var Motocicleta = {
    Class : function(){
    Veiculo.Class.apply(this);
    this.velocimetro = function(){
    return ‘A velocidade atual é: ‘ + velocidade;
    }
    }
    }

    m = new Motocicleta();
    m.acelerar();
    alert(m.velocimentro()); //A velocidade atual é: undefined;

    Muito obrigado e parabéns mais uma vez.

    • Olá Márcio, tudo bem?

      Primeiramente, obrigado pelos elogios e por seguir o blog =).

      Na sua pergunta você já respondeu o que acontece… Você não consegue acessar a variável velocidade da classe Pai justamente por ela ser privada.
      Para acessar uma variável da classe pai essa variável precisar se pública ou protegida.

      Existe o método .velocidade() que é público, que você pode usar para acessar a variável privada “velocidade”.

      Então o que precisa fazer pra resolver o seu problema é chamar o método público para acessar a variável privada:

      this.velocimetro = function(){
      return ‘A velocidade atual é: ‘ + this.velocidade();
      }

      OK? =P

      []’s

  6. hellow javiani,

    Muito bom os posts. Tenho lido muito sobre javascript e confesso a sintaxe que vc utiliza, torna o codigo mais (muito mais) legivel. Agora, sobre o pattern singleton. Nao seria melhor redefinir o metodo Class do objeto Deus???

    var Deus = {
    Class :function(){
    var self = this,
    func = function(){ return self };

    Deus.getInstance = func;
    Deus.Class = func;
    },
    getInstance : function(){ return new Deus.Class }
    }

    • Oi Manoel tudo bem ? Realmente o seu método seria melhor pois não se perderia o .Class, se alguém o chamasse não daria erro.

      Mas este post foi só pra mostrar como seria usando o método que utilizo para fazer uma classe singleton clássica, é apenas para fins didáticos.

      A melhor forma de todas de se fazer um singleton em javascript é simplesmente declarar um objeto:

      var Singleton = {};

      😉

      Abraços

  7. Excelente Post!

    É a mais pura verdade que muitos profissionais banalizam o desenvolvimento do JavaScript OO, em contrapartida é notório em muitos aspectos as vantagens de usá-lo. Seja para uma simples organização estrutural como por exemplo um ganho na performance e é claro, um controle do fluxo das funções.

    Quanto ao POST a herança é sem dúvidas uma grande técnica, aliás, a Orientação à Objetos e seus conceitos só vieram para facilitar a vida do desenvolvedor, seja de qualquer linguagem.

    Novamente parabéns pelo POST!

Deixar mensagem para javiani Cancelar resposta