17 de mai. de 2012

Orientação à Objetos – Métodos e atributos estáticos

O código original – Orientado à objetos ou PHP estruturado?

O código original já estava dentro de uma classe, mas não faria diferença nenhuma se fosse PHP estruturado… não lembro exatamente o nome dos métodos/variáveis, mas o restante está igualzinho:
01class cFileType {
02
03    function fImage($type) {
04        switch($type) {
05            case 'jpg':
06            $bool = true;
07            break;
08            case 'png':
09            $bool = true;
10            break;
11            case 'gif':
12            $bool = true;
13            break;
14            default:
15            $bool = false;
16            break;
17        }
18        return $bool;
19    }
20
21}
A primeira mudança foi trocar esse switch, que não está fazendo nada além de definir o valor da variável $bool como true ou false se o $type for um dos valores válidos (jpg, png ou gif)… Nada melhor então do que usar a função in_array():
1class cFileType {
2
3    function fImage($type) {
4        return in_array($type, array('jpg', 'png', 'gif'));
5    }
6
7}
WOW! Reduzimos de 21 para 7 linhas… mas ainda assim, se fosse estruturado não teria diferença nenhuma.
Meu amigo me disse que essa classe seria para verificar os tipos de arquivos (extensões), por exemplo “se é uma imagem” ou “se é um doc”… Então criamos outro método para verificar DOCs:
01class cFileType {
02
03    function fImage($type) {
04        return in_array($type, array('jpg', 'png', 'gif'));
05    }
06
07    function fDoc($type) {
08        return in_array($type, array('doc', 'docx'));
09    }
10
11}

Atributos, melhor tê-los

O código está melhorando, mas ainda assim tem algo errado… não é responsabilidade dos métodos fImage e fDoc saber a lista de extensões válidas… isso não deveria pertencer à classe como um todo e poder ser reutilizado?
01class cFileType {
02
03    public $image = array('jpg', 'png', 'gif');
04
05    public $doc = array('doc', 'docx');
06
07    function fImage($type) {
08        return in_array($type, $this->image);
09    }
10
11    function fDoc($type) {
12        return in_array($type, $this->doc);
13    }
14
15}

Atributos e métodos estáticos

Agora sim está parecendo uma classe normal, com atributos e métodos… Aí percebi que de orientada à OBJETOS essa classe não tem nada! Não estamos trabalhando com objetos.. O uso atual dessa classe seria assim:
1$cFileType = new cFileType();
2if ($cFileType->fImage('jpg')) {
3    // É uma imagem válida
4}
Eu não trabalho o objeto $cFileType, apenas instancio e utilizo um único modo… então vamos economizar um pouco de memória, transformando os métodos em métodos estáticos:
01class cFileType {
02
03    public static $image = array('jpg', 'png', 'gif');
04
05    public static $doc = array('doc', 'docx');
06
07    static function fImage($type) {
08        return in_array($type, self::$image);
09    }
10
11    static function fDoc($type) {
12        return in_array($type, self::$doc);
13    }
14
15}
E agora a utilização ficou um pouco mais simples:
1if (cFileType::fImage('jpg')) {
2    // É uma imagem válida
3}
Sendo que você ainda pode usar o cFileType::image (pra ter uma lista de imagens válidas) em qualquer parte da sua aplicação sem instanciar a classe.

Reutilização de código

Segundo a abordagem DRY, não devemos nos repetir… Por isso aquele in_array() começou a me incomodar… Vai que você está verificando 30 tipos diferentes de arquivos, todos os métodos fazendo exatamente a mesma coisa… mas aí você decide mudar o in_array() pra algo mais eficiente ou aceitar até o caminho absoluto de um arquivo… vai mudar em 30 métodos na mão?
A responsabilidade de verificar se o valor $type tá dentro de uma “lista” válida não é dos métodos fImage e fDoc.. então vamos delegar:
01class cFileType {
02
03    public static $image = array('jpg', 'png', 'gif');
04
05    public static $doc = array('doc', 'docx');
06
07    static function fType($type, $list) {
08        return in_array($type, $list);
09    }
10
11    static function fImage($type) {
12        return self::fType($type, self::$image);
13    }
14
15    static function fDoc($type) {
16        return self::fType($type, self::$doc);
17    }
18
19}
Agora se precisarmos mudar essa lógica de verificar se o $type tá dentro de uma “lista” válida, só vamos precisar mudar em um lugar só.

cFileType? fType? fImage? O resultado final

Temos que concordar que os nomes de classe e métodos escolhidos pelo meu amigo não são os mais intuitos… Então como uma modificação final, sugiro a seguinte classe devidamente renomeada:
01class FileType {
02
03    public static $image = array('jpg', 'png', 'gif');
04
05    public static $doc = array('doc', 'docx');
06
07    public static function isTypeInList($type, $list) {
08        return in_array($type, $list);
09    }
10
11    public static function isImage($type) {
12        return self::isTypeInList($type, self::$image);
13    }
14
15    public static function isDoc($type) {
16        return self::isTypeInList($type, self::$doc);
17    }
18
19}
Com uma utilização bem simples e intuitiva:
1if (FileType::isImage('jpg')) {
2    // É uma imagem válida
3}


Pra quem quiser ver o código completo da classe final, com os métodos comentados: https://gist.github.com/1338259


Fonte:  Thiago Belem: Orientação à Objetos – Métodos e atributos estáticos

Nenhum comentário:

Postar um comentário