Browse Category

Java

soap_pattern

Integración Retrofit y Servicios SOAP

Conocer como se integran servicios web en aplicaciones móviles es un “Must” que todo desarrollador debe conocer. La mayoría de sistemas han adoptado los servicios Web del tipo REST / JSON, pero aún existen sistemas, que por antigüedad o por integración con otros servicios, deben utilizar servicios del tipo SOAP/WSDL. A lo largo de los años, un desarrollador tocará ambos tipos de servicios web, por lo que es muy recomendable saber como se introducen en nuestros proyectos.

 


SOAP vs REST

No voy a entrar en el debate de cual es mejor, ya que ambos hacen la función que tienen. Son tecnologías que son complementarias, aunque no es normal encontrarse las dos en un mismo proyecto.

Algunas de las características más importantes son:

  • Los servicios SOAP funcionan con un fichero de definición del servicio (WSDL), en el que se definen los tipos de datos que se usarán, los métodos que tiene el servicio, y las respuestas. Por el contrario, una petición REST no tiene este tipo de fichero, por lo que tendremos que saber de antemano el nombre de los métodos, y el tipo de datos que se le pasa.
  • Las servicios REST suelen ser más rápidos que los servicios SOAP. Esto es debido a la estructura de los datos. JSON es más ligero, mientras que el XML que se genera en un servicio SOAP es mucho mas pesado. En servicios SOAP hay que mandar cabeceras y “namespaces” (lo que es en desarrollo no tienen ninguna importancia), que en un JSON no aparecen.
  • Los servicios REST, al ser más ágiles, se han creado más librerías para dispositivos móviles (Gson, Jackson, Moshi, … ) y mas facilidad de integración, mientras que para los servicios SOAP no hay tal abanico de oportunidades (Ksoap2, SimpleXML).

Ejemplo práctico

Para el ejemplo que vamos a realizar, utilizaremos un servicio web gratuito de WebServiceX, el cual nos ofrecerá un listado de ciudades de Estados Unidos con sus códigos postales a través del nombre de la ciudad, que será lo que le pasaremos al servicio.

Definición del Servicio

La petición tendrá el siguiente formato:

<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
  <soap12:Body>
    <GetInfoByCity xmlns="http://www.webserviceX.NET">
      <USCity>string</USCity>
    </GetInfoByCity>
  </soap12:Body>
</soap12:Envelope>

Mientras, la respuesta tendrá el siguiente formato:

<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
  <soap12:Body>
    <GetInfoByCityResponse xmlns="http://www.webserviceX.NET">
      <GetInfoByCityResult>xml</GetInfoByCityResult>
    </GetInfoByCityResponse>
  </soap12:Body>
</soap12:Envelope>

Para probar el servicio antes del desarrollo, podremos hacerlo simulando una petición POST con alguna herramienta de servicios. En mi caso, suelo usar POSTMAN.

soap_postman

Gracias al uso de esta herramienta, podemos observar la salida del webservice, así poder crear el objeto de la respuesta correcto.

Librerías

Utilizaremos las librerías de retrofit y okHttp para las peticiones. Ademas utilizaremos el convertidor SimpleXml para retrofit

compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
compile 'com.squareup.okhttp3:okhttp:3.3.1'
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.3.1'

compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile ('com.squareup.retrofit2:converter-simplexml:2.1.0'){
    exclude group: 'stax', module: 'stax-api'
    exclude group: 'stax', module: 'stax'
    exclude group: 'xpp3', module: 'xpp3'
}

Configuración de Retrofit y OkHttp

La creación de la instancia de Retrofit tendrá que llevar consigo el Converter de SimpleXml con el interceptor necesario:

HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();

interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

Strategy strategy = new AnnotationStrategy();

Serializer serializer = new Persister(strategy);

OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .addInterceptor(interceptor)
            .connectTimeout(2, TimeUnit.MINUTES)
            .writeTimeout(2, TimeUnit.MINUTES)
            .readTimeout(2, TimeUnit.MINUTES)
            .build();

Retrofit retrofit =  new Retrofit.Builder()
    .addConverterFactory(SimpleXmlConverterFactory.create(serializer))
    .baseUrl("http://www.webservicex.net/")
    .client(okHttpClient)
    .build();

Creación del objeto API

Será la interfaz que se utilizará Retrofit para la creación de la API.

public interface UsStatesApi {

    @Headers({
            "Content-Type: text/xml",
            "Accept-Charset: utf-8"
    })
    @POST("/uszip.asmx")
    Call<UsStatesResponseEnvelope> requestStateInfo(@Body UsStatesRequestEnvelope body);

}

Creación de las clases para la request y response

Los objetos tanto de la petición como la respuesta tienen la misma forma de creación, por lo que solo pondremos aquí los de la petición.

@Root(name = "soap12:Envelope")
@NamespaceList({
        @Namespace( prefix = "xsi", reference = "http://www.w3.org/2001/XMLSchema-instance"),
        @Namespace( prefix = "xsd", reference = "http://www.w3.org/2001/XMLSchema"),
        @Namespace( prefix = "soap12", reference = "http://www.w3.org/2003/05/soap-envelope")
})
public class UsStatesRequestEnvelope {

    @Element(name = "soap12:Body", required = false)
    private UsStatesRequestBody body;

    public UsStatesRequestBody getBody() {
        return body;
    }

    public void setBody(UsStatesRequestBody body) {
        this.body = body;
    }
}
@Root(name = "soap12:Body", strict = false)
public class UsStatesRequestBody {

    @Element(name = "GetInfoByCity",required = false)
    private UsStatesRequestData usStatesRequestData;

    public UsStatesRequestData getUsStatesRequestData() {
        return usStatesRequestData;
    }

    public void setUsStatesRequestData(UsStatesRequestData usStatesRequestData) {
        this.usStatesRequestData = usStatesRequestData;
    }

}
@Root(name = "GetInfoByState", strict = false)
@Namespace(reference = "http://www.webserviceX.NET")
public class UsStatesRequestData {

    @Element(name = "USCity", required = false)
    private String city;

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

}

Por último, para hacer la petición deberemos hacer lo siguiente:

UsStatesRequestEnvelope envelope = new UsStatesRequestEnvelope();

UsStatesRequestBody body = new UsStatesRequestBody();

UsStatesRequestData data = new UsStatesRequestData();

data.setCity( cityName );

body.setUsStatesRequestData(data);

envelope.setBody(body);

Call<UsStatesResponseEnvelope> call = usStatesApi.requestStateInfo(envelope);

call.enqueue(new retrofit2.Callback<UsStatesResponseEnvelope>() {

    @Override
    public void onResponse(Call<UsStatesResponseEnvelope> call, final Response<UsStatesResponseEnvelope> response) {

       //DO WHAT YOU WANT 

    }

    @Override
    public void onFailure(Call<UsStatesResponseEnvelope> call, Throwable t) {
 
        //ERROR!!
                
    }

});

 

Puedes ver un ejemplo completo en GITHUB

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

SOLID

Principios S.O.L.I.D

Una de las palabras más oídas en la CODEMOTION 2015 fue “S.O.L.I.D” y no es para menos, ya que con el se definen unas buenas prácticas. En un principio pensé que era algo novedoso, algo que habían creado para el desarrollo mobile, pero ni mucho menos es así.
Este conjunto de principios se remonta a principios de la década de los 2000, cuando Robert C. Martin, también conocido como “Uncle Bob”, creó esta regla mnemotécnica de principios básicos para el desarrollo orientado a objetos. La aplicación de estos principios hace que el código creado sea mucho más mantenible y expandible, siendo menor la creación de código spaghetti, y las refactorizaciones sobre el mismo.

Cada una de las letras corresponde con un principio diferente:


Single Responsibility Principle

A class should have one, and only one, reason to change.
(Una clase debería tener una y solo una, razón para cambiar)

Cada clase es responsable de solo una tarea, por lo que si es modificada, solo debería ser porque esa tarea ha sido cambiada. Si en algún momento nuestra clase tiene más de una responsabilidad, éstas deben desacoplarse en distintas clases.


Open Closed Principle

You should be able to extend a classes behavior, without modifying it.
(Deberías ser capaz de extender el comportamiento de una clase sin modificarlo)

Una clase debe estar abierta a extensión pero no a su modificación. Esto se consigue mediante herencia. Teniendo una clase padre que tiene un comportamiento específico, puedes extender esta clase a una clase hija, pudiendo esta tener más funcionalidad, pero manteniendo la anterior. Con esto consigues que la clase padre esté cerrada a modificación pero abierta a la extensión.


Liskov Substitution Principle

Derived classes must be substitutable for their base classes.
(Las clases derivadas deben ser sustituibles por sus clases base)

Quizás sea el punto en el que más dudas tiene la gente. En este principio se comenta una clase que extiende de otra puede usarse como sustituta de su padre sin que su comportamiento se modifique.Pongamos un ejemplo para que quede más claro. Tenemos una clase “Telefono” que contiene un método que es “realizarLlamada()”. Si tuviéramos una clase “SmartPhone” que extiende de “Telefono”, podríamos sustituir todas las referencias a “Telefono” por referencias a “SmartPhone”


Interface Segregation Principle

Make fine grained interfaces that are client specific.
(Haz interfaces pequeñas que sean para un cliente específico)

Este principio nos indica que se usen interfaces para mostrar a los clientes que las usen sólo los métodos que necesiten, así evitaremos que tengan acceso a ciertas partes que no deben ver.
En el desarrollo android, en especial en MVP, tenemos un caso clarísimo. Normalmente las actividades o fragmentos implementan la parte de las vistas, pasándole a los presentadores nuestra implementación. El presentador solo tendrá acceso a los métodos de la interfaz, ocultándole así los métodos del fragment/actividad (onCreate, onResume, etc..) que no necesitan.


Dependency Inversion Principle

Depend on abstractions, not on concretions.
(Haz que dependas de abstracciones y no de concreciones)

Por último, el principio de inversión de dependencias nos indica que se debe depender de abstracciones y no de concreciones de clases. Si tenemos una clase que implementa una interfaz deberíamos poder ser capaces de usar la interfaz como ejecutora de los métodos sin que eso suponga un problema.
La inyección de dependencias en android mediante Dagger / Dagger2 , nos ayuda a cumplir este principio.

keep_calm_solid

Para finalizar, comentar que estos principios no son obligatorios ni la única verdad existente; Son buenas prácticas, que si las sigues, tu código, tu trabajo y sobre todo TUS COMPAÑEROS te lo agradecerán.

Puedes ver AQUI el artículo de Uncle Bob en el que se exponen estos principios.

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