Pular para o conteúdo principal

Desacoplamento e composição

Ho ho ho! Feliz Natal !!!

Estava pensando em fazer algo com o tema natalino, mas que também fosse interessante para o dia-a-dia e me veio uma coisa na cabeça: Desenhar com caracteres ASCII uma imagem. Na hora pensei num algoritmo, formato e tudo o mais, e em seguida comecei a fazer algo que é muito natural para mim, desacoplar as funções.

Primeiro passo - definindo um formato


O formato que eu imaginei é
Sendo que quantidade = 0 seria quebra de linha. Vejamos o exemplo:

[code language="cpp"]
/* Xmas tree
**
* *
* *
* *
* *
************
****
****
****
*/
string img =
"5 2*00"
"4 1*2 1*00"
"3 1*4 1*00"
"2 1*6 1*00"
"1 1*8 1*00"
"<*00"
"4 4*00"
"4 4*00"
"4 4*00";
[/code]



E agora precisamos de uma função para carregar e uma struct

[code language="cpp"]
struct command {
command(char count, char pencil) :
count(count), pencil(pencil) { }
char count;
char pencil;
};

vector<command> load(const string &img) {
vector<command> v;
for(auto it = img.begin(); it != img.end(); ++it) {
char count = (*it) - '0';
++it;
char pencil = *it;
v.push_back(command(count, pencil));
}
return std::move(v);
}
[/code]

E uma para fazer o desenho a partir de um vector

[code language="cpp"]
void draw_cmd(const command &cmd) {
if( cmd.count == 0 ) {
putc(10, stdout);
}
for(int i=0; i < cmd.count; ++i) {
putc(cmd.pencil, stdout);
}
}

void draw(const vector<command> &img) {
for(auto &c : img)
draw_cmd(c);
}
[/code]

Temos o nosso código pronto, mas tem um problema grave! Este código é extremamente acoplado. Vou listar o que está acoplado no que:

load -> string, vector, command
draw -> draw_cmd, vector, command
draw_cmd -> putc, command

Existirem acoplamentos, não significa necessariamente que eu precise acabar com todos, mas todo o código que puder ser genérico, deve ser genérico. Vou explicar algumas razões para deixar o código desacoplado.

  1. Se eu resolver ao invés de escrever no terminal com putc, gerar uma string ou um HTML, ou qualquer outra coisa, não consigo.

  2. Se eu quiser, ao invés de command, usar algo com cor, por exemplo, não consigo.

  3. Eu não consigo, por exemplo, gerar um plotter que desenhe espelhado.


Vamos começar a soltar as amarras!

Para mostrar o quanto um exemplo simples desse pode ser extensível, vamos usar as funções draw_cmd e draw. No caso, vamos remover o putc e passar um std::function

[code language="cpp"]
void plot(char c) { putc(c, stdout); }

void draw_cmd(const command &cmd, function<void(char)> f_plot) {
if( cmd.count == 0 ) {
f_plot(10);
}
for(int i=0; i < cmd.count; ++i) {
f_plot(cmd.pencil);
}
}
[/code]

Agora podemos passar a função plot ou qualquer outra para o draw_cmd. Vamos agora tirar a função draw_cmd e command da função draw

[code language="cpp"]
template<typename SequenceContainer, typename DrawFunction>
void draw(SequenceContainer &img, DrawFunction f_draw) {
for(auto &c : img)
f_draw(c);
}
[/code]

Simples e fácil. Para completar, como ficaria a chamada ?

[code language="cpp"]
vector<command> v = load(img);
auto console_draw = [](command cmd) {
draw_cmd(cmd, plot);
};
draw(v, console_draw);
[/code]

E ainda dá para brincar um pouco mais:

[code language="cpp"]
auto inverse_draw = [](command cmd) {
if( cmd.pencil == ' ' )
cmd.pencil = '*';
else
cmd.pencil = ' ';
draw_cmd(cmd, plot);
};

auto funny_draw = [](command cmd) -> void {
if( cmd.pencil != ' ' ) {
if( cmd.count > 2 )
cmd.pencil = '=';
else
cmd.pencil = '^';
}
draw_cmd(cmd, [](char c) -> void {
if( c == '^' )
cout << "\033[13;32m" << c;
else if( c == '=' )
cout << "\033[12;33m" << c;
else
cout << c;
});
};
[/code]

Vejam só como ficaram:
screen-shot-2016-12-24-at-1-59-43-am

screen-shot-2016-12-24-at-1-57-29-am

Espero que tenham gostado !

Ah, o tema natalino... Essa é uma árvore de natal !

Fonte:

  • https://github.com/SimplyCpp/examples/blob/master/draw.cpp

  • https://github.com/SimplyCpp/examples/blob/master/draw2.cpp


Relacionado:

  • https://simplycpp.com/2016/01/01/composicao-parte-1-com-exemplo-classico-do-unix/

Comentários

Postagens mais visitadas deste blog

Mestre Iota

Iota é a nona letra do alfabeto grego, ela é equivalente à letra i do nosso alfabeto. Por convenção ou hábito, utilizamos a letra i na programação para indicar algum tipo de incrementador, como por exemplo, em um for-loop . https://gist.github.com/fabiogaluppo/a23894ae743f7dd29274 Curiosamente, iota , como identificador, também é utilizado na programação para indicar uma sequência finita e consecutiva de números inteiros, como por exemplo, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]. Inclusive, originalmente na STL existia a função iota , inspirada pela linguagem de programação APL , você pode conferir neste link: http://www.sgi.com/tech/stl/iota.html

Valores Aleatórios Simplificados

A partir do C++ 11, foi introduzido o header <random>  com diversos facilitadores para suporte de geração de números aleatórios. A produção destes números é feita através da combinação de duas categorias de objetos: os geradores e os distribuidores. Os geradores, são responsáveis pela geração dos números, e os distribuidores são responsáveis pela transformação dos números gerados em algum tipo de distribuição de probabilidade.  Como por exemplo, uma distribuição normal (aquela da Gaussiana) ou uma distribuição de Pareto (aquela do 80-20). As opções não faltam, como você pode ver nas referências, por exemplo:   http://www.cplusplus.com/reference/random/ ou  http://en.cppreference.com/w/cpp/header/random .

Policy-based design: log writer

Policy-based design Vamos neste artigo dar mais uma pincelada no Policy-based design . Vamos fazer como exemplo uma classe de log. Como este é só um exemplo, não vamos considerar múltiplos parâmetros no log, mas somente uma string, assim não fugiremos do assunto. Uma das coisas mais importantes neste tipo de design é o desacoplamento. Ele é uma excelente alternativa ao uso de interfaces por duas razões: Não gera chamadas virtuais (ou um nível de indireção em tempo de execução) Duck typing ( https://pt.wikipedia.org/wiki/Duck_typing ) Eu gosto bastante desse tipo de design, já usado aqui: http://simplycpp.com/2016/02/05/leitura-de-configuracao-em-c/