Browse Category

Patterns

patron_II

Patrones de diseño en Android (II): Patrones estructurales

Patrones de diseño en Android (I) : Patrones de creación
Patrones de diseño en Android (II): Patrones estructurales

Siguiendo la línea de patrones de diseño que comenzamos con los patrones de creación, seguimos con los patrones estructurales más utilizados en el desarrollo mobile. Éstos son: Adapter, Facade, y Proxy.

Adapter

El patrón adaptador consiste en crear una clase intermedia para poder ajustar el resultado a una salida esperada. Pongamos un pequeño ejemplo. Imaginaos que tenemos una interfaz(“Phone”) que define los métodos que deben implementar todos los teléfonos.

/**
 * Interface that defines the methods for every phone.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 25/4/16.
 */
public interface Phone {

    /**
     * Dialing numbers.
     */
    void dialNumbers();

    /**
     * Make a phone call.
     */
    void makeCall();

}

En 1980 existían solo teléfonos fijos “LandlinePhone”, los cuales implementaban estos métodos sin problema.

/**
 * LandlinePhone entity.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 25/4/16.
 */
public class LandlinePhone implements Phone{

    @Override
    public void dialNumbers() {
        System.out.println("Dialing numbers from landline phone");
    }

    @Override
    public void makeCall() {
        System.out.println("Making phone call from landline phone");
    }
}

A principios del año 2000 aparecen los teléfonos móviles los cuales tienen otros métodos diferentes a los definidos en la interfaz.

/**
 * Smartphone entity.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 25/4/16.
 */
public class SmartPhone {

    public void touchScreenForNumbers(){
        System.out.println("Dialing numbers from smartphone");
    }

    public void makePhoneCall(){
        System.out.println("Making phone call from smartphone");
    }

}

Podremos crear una clase “Wrapper” , la cual adaptará los métodos nuevos a los antiguos.

/**
 * Wrapper from {@link SmartPhone} to {@link Phone}.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 25/4/16.
 */
public class SmartphoneWrapper implements Phone{

    private SmartPhone smartPhone = new SmartPhone();

    @Override
    public void dialNumbers() {
        smartPhone.touchScreenForNumbers();
    }

    @Override
    public void makeCall() {
        smartPhone.makePhoneCall();
    }
}

 


Facade

El patrón “Facade” o “Fachada” tiene como objetivo hacer que la comunicación entre dos elementos sea mas sencilla gracias al uso de interfaces, haciendo invisible ciertas partes que pudieran ser más complejas. Pongamos un ejemplo:
Tenemos un sistema en el cual se pueden dar de alta usuarios con su nombre junto con su password. Pero hay ciertos campos que al que va a hacer el registro no le interesan (fecha del registro, sistema,etc..), por lo que al crear una fachada para este proceso, facilitaremos el proceso.

/**
 * User entity.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 25/4/16.
 */
public class User {

    private String username;

    private String password;

    private String system;

    private Date registrationDate;

    public User() {
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Date getRegistrationDate() {
        return registrationDate;
    }

    public void setRegistrationDate(Date registrationDate) {
        this.registrationDate = registrationDate;
    }

    public String getSystem() {
        return system;
    }

    public void setSystem(String system) {
        this.system = system;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}
/**
 * Facade for registration process.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 25/4/16.
 */
public class RegisterFacade {

    private RegistrationManager registrationManager;

    public RegisterFacade() {

        registrationManager = new RegistrationManager();

    }

    public void registerUser(String username, String password){

        User user = new User();

        user.setUsername( username );

        user.setPassword( password );

        user.setRegistrationDate( new Date() );

        user.setSystem("MacOS X - 10.10.5");

        registrationManager.makeRegistry( user );

    }

}
/**
 * Registration manager.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 25/4/16.
 */
public class RegistrationManager {

    public void makeRegistry(User user) {

        SimpleDateFormat sdf = new SimpleDateFormat("dd/mm/yyyy");

        System.out.println(
                "We'll made the registration for user with data: "
                + "\n\tUsername:\t" + user.getUsername()
                + "\n\tPassword:\t" + user.getPassword()
                + "\n\tSystem:\t" + user.getSystem()
                +" \n\tRegistration date:\t" + sdf.format(user.getRegistrationDate())
        );

        // We will make the registration in our system here.Database, rest services,etc...

    }

}

 


Proxy

En informática, el término “proxy” se utiliza en multitud de ocasiones. Este patrón se define como “Proporcionar un representante o sustituto de otro objeto para controlar el acceso a éste con el objetivo de retrasar o controlar y retrasar el coste de inicialización de objetos”.

  • proxy remoto
  • proxy virtual
  • proxy de protección

Pongamos un ejemplo: Tenemos que realizar scrapping de una web. El proceso de conexión y obtención del código de esa web puede ser un proceso muy pesado, por lo que nos interesa poder guardar la referencia de esos datos ya guardados.

Tenemos una interfaz WebParser que define el método de acceso al código de la web.

/**
 * WebParser interface.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 9/5/16.
 */
public interface WebParser {
    String getHtmlContent();
}

Por otra parte, tenemos una clase HtmlWebParser que implementa WebParser, la cual realiza las operaciones necesarias para obtener los datos de la página web que necesitemos

/**
 * HtmlWebParser class.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 9/5/16.
 */
public class HtmlWebParser implements WebParser {

    private String content;

    private String websiteUrl;

    public HtmlWebParser(String url) {

        websiteUrl = url;

        //Here, we would make the connection to the website, making all the heavy transactions here.

        content = "
Website html content
";

    }

    @Override
    public String getHtmlContent() {

        return content;

    }
}

Por último, tendremos una clase HtmlWebParserProxy también implementa la misma interfaz, pero guarda una instancia de un objeto HtmlWebParser para así evitarnos crear más objetos.

/**
 * Proxy class for html web parser.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 9/5/16.
 */
public class HtmlWebParserProxy implements WebParser {

    private HtmlWebParser connection;

    private String websiteUrl;

    public HtmlWebParserProxy(String url) {

        this.websiteUrl = url;

    }

    @Override
    public String getHtmlContent() {

        if (connection == null) {

            System.out.println("New instance of HtmlWebParser");

            connection = new HtmlWebParser(websiteUrl);

        }

        return connection.getHtmlContent();

    }
}

Una vez hecho esto, podremos utilizar nuestra clase proxy de la siguiente manera.

WebParser connection = new HtmlWebParserProxy("http://blog.asanchez-portfolio.es");
System.out.println(connection.getHtmlContent());
System.out.println(connection.getHtmlContent());

En la salida por consola podremos observar que solo se crea una instancia con lo que el código solo se descargará una vez :

...
New instance of HtmlWebParser

Website html content

Website html content
...

 Puedes ver el código completo en GITHUB

patron_I

Patrones de diseño en Android (I): Patrones de creación

Patrones de diseño en Android (I) : Patrones de creación
Patrones de diseño en Android (II): Patrones estructurales

El desarrollo de software no es una ciencia cierta. Siempre tenemos que realizar tareas diferentes, pero dentro de estas tareas, muchas veces nos encontramos con un mismo problema recurrente.

 

Cada patrón describe un problema que ocurre una y otra vez en nuestro entorno, así como la solución a ese problema, de tal modo que se pueda aplicar esta solución un millón de veces, sin hacer lo mismo dos veces.

 

Bajo esta premisa, realizaron los patrones de diseño. En el libro “Patrones de diseño” de Erich Gamma se dice que “…un patrón de diseño nomina, abstrae, e identifica los aspectos clave de una estructura de diseño común,lo que los hace útiles para crear un diseño orientado a objetos reutilizable..”

 

Los patrones de diseño en el ámbito del desarrollo software se dividen en tres categorías diferentes.

  • Patrones de creación, encargados de abstraer el proceso de creación de nuevas instancias de nuestra clase.
  • Patrones estructurales, que se ocupan de como combinar clases y objetos para formar estructuras mas grandes.
  • Patrones de comportamiento, los cuales no solo se encargan de describir patrones de clases y objetos, sino también la comunicación entre ambos.

En esta primera entrada nos vamos a centrar en los patrones de creación, en especial en los patrones “Builder”, “Singleton”, y “Factory Method”

Patrón Builder

Patrón utilizado para crear instancias de elementos complejas. Con este patrón se tiene un proceso de creación mas fácil e intuitivo para el desarrollador. Es un patrón muy utilizado dentro del desarrollo android, ya que permite crear nuevas instancias muy personalizadas. Un ejemplo muy claro es la clase “AlertDialog”, la cual se puede instanciar mediante este tipo de patrón.

/**
 * Entity that represents a "Car".
 *
 * @author asanchezyu@gmail.com
 * @version 1.0.
 * @since 8/4/16.
 */
public class Car {

    private final String plateNumber;
    private final String ownerDrivingLicenseId;
    private final String colour;
    private final String type;
    private final String year;

    private Car(Builder builder) {
        this.plateNumber = builder.plateNumber;
        this.ownerDrivingLicenseId = builder.ownerDrivingLicenseId;
        this.colour = builder.colour;
        this.type = builder.type;
        this.year = builder.year;
    }

    public String getColour() {
        return colour;
    }

    public String getOwnerDrivingLicenseId() {
        return ownerDrivingLicenseId;
    }

    public String getPlateNumber() {
        return plateNumber;
    }

    public String getType() {
        return type;
    }

    public String getYear() {
        return year;
    }

    /**
     * Builder class.
     */
    public static class Builder {

        private final String plateNumber;
        private final String ownerDrivingLicenseId;
        private String colour;
        private String type;
        private String year;

        public Builder(String plateNumber, String ownerDrivingLicenseId) {
            this.plateNumber = plateNumber;
            this.ownerDrivingLicenseId = ownerDrivingLicenseId;
        }

        public Builder setColour(String colour) {
            this.colour = colour;
            return this;
        }

        public Builder setType(String type) {
            this.type = type;
            return this;
        }

        public Builder setYear(String year) {
            this.year = year;
            return this;
        }

        public Car build() {
            return new Car(this);
        }
    }
}
Builder

Patrón Singleton

Este patrón nos permite tener una sola instancia de una clase en toda nuestra aplicación. Este patrón es muy útil cuando utilizamos managers de bases de datos, de peticiones REST, cola de tareas en background, ya que solo queremos que se instancie una sola vez. Si se trabaja sobre la instancia en diferentes hilos, debemos tener control sobre la concurrencia, tal y como se ve en el siguiente ejemplo.

/**
 * Database manager.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 8/4/16.
 */
public class DbManager {

    private static volatile DbManager instance = null;

    private DbManager() {
        // No instances created from outside.
    }

    public static DbManager getInstance() {
        if (instance == null) {
            synchronized (DbManager.class) {
                if (instance == null) {
                    instance = new DbManager();
                }
            }
        }
        return instance;
    }

}
Singleton

 

Patrón Factory Method

Este patrón de creación nos permite desde una clase abstracta constructora crear instancias de otros objetos que a su vez, heredan o implementan un elemento en común . Este patrón es muy útil cuando desde nuestra aplicación, por ejemplo, tenemos varios entornos de desarrollo (desarrollo, pre-producción y producción), y cada uno de ellos tiene un endPoint diferente. Gracia a este patrón, el cambiar de entorno se simplifica en un cambio de variable.

Pongamos el anterior ejemplo en práctica:

Nuestra aplicación tendrá dos entornos (“environments”). Ambos entornos nos tendrán que devolver la url de nuestro blog. Para ello creamos una intefaz “Environment” y dos clases que la implementan, “DevEnvironment” y “ProductionEnvironment”.

/**
 * Interface that defines the methods of one environment.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 8/4/16.
 */
public interface Environment {

    String getBlogUrl();

}
Environment
/**
 * Class that represents one type of enviroment: Development Environment.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 8/4/16.
 */
public class DevEnvironment implements Environment {

    @Override
    public String getBlogUrl() {

        return "http://localhost:8080/myBlog";

    }
}
DevEnvironment
/**
 * Class that represents one type of enviroment: Production Environment.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 8/4/16.
 */
public class ProductionEnvironment implements Environment {

    @Override
    public String getBlogUrl() {

        return "http://blog.asanchez-portfolio.es";

    }
}
ProductionEnvironment

Para crear instancias de estos elementos, crearemos una factoría abstracta “EnvironmentFactory” y que a su vez será implementada por la factoría de nuestra aplicación “ApplicationEnvironmentFactory”.

/**
 * Interface that defines needed methods for one environment factory.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 8/4/16.
 */
public interface EnvironmentFactory {

    enum EnvironmentType {
        DEVELOPMENT,
        PRODUCTION
    }

    Environment getEnvironment(EnvironmentType environmentType);

}
EnvironmentFactory
/**
 * Class that represents one type of environment creator: Our application environment factory.
 *
 * @author asanchezyu@gmail.com.
 * @version 1.0.
 * @since 8/4/16.
 */
public class ApplicationEnvironmentFactory implements EnvironmentFactory {

    @Override
    public Environment getEnvironment(EnvironmentType environmentType) {
        Environment environment;

        switch (environmentType) {
            case DEVELOPMENT:
                environment = new DevEnvironment();
                break;
            case PRODUCTION:
                environment = new ProductionEnvironment();
                break;
            default:
                environment = new DevEnvironment();
                break;
        }

        return environment;
    }
}
ApplicationEnvironmentFactory

Una vez hecho esto, desde la clase que queramos utilizar la factoría, solo debemos realizar lo siguiente:

...

EnvironmentFactory environmentFactory = new ApplicationEnvironmentFactory();

Environment debugEnvironment = environmentFactory.getEnvironment(EnvironmentFactory.EnvironmentType.DEVELOPMENT);

Environment productionEnvironment = environmentFactory.getEnvironment(EnvironmentFactory.EnvironmentType.PRODUCTION);

System.out.println("Blog url from debug  " + debugEnvironment.getBlogUrl());

System.out.println("Blog url from production  " + productionEnvironment.getBlogUrl());

...
How to use

Puedes ver el código en github