OOP: Herança

Herança? O que é isso?

Um dos conceitos mais importantes da OOP, a herança tem um papel essencial no que se diz respeito ao reaproveitamento do código. Ela permite que, dada uma classe A, possamos herdar suas qualidades e comportamentos em uma classe B.

Da mesma forma que você herdou, geneticamente, os olhos de seu pai, ou o cabelo da sua mãe, a classe B irá herdar todas as qualidades (atributos) da classe A, mas ainda assim serão objetos diferentes.

No mundo da programação

Para exemplificar, vamos dizer que temos uma simples classe de usuário:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package
{
    public class User
    {
        public var username:String = null;
        public var password:String = null;
       
        public function User(username:String, password:String)
        {
            this.username = username;
            this.password = password;
        }
       
        public function doLogin():void
        {
            trace("User " + username + " has logged in.");
        }
       
        public function doLogout():void
        {
            trace("User " + username + " has logged out.");
        }

    }
   
}

A classe define 2 propriedades e 2 métodos, que seriam as características e comportamentos de um simples usuário dentro de um sistema.

Certo, mas, eu preciso de um usuário administrativo no meu sistema, com comportamentos iguais ao de um usuário simples, mas com mais coisas agregadas.
É aí que entra a herança, pois, ao invés de fazermos isso:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package
{
    public class AdminUser
    {
        public var username:String = null;
        public var password:String = null;
       
        public function AdminUser(username:String, password:String)
        {
            this.username = username;
            this.password = password;
        }
       
        public function doLogin():void
        {
            trace("User " + this.username + " has logged in.");
        }
       
        public function doLogout():void
        {
            trace("User " + this.username + " has logged out.");
        }
       
        public function editPage(page:String):void
        {
            trace("The admin user " + this.username + " has just edited the " + page + " page.");
        }

    }
   
}

Nós iremos reaproveitar o código da classe User, e teremos apenas isso:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package
{
    public class AdminUser extends User
    {
       
        public function AdminUser(username:String, password:String)
        {
            super(username, password);
        }
       
        public function editPage(page:String):void
        {
            trace("The admin user " + this.username + " has just edited the " + page + " page.");
        }

    }
   
}

Bem menos código para escrever, certo? Fora a manutenção, pois toda e qualquer modificação feita em User, reflete em AdminUser daqui pra frente, pois AdminUser é um User, apenas com umas características a mais.

Sacou a palavrinha extends ali em cima?

1
public class AdminUser extends User

Isso significa que a classe AdminUser herda a classe User. Sempre que ver a palavra chave extends, significa que a classe irá herdar uma outra classe.

Vamos fazer este pequeno exemplo rodar? Crie as classes User.as e AdminUser.as, e em seguida crie um FLA, e salve na mesma pasta das classes, apenas para facilitar o processo.

No FLA, insira o seguinte código:

1
2
3
4
5
6
7
8
var user1:User = new User("John", "123456");
var user2:AdminUser = new AdminUser("Robert", "123456");

user1.doLogin();
user2.doLogin();
user2.editPage("Home");
user1.doLogout();
user2.doLogout();

O resultado será esse:

User John has logged in.
User Robert has logged in.
The admin user Robert has just edited the Home page.
User John has logged out.
User Robert has logged out.

Como pode ver, AdminUser contém todos os métodos e propriedades de User, mas com um método a mais, editPage().

Herança é um processo muito comum, tão comum que é muito provável que você já tenha feito isso mil vezes e nunca tenha percebido. A diferença é que agora você entende o que é e o porque fazemos.

No Flash, todo elemento visual herda uma classe chamada DisplayObject. Essa classe define os métodos e propriedades que um objeto que será exibido deve conter. Então, basicamente todo o objeto visual que iremos criar deve herdar essa classe, ou uma de suas classes derivadas.

Por exemplo, a classe Sprite herda a DisplayObject, e a tão conhecida classe MovieClip herda a Sprite. Consequentemente, tudo que a classe Sprite contém, a classe MovieClip também terá.

Um exemplo mais prático

Vamos a um exemplo mais prático do que usuários imaginários em um sistema imaginário. Podemos criar um elemento simples e de grande utilidade por todos, um Botão.

Um botão, a principio, tem um funcionamento bem simples, algo como um evento de click. Sabendo isso, podemos definir a nossa classe para botões simples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/**
 * (c) 2009 - mozartpetter.com
 */

package app.display
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
   
    /**
     * A classe BasicButton define um botão básico.
     */

    public class BasicButton extends Sprite
    {
        // Link do botão.
        public var link:String = null;
       
        /**
         * Cria uma nova instância de BasicButton.
         */

        public function BasicButton()
        {
            super();
           
            // Adicionando evento ADDED_TO_STAGE.
            this.addEventListener(Event.ADDED_TO_STAGE, this.handleAddedToStage);
        }
       
        /**
         * Define as configurações iniciais do botão.
         */

        protected function setup():void
        {
            // Definindo modo botão.
            this.buttonMode = true;
           
            // Adicionando evento CLICK.
            this.addEventListener(MouseEvent.CLICK, this.handleClick);
        }
       
        /**
         * @private
         * Handler do evento ADDED_TO_STAGE.
         *
         * @param event Objeto Event.
         */

        private function handleAddedToStage(event:Event):void
        {
            // Configurando botão.
            this.setup();
        }
       
        /**
         * @private
         * Handler do evento CLICK.
         *
         * @param event Objeto MouseEvent
         */

        private function handleClick(event:MouseEvent):void
        {
            trace("Indo para o link: " + this.link);
        }
       
    }
   
}

A classe parece bem simples, temos uma propriedade link, que irá definir o endereço para onde o nosso botão irá, e 2 handlers de evento, 1 para quando o botão for adicionado ao palco (importante para evitar erros, explico melhor futuramente), e 1 para o click que o objeto irá sofrer.

Vamos botar esse carinha para funcionar, salve a nossa classe em app.display.BasicButton.as e em seguida crie um FLA, mas desta vez o coloque na raiz, junto ao pacote app.

Desenhe um retângulo, da medida que lhe der na telha, para ser o nosso botão.

Desenhando retângulo no palco

Desenhando retângulo no palco

Transforme esse retângulo em um símbolo, e associe a classe BasicButton a ele.

Convertendo retângulo em um símbolo

Convertendo retângulo em um símbolo

Dê o nome de instância de basicButton ao elemento. No painel de ações, apenas insira a seguinte linha:

1
this.basicButton.link = "http://www.mozartpetter.com";

Feito isso, basta compilar e ver o resultado.

Agora, e se eu quiser um botão, mas com efeitos ao passar o mouse em cima dele? Algo como, mudar a cor por exemplo, algo simples, mas bacana?

O primeiro passo é criarmos uma classe que herde a nossa classe BasicButton:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * (c) 2009 - mozartpetter.com
 */

package app.display
{
    /**
     * A classe AnimatedButton define um botão animado.
     */

    public class AnimatedButton extends BasicButton
    {
        /**
         * Cria uma nova instância de BasicButton.
         */

        public function AnimatedButton()
        {
            super();
        }
       
    }
   
}

Salve o arquivo como AnimatedButton.as no mesmo pacote da classe anterior.

Agora, vamos duplicar o símbolo que já temos em nosso FLA, e associar esta nova classe a ele.

Duplicando símbolo na biblioteca

Duplicando símbolo na biblioteca

Editando o símbolo duplicado

Editando o símbolo duplicado

Arraste este no símbolo para o palco, e coloque-o ao lado da instância de BasicButton existente.

Dê o nome de instância de animatedButton para o objeto, e adicione um novo link no painel de ações, logo abaixo do link existente.

Se você compilar o filme, verá que o novo botão funciona exatamente como o outro. Ou seja, a nossa classe AnimatedButton herdou o comportamento da classe BasicButton.

O próximo passo é adicionarmos novas funcionalidades no nosso botão. Vamos fazer com que as cores dele se alterem ao passarmos o mouse sobre o botão, e ao retirarmos o mouse.

Sobreescrevendo métodos de uma superclasse

Quero fazer um parênteses neste ponto, e falar sobre um recurso que utilizamos muitas vezes ao herdarmos uma classe: a sobreescrita de métodos.

A sobreescrita de métodos, ocorre quando você necessita de uma implementação diferenciada de um método específico da classe, que já existe na superclasse (ou classe pai).

Vamos pegar como exemplo o método setup da superclasse BasicButton. Se na nossa classe (ou subclasse) AnimatedButton fizermos isto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
 * (c) 2009 - mozartpetter.com
 */

package app.display
{
    /**
     * A classe AnimatedButton define um botão animado.
     */

    public class AnimatedButton extends BasicButton
    {
        /**
         * Cria uma nova instância de BasicButton.
         */

        public function AnimatedButton()
        {
            super();
        }
       
        /**
         * Define as configurações iniciais do botão.
         */

        protected function setup():void
        {
           
        }
       
    }
   
}

O Flash irá exibir o seguinte erro:

1024: Overriding a function that is not marked for override.

Isso porque o método já existe na superclasse, e se quisermos alterar a forma como ele se comporta, temos que sobreescrevê-lo.

1
2
3
4
5
6
7
/**
 * Define as configurações iniciais do botão.
 */

override protected function setup():void
{
   
}

A palavra chave override faz toda a diferença, pois ela indica que o método está sendo sobreescrito, ou seja, uma nova implementação será utilizada nele, ao invés da implementação do método da superclasse.

Mas, caso você apenas queira adicionar novas instruções, sem apagar as implementações feitas na superclasse, basta chamar o método usando a palavra chave super.

1
2
3
4
5
6
7
8
/**
 * Define as configurações iniciais do botão.
 */

override protected function setup():void
{
    // Chamando método da superclasse.
    super.setup();
}

Se você testar o filme agora, estará tudo funcionando normalmente, mas se testar o filme sem a chamada ao método super, verá que o comportamento do botão irá mudar, pois as instruções que ali existiam antes, não existem mais.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/**
 * (c) 2009 - mozartpetter.com
 */

package app.display
{
    import flash.events.MouseEvent;
    import flash.geom.ColorTransform;
   
    /**
     * A classe AnimatedButton define um botão animado.
     */

    public class AnimatedButton extends BasicButton
    {
        // Cor do botão ao passar o mouse.
        private var _overColor:uint = 0x0099CC;
       
        // Cor do botão no estado normal.
        private var _upColor:uint = 0x0066CC;
       
        /**
         * Cria uma nova instância de BasicButton.
         */

        public function AnimatedButton()
        {
            super();
        }
       
        /**
         * Define as configurações iniciais do botão.
         */

        override protected function setup():void
        {
            // Chamando método da superclasse.
            super.setup();
           
            // Definindo cor inicial.
            this.setColor(this._upColor);
           
            // Adicionando método ROLL_OVER.
            this.addEventListener(MouseEvent.ROLL_OVER, this.handleRollOver);
        }
       
        /**
         * Altera a cor do botão para a cor especificada.
         *
         * @param color Valor hexadecimal da nova cor.
         */

        protected function setColor(color:uint):void
        {
            // Criando objeto ColorTransform.
            var colorTrans:ColorTransform = new ColorTransform();
           
            // Definindo cor.
            colorTrans.color = color;
           
            // Aplicando nova cor ao botão.
            this.transform.colorTransform = colorTrans;
        }
       
        /**
         * @private
         * Handler do evento ROLL_OUT.
         *
         * @param event Objeto MouseEvent.
         */

        private function handleRollOut(event:MouseEvent):void
        {
            // Alterando a cor do botão.
            this.setColor(this._upColor);
           
            // Removendo evento ROLL_OUT.
            this.removeEventListener(MouseEvent.ROLL_OUT, this.handleRollOut);
           
            // Adicionando evento ROLL_OVER.
            this.addEventListener(MouseEvent.ROLL_OVER, this.handleRollOver);
        }
       
        /**
         * @private
         * Handler do evento ROLL_OVER.
         *
         * @param event Objeto MouseEvent.
         */

        private function handleRollOver(event:MouseEvent):void
        {
            // Alterando a cor do botão.
            this.setColor(this._overColor);
           
            // Removendo evento ROLL_OVER.
            this.removeEventListener(MouseEvent.ROLL_OVER, this.handleRollOver);
           
            // Adicionando evento ROLL_OUT.
            this.addEventListener(MouseEvent.ROLL_OUT, this.handleRollOut);
        }
       
    }
   
}

Teste o FLA para ver o resultado.

Sobreescrevemos o método setup, e adicionamos a chamada ao novo método setColor, e também o evento ROLL_OVER, que será disparado sempre que o mouse passar sobre o botão.

Ao passarmos o mouse sobre o botão, ele muda de cor. As cores estão definidas nas propriedades lá em cima.

Como você pode ver, economizamos algum tempo, pois não tivemos de re-implementar toda a parte que está definida na classe BasicButton.

Mas nós ganhamos algo a mais com isso. A possibilidade de escolha. Só iremos utilizar a classe AnimatedButton, se precisarmos de um botão que tenha essas qualidades e comportamentos. Do contrário, iremos utilizar a classe BasicButton, que contém menos recursos, e por isso é mais leve.

Espero que tenham entendido o que significa Herança e o porque de utilizá-la. Vocês podem baixar os exemplos: OOP Herança - Exemplos.

Até a próxima.

7 Comments

  1. Muito bom post. Me deu uma visão melhor sobre o assunto.
    Obrigado!

  2. João Fernando

    Belíssimo Post.

    Ótimo blog.

    Parabéns!

    Foi o melhor tutorial para mim que estou migrando do AS2 para o AS3.

    Falow!

  3. Poxaaa!!! Muito bom seu post cara!!! Muito bom mesmo!!! O problema que vejo em outros posts, é que as pessoas não preocupam em passar exemplos práticos, mas você fez isso e muito bem! Meus parabéns!

    Agora duas perguntas sobre seu post:
    1º_ Na classe BasicButton vc utilizou está linha do método construtor:
    [code]this.addEventListener(Event.ADDED_TO_STAGE, this.handleAddedToStage);[/code]

    O pq da utilização dessa linha?

    2º_ O método super(), ele é utilizado para…?

    No mais cara, super!!! :P

  4. Mozart Petter

    @all Obrigado pelos comentários, o feedback é realmente gratificante. :)

    @tiago
    1 – Esta linha adiciona listener para o evento ADDED_TO_STAGE, assim, sempre que o objeto for adicionado ao palco eu inicializo valores e adiciono evento que só tem relevância se o objeto se encontrar no palco. Outro evento importante é o REMOVED_FROM_STAGE, para tu remover eventos que o objeto utilize enquanto no palco, mas que não serão necessários caso ele seja removido.

    2 – Falha minha, jurava que tinha explicado. A palavra super faz referência a superclasse, desta forma, quando executamos super(), é o mesmo que chamarmos o método construtor da superclasse. Se fizermos super.meuMetodo(), como foi mostrado em um dos exemplos, chamamos um método específico da superclasse.

  5. projetoecomm

    Legal, agora dá uns exemplos usando Polimorfismo e explicando quando se usa isso ou herança e aplicaçoes com design patterns.
    Bom trabalho.

  6. Grande explicações!
    Realmente um ótimo tutorial que eu utilizo até hoje, sim, faz tempo que encontrei o mesmo, quando me aparecem algumas dúvidas.
    Parabéns por este tutorial!!

  7. Muito legal cara,

    Espero que continue :D

    Flw

Leave a Comment