Se você que já deu manutenção em um site estruturado (aquele que o programador coloca tudo em um só arquivo) sabe o que é complexidade.
Muitas vezes eu acho que quem criou as funções, as criou para que o seu código ficasse menos complexo de dar manutenção e assim ele pudesse ganhar tempo.
No exemplo a seguir, vou mostra um código que é adicionado por muitos para verificar se o usuário é administrador.
O Código abaixo eu achei em um blog na internet
<?php require 'conexao.php'; require 'funcoes.php'; $id_usuario_logado = intval($_SESSION['id_usuario_logado']); // se na sessão não ouver o ID, não esta logado if( $_SESSION['id_usuario_logado'] == 0 ) header('Location login.php'); $sql = 'SELECT * FROM usuário WHERE usuario_id = '.$id_usuario_logado; $result = mysql_query( $sql ); $usuario = mysql_fetch_row( $result ); // se o usuário não for administrador, deverá voltar para a home if( $usuario['usuario_permissao'] != 'admin' ) header('Location login.php'); ?><!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="UTF-8"> <title>KrauSoft</title> </head> . . .
Vocês pode não acreditar, mais existe muitos sites com um código medonho deste. Eu que trabalho muito com EAD e instalando o MoodleAdmin vejo isso quase todos os dias.
Você percebeu que existe um BUG dos grandes no código acima? Olhe de novo e tente achar!
O BUG é simples. oheader()
só adiciona o cabeçalho, mais não interrompe a execução e esta página será toda processada e enviada ao browser do usuário. Então um hacker poderá capturar este HTML e também poderá interagir com toda a página.
Agora, imagine que este sistema tenha 200 páginas de acesso restrito á administradores, e que o cliente pediu que os "gerentes" também tenham acesso a estas páginas. Viu o que é complexo.
Agora, além de ser ultra POG o que foi feito acima, e ultra complexo de se dar manutenção, também é esteticamente feio.
Sendo bem simples, a complexidade ciclomática é uma métrica do número de caminhos possíveis no seu código. fonte Wikipédia.
A primeira regra de funções é que elas devem ser pequenas. A segunda regra de funções é que elas devem ser ainda menores.
— Uncle Bob
Então, quanto menor for sua função, menor será a complexidade em dar manutenção no seu código.
Então, para melhorar nosso exemplo, vou criar uma classe Usuario
e separar o máximo possível nosso código:
class Usuario{ public $usuario_id; public $usuario_nome; public $usuario_permissao; /** * Variável auxiliar do método singleton * * @var Usuario */ private static $_usuarioLogado; /** * @return null|Usuario */ public static function getUsuarioLogado() { if( self::$_usuarioLogado != null ) return self::$_usuarioLogado; $id_usuario_logado = intval($_SESSION['id_usuario_logado']); Usuario::findById( $id_usuario_logado ); return self::$_usuarioLogado; } /** * Retorna o usuário basead no ID * * @param int $usuario_id * @return null|Usuario */ public static function findById($usuario_id) { $sql = 'SELECT * FROM usuário WHERE usuario_id = '.$usuario_id; $row = ConnectDb::returnOneRow( $sql ); return Usuario::converteArrayToUsuario( $row ); } /** * Valida se o usuário logado é administrador * * @return bool */ public static function isLogadoAdmin() { $usuario = Usuario::getUsuarioLogado(); if( $usuario == null ) return false; if( $usuario->usuario_permissao == 'admin' ) return true; return false; } /** * converte um Array vindo do banco de dados, * para um Objeto do tipo Usuário * * @param array $row * @return null|Usuario */ public static function converteArrayToUsuario($row) { if( $row == null ) return null; $usuario = new Usuario(); $usuario->usuario_id = $row['usuario_id']; $usuario->usuario_nome = $row['usuario_nome']; $usuario->usuario_permissao = $row['usuario_permissao']; return $usuario; } }
Então, o código na página passará a ser apenas assim:
<?php if(Usuario::isLogadoAdmin()) Header::location('login.php'); ?><!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="UTF-8"> <title>KrauSoft</title> </head> . . .
E a chamada do header
, eu passei a usar a classe Header
. Implementei o método notFound()
também:
class Header { public static function location($url) { ob_clean(); header('Location: ' . $url); die(); } public static function notFound() { header('HTTP/1.0 404 Not Found'); } }
Agora não há como se esquecer de colocar o die()
e o ob_clean()
nas chamadas do header('Location: …')
. Ou seja, menor a possíbilidade de ter Bugs.
Mais, apesar de escrever muitas linhas a mais, o nosso código esta muito mais simples, pois sempre que recusar saber quem é o usuário logrado é só chamar Usuario::getUsuarioLogado()
, ou se tiver o ID do usuário e precisar saber do nome é só chamar Usuario::findById($id)
.
Veja que nossa classe Usuario
tem vários métodos pequenos, e que podem ser usados em muitos casos. E, cada um destes métodos com baixa complexidade de manutenção.
Veja abaixo a função PHP qe criei:
function umaFuncaoQualquer($umValor) { $resultado = 0; if ($umValor > 9) $resultado += 1; return $resultado; }
Este código pode percorrer dois caminhos. Se o $umValor
possuir o valor maior que 9, ele entará no IF, caso contrário passará direto. Vamos então ver esta segunda função:
function outraFuncaoQualquer($umValor) { $resultado = 0; if ($umValor > 9) $resultado += 1; if ($umValor < 15) $resultado += 2; return $resultado; }
Já a execução desta função poderá percorrer 4 caminhos diferentes. Só com a adição de um novo IF, a complexidade Ciclomática dobra. Se eu apenas adicionar mais um IF, a complexidade passa a se 8.
Entenderam como é "complexo" evitar Bugs?
Em tese, recomenda que a complexidade ciclomática nunca ultrapasse 10 pontos. Sendo que 10 já é um um valor muito alto. Veja este PDF em inglês.
Segundo a pesquisa que a aivosto.com fez, uma correção aplicada em um método com complexidade ciclomática 25 tem 20% de chances de introduzir um novo bug na sua aplicação. E acredito, pois já aconteceu comigo…
Então Bad Fix é a probabilidade que seu código terá de produzir outro BUG, ao ser corrigido um BUG.
Eles então montaram a seguinte tabela:
Complexidade Ciclomática | bad fix |
---|---|
1 - 10 | 5% |
10 - 20 | 20% |
maior que 50 | 40% |
perto de 100 | 60% |
Entendeu?
Eu fico pensando, que bagunça deve ser o ruWindows para todo dia vir um monte de correções de BUG!!!!!
Saber, eu acho que todo mundo sabe, mais agora é pensar nisso e tomar mais cuidado para que da próxima vês programar cada vês, métodos menores e menores.
Antes de alguém comentar, hehehehe, vou aqui descrever meu caso. No projeto wowzaadmin.com eu, antes de começar coloquei no papel todo o sistema e todas as funcionalidades e como cada uma destas funcionalidades iria trabalhar.
O sistema é complexo e tem milhares de formas diferentes de resposta e de analise de dados e toda a comunicação com o servidor Wowza é feita em XML que retorna muitos dados. Então analisar estas centenas de dados que retornam iria, em tese, gerar um monte de Bugs.
Essa era a maior preocupação. Os Bugs. Meu medo seria que o sistema chegasse a um ponto que os Pogs gerados para corrigir os Bugs fossem tantos que o sistema poderia ser abandonado.
Imagine este sistema, aonde a mesma página que analisa o trafego de transmissões ao-vivo, também analisa o trafego de dados de vídeos gravados, câmeras IP, e re-transmissão de rádios ShoutCast. Será que eu estava certo em ficar preocupado?
Então comecei, antes de escrever uma sequer linha de código, a separar o projeto em grandes partes e cada uma destas partes separar o máximo possível até chegar que cada parte fosse pequena e simples de dar manutenção. Assim, por exemplo, só na parte de controle e analise dos dados do Wowza, o sistema possui 107 classes com 930 métodos.
E, para minha surpresa, muito poucos Bugs…