sábado, 24 de abril de 2010

Extension methods

El otro día estaba hablando con un compañero acerca de Extension methods y se me ocurrió escribir un poco sobre ellos. Aunque no es un asunto directamente relacionado con SharePoint no podemos olvidar que esta plataforma está desarrollada sobre .NET y, por lo tanto, podemos aprovechar todos nuestros conocimientos de desarrollo .NET para mejorar la calidad y la comprensibilidad del código de nuestros proyectos en cualquiera de las versiones de SharePoint. Vamos a verlo con un ejemplo muy simple.

Imaginad que estáis desarrollando un webpart que en un determinado momento necesita crear una página de publicación. Lo primero que crearemos será un método que realice el trabajo que necesitamos y nos quedará algo similar a lo siguiente:

public void CreatePublishingPage(string title, string url, string pageLayoutName)
{
    SPWeb web = SPContext.Current.Web;
    PublishingWeb publishingWeb = null;
    try
    {
        PublishingPage page = null;
        PageLayout selectedPageLayout = null;
        PageLayout[] pageLayouts = publishingWeb.GetAvailablePageLayouts();
        foreach (PageLayout layout in pageLayouts)
        {
            if (layout.Name.ToUpper().Equals(pageLayoutName.ToUpper()))
            {
                selectedPageLayout = layout;
                break;
            }
        }
        if (selectedPageLayout != null)
        {
            page = publishingWeb.GetPublishingPages().Add(url, selectedPageLayout);
            page.Title = title;
            SPFile file = page.ListItem.File;
            SPList list = file.Item.ListItems.List;
            if (list.ForceCheckout)
            {
                file.CheckIn(string.Empty);
                file.Publish(string.Empty);
            }
            if (list.EnableModeration)
            {
                file.Approve(string.Empty);
            }
        
        }
    }
    catch (Exception ex)
    {
        // Add exception management code here
    }
    finally
    {
        publishingWeb.Close();
    }
}

Después de implementar el método y probarlo adecuadamente intentaríamos partirlo en pequeñas partes que nos ayuden a hacer el código más inteligible y más reutilizable. Podríamos utilizar las facilidades que nos proporciona Visual Studio para hacerlo de una manera simple y rápida, tal y como muestra la figura:

image

Tras unas cuantas extracciones el código nos quedaría más o menos así:

public void CreatePublishingPage(string title, string url, string pageLayoutName)
{
    SPWeb web = SPContext.Current.Web;
    PublishingWeb publishingWeb = null;
    try
    {
        PublishingPage page = null;
        PageLayout selectedPageLayout = GetPageLayoutByName(pageLayoutName, publishingWeb);
        if (selectedPageLayout != null)
        {
            page = CreatePublishingPage(title, url, publishingWeb, page, selectedPageLayout);
        }
    }
    catch (Exception ex)
    {
        // Add exception management code here
    }
    finally
    {
        publishingWeb.Close();
    }
}
private static PublishingPage CreatePublishingPage(string title, string url, PublishingWeb publishingWeb, PublishingPage page, PageLayout selectedPageLayout)
{
    page = publishingWeb.GetPublishingPages().Add(url, selectedPageLayout);
    page.Title = title;
    PublishPublishingPage(page);
    return page;
}
private static void PublishPublishingPage(PublishingPage page)
{
    SPFile file = page.ListItem.File;
    SPList list = file.Item.ListItems.List;
    if (list.ForceCheckout)
    {
        file.CheckIn(string.Empty);
        file.Publish(string.Empty);
    }
    if (list.EnableModeration)
    {
        file.Approve(string.Empty);
    }
}
private static PageLayout GetPageLayoutByName(string pageLayoutName, PublishingWeb publishingWeb)
{
    PageLayout selectedPageLayout = null;
    PageLayout[] pageLayouts = publishingWeb.GetAvailablePageLayouts();
    foreach (PageLayout layout in pageLayouts)
    {
        if (layout.Name.ToUpper().Equals(pageLayoutName.ToUpper()))
        {
            selectedPageLayout = layout;
            break;
        }
    }
    return selectedPageLayout;
}

Una vez hecho esto, y de cara a poder reutilizar este código desde otras clases o desde otros proyectos los sacaríamos a una clase dentro de una librería externa y la referenciaríamos desde nuestro proyecto y acabaríamos teniendo un único método en nuestro webpart que se parecería a lo siguiente:

public void CreatePublishingPage(string title, string url, string pageLayoutName)
{
    SPWeb web = SPContext.Current.Web;
    PublishingWeb publishingWeb = null;
    try
    {
        PublishingPage page = null;
        PageLayout selectedPageLayout = MiNuevaClase.GetPageLayoutByName(pageLayoutName, publishingWeb);
        if (selectedPageLayout != null)
        {
            page = MiNuevaClase.CreatePublishingPage(title, url, publishingWeb, page, selectedPageLayout);
        }
    }
    catch (Exception ex)
    {
        // Add exception management code here
    }
    finally
    {
        publishingWeb.Close();
    }
}

Aquí es donde entran en juego los extension methods. Cojamos, por ejemplo, el método GetPageLayoutByName. Si estudiamos el contenido del método parece claro que se trata de una funcionalidad ligada directamente con el objeto PublishingWeb. De hecho, lo que realmente querríamos es tener este método disponible en la lista de miembros de cualquier objeto de tipo PublishingWeb. Para conseguir esto, lo que deberíamos hacer es crearnos una clase estática para extender dicha clase. Sigamos con el ejemplo anterior:

public static class PublishingWebExtensions
{
    public static PageLayout GetPageLayoutByName(this PublishingWeb web, string pageLayoutName)
    {
        PageLayout[] pageLayouts = web.GetAvailablePageLayouts();
        foreach (PageLayout layout in pageLayouts)
        {
            if (layout.Name.ToUpper().Equals(pageLayoutName.ToUpper()))
            {
                return layout;
            }
        }
        return null;
    }
}

Como se puede apreciar, el código es básicamente el mismo que teníamos antes. La diferencia es que si ahora estamos en cualquier otra clase desde la cual tenemos acceso a la clase que acabamos de implementar y tenemos un objeto de tipo PublishingWeb podremos llamar a este método directamente sobre el propio objeto, tal y como se muestra a continuación:

image

Tal y como se puede observar, Visual Studio nos ofrecerá directamente gracias a Intellisense el método que acabamos crear. Está claro que técnicamente no nos aporta nada nuevo, ya que habríamos conseguido lo mismo con la primera parte del artículo pero a la larga proporciona alguna que otra ventaja. Por ejemplo:

  • Cohesión: agrupamos los métodos directamente relacionados con un determinado objeto en un mismo lugar.
  • Simplicidad: proporcionamos a otros desarrolladores la manera más simple de encontrar una funcionalidad (intellisense)

Y podríamos añadir mantenibilidad, reusabilidad, estandarización, etc. que se deriban del hecho de tener los métodos agrupados de una manera adecuada.

2 comentarios:

loo dijo...

Me ha parecido muy interesante tu aproximación a los metodos de extensión, muy bien planteado dentro del contexto sharepoint !! Gracias por el post !!

David Martos dijo...

Muchas gracias Loo por el comentario, me anima a seguir escribiendo el hecho que a gente como tú le sean de utilidad mis entradas.