Switch para un enum vs Func delegate

La relación entre los enum y la cláusula switch es una de las más comunes. Tanto es así que muchas veces cuesta pensar en separarlos para encarar otra manera de usar un enum como diferenciador de categorías.
Pues bueno, la intención de este post es, al menos para este ejemplo de situación, romper con esta relación y apuntar a un diseño más orientado a objetos y menos al viejo desarrollo estructurado.

Nota: Antes que nada, deseo aclarar que el ejemplo que deseo plantear, quizás resulte algo largo pero quiero dejar plasmado como puede ir evolucionando el comportamiento de una clase según los requerimientos van evolucionando. Espero sepan disculpar la monotonía en pro de una explicación más clara.

Manos a la obra

Supongamos que se posee una clase simple que devuelve distintas carpetas:

public sealed class Folder
{
    private const string APP = "MyApp";
    private const string COMPANY = "MyCompany";
    private const string LOGS = "logs";

    public string GetLogFolder()
    {
        return LOGS;
    }

    public string GetApplicationFolder()
    {
        return APP;
    }

    public string GetCompanyFolder()
    {
        return COMPANY;
    }
}

Ahora se requiere que antes de retornar la ruta solicitada se agregue un control para validar que la ruta a retornar exista. Es decir, que si la ruta a devolver no existe, se la cree.
Un primer intento podría ser agregar a todos los métodos el control mencionado, pero nos causaría una repetición de código innecesaria. Para evitar eso, crearemos un enum que nos permita saber qué carpeta se nos solicita.

public enum FolderType
{
    Company,
    Application,
    Log
}

public sealed class Folder
{
    private const string APP = "MyApp";
    private const string COMPANY = "MyCompany";
    private const string LOGS = "logs";

    public Folder()
    {
    }

    public string GetFolder(FolderType folderType)
    {
        var folder = string.Empty;

        switch (folderType)
        {
            case FolderType.Company:
                folder = this.GetCompanyFolder();
                break;
            case FolderType.Application:
                folder = this.GetApplicationFolder();
                break;
            case FolderType.Log:
                folder = this.GetLogFolder();
                break;
        }

        if (!System.IO.Directory.Exists(folder))
            System.IO.Directory.CreateDirectory(folder);

        return folder;
    }

    private string GetLogFolder()
    {
        return LOGS;
    }

    private string GetApplicationFolder()
    {
        return APP;
    }

    private string GetCompanyFolder()
    {
        return COMPANY;
    }
}

Como se puede apreciar en este nuevo ejemplo, ahora agregamos un nuevo método que recibe como argumento un enum (el tipo de carpeta que deseamos obtener) y en base a este enum, tomamos la decisión que método ejecutar para obtener la carpeta solicitada para luego controlar que exista (y crearla en caso de inexistencia) para finalmente devolverla. Y además, se cambió el scope de las funciones para que ya no puedan ser llamadas individualmente.
Pues bien. Hemos llegado finalmente al motivo de este post: cómo reemplazar este switch y ejecutar el método que se desea:

public enum FolderType
{
    Company,
    Application,
    Log
}

public sealed class Folder
{
    private const string APP = "HSLifeSynchro";
    private const string COMPANY = "Hasar Sistemas";
    private const string LOGS = "logs";

    private Dictionary> functions = new Dictionary>();

    private Folder()
    {
        this.functions.Add(FolderType.Application, this.GetApplicationFolder);
        this.functions.Add(FolderType.Company, this.GetCompanyFolder);
        this.functions.Add(FolderType.Log, this.GetLogFolder);
    }

    public string Get(FolderType folderType)
    {
     // Se remplazó el switch por la ejecución del método correcto vía Dictionary.
        var directory = this.functions[folderType]();

        if (!Directory.Exists(directory))
            Directory.CreateDirectory(directory);

        return directory;
    }

    private string GetLogFolder()
    {
        return LOGS;
    }

    private string GetApplicationFolder()
    {
        return APP;
    }

    private string GetCompanyFolder()
    {
        return COMPANY;
    }
}

Para resolver este reemplazo, nos vamos a valer de un diccionario que utiliza como clave los tipos declarados en el enum y como elemento, en este caso, un Func<string> (una función que retorna string y no recibe parámetros como es nuestro caso. Aunque se podría utilizar otras firmas de métodos, cabe aclarar que todas las funciones deben cumplir como requerimiento con la misma firma entre ellas: retorno, cantidad y tipos de argumentos).
En el constructor de la clase Folder agregamos los tres elementos posibles al diccionario. De esta manera queda declarada implícitamente la decisión a ser tomada: qué método debe ejecutarse según el tipo de carpeta solicitado. Una mejor aproximación sería recibir esta lista como argumento del constructor y no crearla para cumplir con IoC y el principio de Single Responsability, pero como ya hice largo el ejemplo, no quería complicarlo aun más.
En el método Get (el que contenía al switch) ahora simplemente ejecuta una línea antes del control de existencia del directorio solicitado.
Como se puede ver, ahora el código ha quedado mucho más limpio, mejorada la orientación a objetos y sin código repetido.
Espero que les resulte útil.

0 comentarios:

Publicar un comentario

Muchas gracias por leer el post y comentarlo.

 
Copyright 2009 Programación SOLIDa
BloggerTheme by BloggerThemes | Design by 9thsphere