Design Patterns, fugindo dos ifs com o padrão Strategy

Bom, faz um bom tempo que eu não posto algo por aqui. Desta vez resolvi falar um pouco sobre padrões de projeto, ou design patterns. Bom, eu não vou falar sobre o que são os padrões de projetos, ou muito menos ficar defendendo a sua utilização. Nesses posts eu vou criar uma situação em que poderíamos utilizar um ou mais padrões para simplificar e/ou deixar mais elegante a nossa solução.

O Criador de Hello World

 
Vamos criar um programa simples que dado uma linguagem de programação, ele nos apresenta como fica um Hello World nesta linguagem. Vamos definir que poderemos escolher entre as linguagens Java, C#, Ruby, Python e Go!. Vamos ao código? A primeira vista, poderíamos ter um método que recebe a linguagem como parâmetro e nos mostra como ficaria o hello world na linguagem em questão, certo?

    public class HelloWorldWriter
    {
        public void Write(string language)
        {
            var hello = string.Empty;

            if (language.Equals("CSharp"))
            {
                hello += "public static void Main()" + Environment.NewLine;
                hello += "{" + Environment.NewLine;
                hello += "  System.Console.WriteLine(\"Hello, World!\");" + Environment.NewLine;
                hello += "}";
            } 
            else if (language.Equals("Java"))
            {
                hello += "public static void main(String args[]) {" + Environment.NewLine;
                hello += "  System.out.println(\"Hello, World!\");" + Environment.NewLine;
                hello += "}";
            } 
            else if (language.Equals("Ruby"))
            {
                hello += "puts 'Hello, World!'";
            }
            else if (language.Equals("Python"))
            {
                hello += "print \"Hello, World!\"";
            }
            else if (language.Equals("Go"))
            {
                hello += "package main" + Environment.NewLine;
                hello += "import \"fmt\"" + Environment.NewLine;
                hello += "func main() {" + Environment.NewLine;
                hello += "  fmt.Println(\"Hello, World!\")" + Environment.NewLine;
                hello += "}";
            }

            Console.WriteLine(hello);
        }
    }

Depois, bastaria chamar a classe HelloWorldWriter e pronto, certo?

    class Program
    {
        static void Main(string[] args)
        {
            new HelloWorldWriter().Write("CSharp");
            new HelloWorldWriter().Write("Go");

            Console.ReadKey();
        }
    }

E o nosso resultado seria este:

hello

Até agora tudo bem, definimos uns 4/5 ou casos, está funcionando e tudo mais. Mas já temos alguns problemas em nosso código, certo? Temos uma classe chamada HelloWorldWriter, que deveria escrever um hello world, dada a linguagem em questão, e não ser obrigada a saber como todas as linguagens de programação são. Note também, que dentro dos poucos casos que colocamos, nosso código já ficou bem feio não? vários ifs / else ifs, e caso no futuro queiramos adicionar mais linguagens, teremos que entrar lá e colocar mais um if no final.
O que podemos fazer agora para melhor o nosso código então? Poderíamos criar uma classe para cada linguagem, e cada uma delas saberia como escrever o seu Hello World, certo? Vamos lá então:

    public class CSharp
    {
        public string WriteHelloWorld()
        {
            var hello = string.Empty;
            hello += "public static void Main()" + Environment.NewLine;
            hello += "{" + Environment.NewLine;
            hello += "  System.Console.WriteLine(\"Hello, World!\");" + Environment.NewLine;
            hello += "}";

            return hello;
        }
    }

    public class Go
    {
        public string WriteHelloWorld()
        {
            var hello = string.Empty;
            hello += "package main" + Environment.NewLine;
            hello += "import \"fmt\"" + Environment.NewLine;
            hello += "func main() {" + Environment.NewLine;
            hello += "  fmt.Println(\"Hello, World!\")" + Environment.NewLine;
            hello += "}";

            return hello;
        }
    }

    public class Java
    {
        public string WriteHelloWorld()
        {
            var hello = string.Empty;
            hello += "public static void main(String args[]) {" + Environment.NewLine;
            hello += "  System.out.println(\"Hello, World!\");" + Environment.NewLine;
            hello += "}";

            return hello;
        }
    }

    public class Python
    {
        public string WriteHelloWorld()
        {
            return "print \"Hello, World!\"";
        }
    }

    public class Ruby
    {
        public string WriteHelloWorld()
        {
            return "puts 'Hello, World!'";
        }
    }

E agora, a nossa classe HelloWorldWriter pode ficar um pouco mais enxuta:

    public class HelloWorldWriter
    {
        public void Write(string language)
        {
            var hello = string.Empty;

            if (language.Equals("CSharp"))
            {
                hello = new CSharp().WriteHelloWorld();
            } 
            else if (language.Equals("Java"))
            {
                hello = new Java().WriteHelloWorld();
            } 
            else if (language.Equals("Ruby"))
            {
                hello = new Ruby().WriteHelloWorld();
            }
            else if (language.Equals("Python"))
            {
                hello = new Python().WriteHelloWorld();
            }
            else if (language.Equals("Go"))
            {
                hello = new Go().WriteHelloWorld();
            }

            Console.WriteLine(hello);
        }
    }

Note que melhoramos as responsabilidades aqui, mas a classe HelloWorldWriter ainda possui inúmeros ifs. Como poderíamos melhorar agora? Note que criamos diversas classes, e todas elas tem algo em comum. Elas representam uma linguagem de programação certo? Podemos melhorar as coisas aqui e criar um contrato formal dizendo que todas elas são linguagens de programação. Como criamos um contrato deste tipo? Criamos uma interface certo?

    public interface IProgrammingLanguage
    {
        string WriteHelloWorld();
    }

E agora, todas as nossas linguagens de programação podem assinar este contrato dizendo que elas são uma linguagem de programação e que sabem como escrever um hello world.

    public class Ruby : IProgrammingLanguage
    {
        public string WriteHelloWorld()
        {
            return "puts 'Hello, World!'";
        }
    }

    public class Python : IProgrammingLanguage
    {
        public string WriteHelloWorld()
        {
            return "print \"Hello, World!\"";
        }
    }

    public class Java : IProgrammingLanguage
    {
        public string WriteHelloWorld()
        {
            var hello = string.Empty;
            hello += "public static void main(String args[]) {" + Environment.NewLine;
            hello += "  System.out.println(\"Hello, World!\");" + Environment.NewLine;
            hello += "}";

            return hello;
        }
    }

    public class Go : IProgrammingLanguage
    {
        public string WriteHelloWorld()
        {
            var hello = string.Empty;
            hello += "package main" + Environment.NewLine;
            hello += "import \"fmt\"" + Environment.NewLine;
            hello += "func main() {" + Environment.NewLine;
            hello += "  fmt.Println(\"Hello, World!\")" + Environment.NewLine;
            hello += "}";

            return hello;
        }
    }

    public class CSharp : IProgrammingLanguage
    {
        public string WriteHelloWorld()
        {
            var hello = string.Empty;
            hello += "public static void Main()" + Environment.NewLine;
            hello += "{" + Environment.NewLine;
            hello += "  System.Console.WriteLine(\"Hello, World!\");" + Environment.NewLine;
            hello += "}";

            return hello;
        }
    }

Agora que melhoramos o nosso código, e utilizamos a orientação a objetos para nos ajudar a definir responsabilidades, etc., Podemos alterar a nossa classe HelloWorldWriter para que espere agora uma linguagem de programação, e não apenas uma string:

    public class HelloWorldWriter
    {
        public void Write(IProgrammingLanguage language)
        {
            Console.WriteLine(language.WriteHelloWorld() + Environment.NewLine);
        }
    }

E a chamada do nosso HelloWorldWriter fica deste jeito:

    class Program
    {
        static void Main(string[] args)
        {
            new HelloWorldWriter().Write(new CSharp());
            new HelloWorldWriter().Write(new Go());
            new HelloWorldWriter().Write(new Python());
            new HelloWorldWriter().Write(new Ruby());
            new HelloWorldWriter().Write(new Java());

            Console.ReadKey();
        }
    }

Note como agora, independente da quantidade de linguagens de programação que venhamos a implementar, nosso código não precisará ser alterado.

Por hoje é só 😉

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s