Guía rápida de patrones de diseño

Los patrones de diseño representan las mejores prácticas utilizadas por los desarrolladores de software orientados a objetos con experiencia. Los patrones de diseño son soluciones a los problemas generales que enfrentan los desarrolladores de software durante el desarrollo de software. Numerosos desarrolladores de software obtuvieron estas soluciones mediante prueba y error durante un período de tiempo considerable.

¿Qué es Gang of Four (GOF)?

En 1994, cuatro autores Erich Gamma, Richard Helm, Ralph Johnson y John Vlissides publicaron un libro titulado Design Patterns - Elements of Reusable Object-Oriented Software que inició el concepto de patrón de diseño en el desarrollo de software.

Estos autores se conocen colectivamente como Gang of Four (GOF). Según estos autores, los patrones de diseño se basan principalmente en los siguientes principios de diseño orientado a objetos.

  • Programa en una interfaz, no en una implementación

  • Favorecer la composición de objetos sobre la herencia

Uso de patrón de diseño

Los patrones de diseño tienen dos usos principales en el desarrollo de software.

Plataforma común para desarrolladores

Los patrones de diseño proporcionan una terminología estándar y son específicos para un escenario particular. Por ejemplo, un patrón de diseño singleton significa el uso de un solo objeto, por lo que todos los desarrolladores familiarizados con el patrón de diseño único utilizarán un solo objeto y pueden decirse entre sí que el programa sigue un patrón singleton.

Mejores prácticas

Los patrones de diseño han evolucionado durante un largo período de tiempo y brindan las mejores soluciones a ciertos problemas que se enfrentan durante el desarrollo de software. El aprendizaje de estos patrones ayuda a los desarrolladores sin experiencia a aprender el diseño de software de una manera fácil y rápida.

Tipos de patrón de diseño

Según el libro de referencia de patrones de diseño Design Patterns - Elements of Reusable Object-Oriented Software, hay 23 patrones de diseño. Estos patrones se pueden clasificar en tres categorías: patrones de creación, estructurales y de comportamiento. También discutiremos otra categoría de patrones de diseño: patrones de diseño J2EE.

SN Patrón y descripción
1 Creational Patterns
Estos patrones de diseño proporcionan una forma de crear objetos mientras ocultan la lógica de creación, en lugar de crear instancias de objetos directamente utilizando un operador nuevo. Esto le da al programa más flexibilidad para decidir qué objetos deben crearse para un caso de uso dado.
2 Structural Patterns
Estos patrones de diseño se refieren a la composición de clases y objetos. El concepto de herencia se utiliza para componer interfaces y definir formas de componer objetos para obtener nuevas funcionalidades.
3 Behavioral Patterns
Estos patrones de diseño se refieren específicamente a la comunicación entre objetos.
4 J2EE Patterns
Estos patrones de diseño se refieren específicamente al nivel de presentación. Sun Java Center identifica estos patrones.

El patrón de fábrica es uno de los patrones de diseño más utilizados en Java. Este tipo de patrón de diseño se incluye en el patrón de creación, ya que este patrón proporciona una de las mejores formas de crear un objeto.

En el patrón de fábrica, creamos un objeto sin exponer la lógica de creación al cliente y nos referimos al objeto recién creado usando una interfaz común.

Implementación

Vamos a crear una interfaz Shape y clases concretas implementando la interfaz Shape . Una clase de fábrica ShapeFactory se define como el siguiente paso.

FactoryPatternDemo , nuestra clase de demostración utilizará ShapeFactory para obtener un objeto Shape . Pasará información ( CÍRCULO / RECTÁNGULO / CUADRADO ) a ShapeFactory para obtener el tipo de objeto que necesita.

Paso 1

Crea una interfaz.

Shape.java

public interface Shape {
   void draw();
}

Paso 2

Crea clases concretas implementando la misma interfaz.

Rectangle.java

public class Rectangle implements Shape {

   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}

Square.java

public class Square implements Shape {

   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}

Circle.java

public class Circle implements Shape {

   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}

Paso 3

Cree una fábrica para generar un objeto de clase concreta basado en información dada.

ShapeFactory.java

public class ShapeFactory {
	
   //use getShape method to get object of type shape 
   public Shape getShape(String shapeType){
      if(shapeType == null){
         return null;
      }		
      if(shapeType.equalsIgnoreCase("CIRCLE")){
         return new Circle();
      } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
         return new Rectangle();
      } else if(shapeType.equalsIgnoreCase("SQUARE")){
         return new Square();
      }
      return null;
   }
}

Etapa 4

Utilice Factory para obtener un objeto de una clase concreta pasando una información como el tipo.

FactoryPatternDemo.java

public class FactoryPatternDemo {

   public static void main(String[] args) {
      ShapeFactory shapeFactory = new ShapeFactory();

      //get an object of Circle and call its draw method.
      Shape shape1 = shapeFactory.getShape("CIRCLE");

      //call draw method of Circle
      shape1.draw();

      //get an object of Rectangle and call its draw method.
      Shape shape2 = shapeFactory.getShape("RECTANGLE");

      //call draw method of Rectangle
      shape2.draw();

      //get an object of Square and call its draw method.
      Shape shape3 = shapeFactory.getShape("SQUARE");

      //call draw method of square
      shape3.draw();
   }
}

Paso 5

Verifique la salida.

Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.

Los patrones de Abstract Factory funcionan alrededor de una superfábrica que crea otras fábricas. Esta fábrica también se denomina fábrica de fábricas. Este tipo de patrón de diseño se incluye en el patrón de creación, ya que este patrón proporciona una de las mejores formas de crear un objeto.

En el patrón Abstract Factory, una interfaz es responsable de crear una fábrica de objetos relacionados sin especificar explícitamente sus clases. Cada fábrica generada puede dar los objetos según el patrón de fábrica.

Implementación

Vamos a crear una interfaz Shape y una clase concreta implementándola. Creamos una clase de fábrica abstracta AbstractFactory como siguiente paso. Se define la clase de fábrica ShapeFactory, que amplía AbstractFactory. Se crea una clase FactoryProducer de creador / generador de fábrica.

AbstractFactoryPatternDemo, nuestra clase de demostración usa FactoryProducer para obtener un objeto AbstractFactory. Pasará información (CIRCLE / RECTANGLE / SQUARE para Shape) a AbstractFactory para obtener el tipo de objeto que necesita.

Paso 1

Crea una interfaz para Shapes.

Shape.java

public interface Shape {
   void draw();
}

Paso 2

Crea clases concretas implementando la misma interfaz.

RoundedRectangle.java

public class RoundedRectangle implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside RoundedRectangle::draw() method.");
   }
}

RoundedSquare.java

public class RoundedSquare implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside RoundedSquare::draw() method.");
   }
}

Rectangle.java

public class Rectangle implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}

Paso 3

Cree una clase abstracta para obtener fábricas de objetos de forma normal y redondeada.

AbstractFactory.java

public abstract class AbstractFactory {
   abstract Shape getShape(String shapeType) ;
}

Etapa 4

Cree clases de Factory ampliando AbstractFactory para generar objetos de clase concreta basados ​​en información dada.

ShapeFactory.java

public class ShapeFactory extends AbstractFactory {
   @Override
   public Shape getShape(String shapeType){    
      if(shapeType.equalsIgnoreCase("RECTANGLE")){
         return new Rectangle();         
      }else if(shapeType.equalsIgnoreCase("SQUARE")){
         return new Square();
      }	 
      return null;
   }
}

RoundedShapeFactory.java

public class RoundedShapeFactory extends AbstractFactory {
   @Override
   public Shape getShape(String shapeType){    
      if(shapeType.equalsIgnoreCase("RECTANGLE")){
         return new RoundedRectangle();         
      }else if(shapeType.equalsIgnoreCase("SQUARE")){
         return new RoundedSquare();
      }	 
      return null;
   }
}

Paso 5

Cree una clase de generador / productor de fábrica para obtener fábricas pasando una información como Shape

FactoryProducer.java

public class FactoryProducer {
   public static AbstractFactory getFactory(boolean rounded){   
      if(rounded){
         return new RoundedShapeFactory();         
      }else{
         return new ShapeFactory();
      }
   }
}

Paso 6

Utilice FactoryProducer para obtener AbstractFactory con el fin de obtener fábricas de clases concretas pasando una información como el tipo.

AbstractFactoryPatternDemo.java

public class AbstractFactoryPatternDemo {
   public static void main(String[] args) {
      //get shape factory
      AbstractFactory shapeFactory = FactoryProducer.getFactory(false);
      //get an object of Shape Rectangle
      Shape shape1 = shapeFactory.getShape("RECTANGLE");
      //call draw method of Shape Rectangle
      shape1.draw();
      //get an object of Shape Square 
      Shape shape2 = shapeFactory.getShape("SQUARE");
      //call draw method of Shape Square
      shape2.draw();
      //get shape factory
      AbstractFactory shapeFactory1 = FactoryProducer.getFactory(true);
      //get an object of Shape Rectangle
      Shape shape3 = shapeFactory1.getShape("RECTANGLE");
      //call draw method of Shape Rectangle
      shape3.draw();
      //get an object of Shape Square 
      Shape shape4 = shapeFactory1.getShape("SQUARE");
      //call draw method of Shape Square
      shape4.draw();
      
   }
}

Paso 7

Verifique la salida.

Inside Rectangle::draw() method.
Inside Square::draw() method.
Inside RoundedRectangle::draw() method.
Inside RoundedSquare::draw() method.

El patrón singleton es uno de los patrones de diseño más simples de Java. Este tipo de patrón de diseño se incluye en el patrón de creación, ya que este patrón proporciona una de las mejores formas de crear un objeto.

Este patrón involucra una sola clase que es responsable de crear su propio objeto mientras se asegura de que solo se cree un único objeto. Esta clase proporciona una forma de acceder a su único objeto al que se puede acceder directamente sin necesidad de crear una instancia del objeto de la clase.

Implementación

Vamos a crear una clase SingleObject . La clase SingleObject tiene su constructor como privado y tiene una instancia estática de sí mismo.

La clase SingleObject proporciona un método estático para llevar su instancia estática al mundo exterior. SingletonPatternDemo , nuestra clase de demostración utilizará la clase SingleObject para obtener un objeto SingleObject .

Paso 1

Crea una clase Singleton.

SingleObject.java

public class SingleObject {

   //create an object of SingleObject
   private static SingleObject instance = new SingleObject();

   //make the constructor private so that this class cannot be
   //instantiated
   private SingleObject(){}

   //Get the only object available
   public static SingleObject getInstance(){
      return instance;
   }

   public void showMessage(){
      System.out.println("Hello World!");
   }
}

Paso 2

Obtenga el único objeto de la clase singleton.

SingletonPatternDemo.java

public class SingletonPatternDemo {
   public static void main(String[] args) {

      //illegal construct
      //Compile Time Error: The constructor SingleObject() is not visible
      //SingleObject object = new SingleObject();

      //Get the only object available
      SingleObject object = SingleObject.getInstance();

      //show the message
      object.showMessage();
   }
}

Paso 3

Verifique la salida.

Hello World!

El patrón de constructor construye un objeto complejo usando objetos simples y usando un enfoque paso a paso. Este tipo de patrón de diseño se incluye en el patrón de creación, ya que este patrón proporciona una de las mejores formas de crear un objeto.

Una clase Builder construye el objeto final paso a paso. Este constructor es independiente de otros objetos.

Implementación

Hemos considerado un caso comercial de restaurante de comida rápida donde una comida típica podría ser una hamburguesa y una bebida fría. La hamburguesa puede ser una hamburguesa vegetariana o una hamburguesa de pollo y se empacará con una envoltura. La bebida fría puede ser una coca cola o una pepsi y se envasa en una botella.

Vamos a crear un artículo de interfaz que representa alimentos como hamburguesas y bebidas frías y clases concretas de aplicación del artículo interfaz y un embalaje interfaz que representa el envasado de alimentos y clases concretas de aplicación del Embalaje interfaz como hamburguesa sería embalado en la envoltura y el frío la bebida se embalaría como botella.

Luego creamos una clase Meal con ArrayList of Item y MealBuilder para construir diferentes tipos de objetos Meal combinando Item . BuilderPatternDemo , nuestra clase de demostración utilizará MealBuilder para crear una comida .

Paso 1

Cree un elemento de interfaz que represente el producto alimenticio y el embalaje.

Item.java

public interface Item {
   public String name();
   public Packing packing();
   public float price();	
}

Packing.java

public interface Packing {
   public String pack();
}

Paso 2

Cree clases concretas implementando la interfaz Packing.

Wrapper.java

public class Wrapper implements Packing {

   @Override
   public String pack() {
      return "Wrapper";
   }
}

Bottle.java

public class Bottle implements Packing {

   @Override
   public String pack() {
      return "Bottle";
   }
}

Paso 3

Cree clases abstractas implementando la interfaz del elemento que proporciona funcionalidades predeterminadas.

Burger.java

public abstract class Burger implements Item {

   @Override
   public Packing packing() {
      return new Wrapper();
   }

   @Override
   public abstract float price();
}

ColdDrink.java

public abstract class ColdDrink implements Item {

	@Override
	public Packing packing() {
       return new Bottle();
	}

	@Override
	public abstract float price();
}

Etapa 4

Cree clases concretas que amplíen las clases de Burger y ColdDrink

VegBurger.java

public class VegBurger extends Burger {

   @Override
   public float price() {
      return 25.0f;
   }

   @Override
   public String name() {
      return "Veg Burger";
   }
}

ChickenBurger.java

public class ChickenBurger extends Burger {

   @Override
   public float price() {
      return 50.5f;
   }

   @Override
   public String name() {
      return "Chicken Burger";
   }
}

Coke.java

public class Coke extends ColdDrink {

   @Override
   public float price() {
      return 30.0f;
   }

   @Override
   public String name() {
      return "Coke";
   }
}

Pepsi.java

public class Pepsi extends ColdDrink {

   @Override
   public float price() {
      return 35.0f;
   }

   @Override
   public String name() {
      return "Pepsi";
   }
}

Paso 5

Cree una clase Meal con objetos Item definidos anteriormente.

Meal.java

import java.util.ArrayList;
import java.util.List;

public class Meal {
   private List<Item> items = new ArrayList<Item>();	

   public void addItem(Item item){
      items.add(item);
   }

   public float getCost(){
      float cost = 0.0f;
      for (Item item : items) {
         cost += item.price();
      }		
      return cost;
   }

   public void showItems(){
      for (Item item : items) {
         System.out.print("Item : "+item.name());
         System.out.print(", Packing : "+item.packing().pack());
         System.out.println(", Price : "+item.price());
      }		
   }	
}

Paso 6

Cree una clase MealBuilder, la clase de constructor real responsable de crear objetos Meal.

MealBuilder.java

public class MealBuilder {

   public Meal prepareVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new VegBurger());
      meal.addItem(new Coke());
      return meal;
   }   

   public Meal prepareNonVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new ChickenBurger());
      meal.addItem(new Pepsi());
      return meal;
   }
}

Paso 7

BuiderPatternDemo usa MealBuider para demostrar el patrón del constructor.

BuilderPatternDemo.java

public class BuilderPatternDemo {
   public static void main(String[] args) {
      MealBuilder mealBuilder = new MealBuilder();

      Meal vegMeal = mealBuilder.prepareVegMeal();
      System.out.println("Veg Meal");
      vegMeal.showItems();
      System.out.println("Total Cost: " +vegMeal.getCost());

      Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
      System.out.println("\n\nNon-Veg Meal");
      nonVegMeal.showItems();
      System.out.println("Total Cost: " +nonVegMeal.getCost());
   }
}

Paso 8

Verifique la salida.

Veg Meal
Item : Veg Burger, Packing : Wrapper, Price : 25.0
Item : Coke, Packing : Bottle, Price : 30.0
Total Cost: 55.0

Non-Veg Meal
Item : Chicken Burger, Packing : Wrapper, Price : 50.5
Item : Pepsi, Packing : Bottle, Price : 35.0
Total Cost: 85.5

El patrón de prototipo se refiere a la creación de un objeto duplicado teniendo en cuenta el rendimiento. Este tipo de patrón de diseño se incluye en el patrón de creación, ya que este patrón proporciona una de las mejores formas de crear un objeto.

Este patrón implica la implementación de una interfaz de prototipo que le dice que cree un clon del objeto actual. Este patrón se utiliza cuando la creación de un objeto directamente es costosa. Por ejemplo, un objeto debe crearse después de una costosa operación de base de datos. Podemos almacenar en caché el objeto, devolver su clon en la próxima solicitud y actualizar la base de datos cuando sea necesario, reduciendo así las llamadas a la base de datos.

Implementación

Vamos a crear una clase abstracta Shape y clases concretas ampliando la clase Shape . Una clase ShapeCache se define como el siguiente paso que almacena objetos de forma en una tabla hash y devuelve su clon cuando se solicita.

PrototypPatternDemo , nuestra clase de demostración usará la clase ShapeCache para obtener un objeto Shape .

Paso 1

Cree una clase abstracta que implemente la interfaz Clonable .

Shape.java

public abstract class Shape implements Cloneable {
   
   private String id;
   protected String type;
   
   abstract void draw();
   
   public String getType(){
      return type;
   }
   
   public String getId() {
      return id;
   }
   
   public void setId(String id) {
      this.id = id;
   }
   
   public Object clone() {
      Object clone = null;
      try {
         clone = super.clone();
      } catch (CloneNotSupportedException e) {
         e.printStackTrace();
      }
      return clone;
   }
}

Paso 2

Cree clases concretas ampliando la clase anterior.

Rectangle.java

public class Rectangle extends Shape {

   public Rectangle(){
     type = "Rectangle";
   }

   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}

Square.java

public class Square extends Shape {

   public Square(){
     type = "Square";
   }

   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}

Circle.java

public class Circle extends Shape {

   public Circle(){
     type = "Circle";
   }

   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}

Paso 3

Cree una clase para obtener clases concretas de la base de datos y almacenarlas en una tabla hash .

ShapeCache.java

import java.util.Hashtable;

public class ShapeCache {
	
   private static Hashtable<String, Shape> shapeMap 
      = new Hashtable<String, Shape>();

   public static Shape getShape(String shapeId) {
      Shape cachedShape = shapeMap.get(shapeId);
      return (Shape) cachedShape.clone();
   }

   // for each shape run database query and create shape
   // shapeMap.put(shapeKey, shape);
   // for example, we are adding three shapes
   public static void loadCache() {
      Circle circle = new Circle();
      circle.setId("1");
      shapeMap.put(circle.getId(),circle);

      Square square = new Square();
      square.setId("2");
      shapeMap.put(square.getId(),square);

      Rectangle rectangle = new Rectangle();
      rectangle.setId("3");
      shapeMap.put(rectangle.getId(),rectangle);
   }
}

Etapa 4

PrototypePatternDemo usa la clase ShapeCache para obtener clones de formas almacenadas en una Hashtable .

PrototypePatternDemo.java

public class PrototypePatternDemo {
   public static void main(String[] args) {
      ShapeCache.loadCache();

      Shape clonedShape = (Shape) ShapeCache.getShape("1");
      System.out.println("Shape : " + clonedShape.getType());		

      Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
      System.out.println("Shape : " + clonedShape2.getType());		

      Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
      System.out.println("Shape : " + clonedShape3.getType());		
   }
}

Paso 5

Verifique la salida.

Shape : Circle
Shape : Square
Shape : Rectangle

El patrón de adaptador funciona como un puente entre dos interfaces incompatibles. Este tipo de patrón de diseño viene bajo patrón estructural ya que este patrón combina la capacidad de dos interfaces independientes.

Este patrón involucra a una sola clase que es responsable de unir funcionalidades de interfaces independientes o incompatibles. Un ejemplo de la vida real podría ser el caso de un lector de tarjetas que actúa como adaptador entre la tarjeta de memoria y una computadora portátil. Conecta la tarjeta de memoria al lector de tarjetas y el lector de tarjetas a la computadora portátil para que la tarjeta de memoria se pueda leer a través de la computadora portátil.

Estamos demostrando el uso del patrón Adaptador mediante el siguiente ejemplo en el que un dispositivo reproductor de audio solo puede reproducir archivos mp3 y desea usar un reproductor de audio avanzado capaz de reproducir archivos vlc y mp4.

Implementación

Tenemos una interfaz MediaPlayer y una clase concreta AudioPlayer que implementa la interfaz MediaPlayer . AudioPlayer puede reproducir archivos de audio en formato mp3 de forma predeterminada.

Tenemos otra interfaz AdvancedMediaPlayer y clases concretas que implementan la interfaz AdvancedMediaPlayer . Estas clases pueden reproducir archivos en formato vlc y mp4.

Queremos que AudioPlayer también reproduzca otros formatos. Para lograr esto, hemos creado una clase de adaptador MediaAdapter que implementa la interfaz MediaPlayer y usa objetos AdvancedMediaPlayer para reproducir el formato requerido.

AudioPlayer usa la clase de adaptador MediaAdapter pasándole el tipo de audio deseado sin conocer la clase real que puede reproducir el formato deseado. AdapterPatternDemo , nuestra clase de demostración utilizará la clase AudioPlayer para reproducir varios formatos.

Paso 1

Cree interfaces para Media Player y Advanced Media Player.

MediaPlayer.java

public interface MediaPlayer {
   public void play(String audioType, String fileName);
}

AdvancedMediaPlayer.java

public interface AdvancedMediaPlayer {	
   public void playVlc(String fileName);
   public void playMp4(String fileName);
}

Paso 2

Cree clases concretas implementando la interfaz AdvancedMediaPlayer .

VlcPlayer.java

public class VlcPlayer implements AdvancedMediaPlayer{
   @Override
   public void playVlc(String fileName) {
      System.out.println("Playing vlc file. Name: "+ fileName);		
   }

   @Override
   public void playMp4(String fileName) {
      //do nothing
   }
}

Mp4Player.java

public class Mp4Player implements AdvancedMediaPlayer{

   @Override
   public void playVlc(String fileName) {
      //do nothing
   }

   @Override
   public void playMp4(String fileName) {
      System.out.println("Playing mp4 file. Name: "+ fileName);		
   }
}

Paso 3

Cree una clase de adaptador implementando la interfaz MediaPlayer .

MediaAdapter.java

public class MediaAdapter implements MediaPlayer {

   AdvancedMediaPlayer advancedMusicPlayer;

   public MediaAdapter(String audioType){
      if(audioType.equalsIgnoreCase("vlc") ){
         advancedMusicPlayer = new VlcPlayer();			
      } else if (audioType.equalsIgnoreCase("mp4")){
         advancedMusicPlayer = new Mp4Player();
      }	
   }

   @Override
   public void play(String audioType, String fileName) {
      if(audioType.equalsIgnoreCase("vlc")){
         advancedMusicPlayer.playVlc(fileName);
      }else if(audioType.equalsIgnoreCase("mp4")){
         advancedMusicPlayer.playMp4(fileName);
      }
   }
}

Etapa 4

Cree una clase concreta implementando la interfaz MediaPlayer .

AudioPlayer.java

public class AudioPlayer implements MediaPlayer {
   MediaAdapter mediaAdapter; 

   @Override
   public void play(String audioType, String fileName) {		

      //inbuilt support to play mp3 music files
      if(audioType.equalsIgnoreCase("mp3")){
         System.out.println("Playing mp3 file. Name: "+ fileName);			
      } 
      //mediaAdapter is providing support to play other file formats
      else if(audioType.equalsIgnoreCase("vlc") 
         || audioType.equalsIgnoreCase("mp4")){
         mediaAdapter = new MediaAdapter(audioType);
         mediaAdapter.play(audioType, fileName);
      }
      else{
         System.out.println("Invalid media. "+
            audioType + " format not supported");
      }
   }   
}

Paso 5

Utilice AudioPlayer para reproducir diferentes tipos de formatos de audio.

AdapterPatternDemo.java

public class AdapterPatternDemo {
   public static void main(String[] args) {
      AudioPlayer audioPlayer = new AudioPlayer();

      audioPlayer.play("mp3", "beyond the horizon.mp3");
      audioPlayer.play("mp4", "alone.mp4");
      audioPlayer.play("vlc", "far far away.vlc");
      audioPlayer.play("avi", "mind me.avi");
   }
}

Paso 6

Verifique la salida.

Playing mp3 file. Name: beyond the horizon.mp3
Playing mp4 file. Name: alone.mp4
Playing vlc file. Name: far far away.vlc
Invalid media. avi format not supported

Bridge se utiliza cuando necesitamos desacoplar una abstracción de su implementación para que los dos puedan variar de forma independiente. Este tipo de patrón de diseño viene bajo patrón estructural ya que este patrón desacopla la clase de implementación y la clase abstracta al proporcionar una estructura de puente entre ellas.

Este patrón involucra una interfaz que actúa como un puente que hace que la funcionalidad de clases concretas sea independiente de las clases implementadoras de interfaz. Ambos tipos de clases pueden modificarse estructuralmente sin que se afecten entre sí.

Estamos demostrando el uso del patrón Bridge a través del siguiente ejemplo en el que se puede dibujar un círculo en diferentes colores usando el mismo método de clase abstracta pero diferentes clases de implementación de puentes.

Implementación

Tenemos una interfaz DrawAPI que actúa como un implementador de puentes y clases concretas RedCircle , GreenCircle implementando la interfaz DrawAPI . Shape es una clase abstracta y utilizará el objeto de DrawAPI . BridgePatternDemo , nuestra clase de demostración usará la clase Shape para dibujar círculos de diferentes colores.

Paso 1

Cree la interfaz del implementador del puente.

DrawAPI.java

public interface DrawAPI {
   public void drawCircle(int radius, int x, int y);
}

Paso 2

Cree clases de implementador de puentes concretos que implementen la interfaz DrawAPI .

RedCircle.java

public class RedCircle implements DrawAPI {
   @Override
   public void drawCircle(int radius, int x, int y) {
      System.out.println("Drawing Circle[ color: red, radius: "
         + radius +", x: " +x+", "+ y +"]");
   }
}

GreenCircle.java

public class GreenCircle implements DrawAPI {
   @Override
   public void drawCircle(int radius, int x, int y) {
      System.out.println("Drawing Circle[ color: green, radius: "
         + radius +", x: " +x+", "+ y +"]");
   }
}

Paso 3

Cree una forma de clase abstracta utilizando la interfaz DrawAPI .

Shape.java

public abstract class Shape {
   protected DrawAPI drawAPI;
   protected Shape(DrawAPI drawAPI){
      this.drawAPI = drawAPI;
   }
   public abstract void draw();	
}

Etapa 4

Cree una clase concreta implementando la interfaz Shape .

Circle.java

public class Circle extends Shape {
   private int x, y, radius;

   public Circle(int x, int y, int radius, DrawAPI drawAPI) {
      super(drawAPI);
      this.x = x;  
      this.y = y;  
      this.radius = radius;
   }

   public void draw() {
      drawAPI.drawCircle(radius,x,y);
   }
}

Paso 5

Utilice las clases Shape y DrawAPI para dibujar círculos de diferentes colores.

BridgePatternDemo.java

public class BridgePatternDemo {
   public static void main(String[] args) {
      Shape redCircle = new Circle(100,100, 10, new RedCircle());
      Shape greenCircle = new Circle(100,100, 10, new GreenCircle());

      redCircle.draw();
      greenCircle.draw();
   }
}

Paso 6

Verifique la salida.

Drawing Circle[ color: red, radius: 10, x: 100, 100]
Drawing Circle[  color: green, radius: 10, x: 100, 100]

El patrón de filtro o patrón de criterios es un patrón de diseño que permite a los desarrolladores filtrar un conjunto de objetos, utilizando diferentes criterios, encadenándolos de forma desacoplada a través de operaciones lógicas. Este tipo de patrón de diseño se incluye en el patrón estructural, ya que este patrón combina múltiples criterios para obtener criterios únicos.

Implementación

Vamos a crear un objeto Person , una interfaz Criteria y clases concretas implementando esta interfaz para filtrar la lista de objetos Person . CriteriaPatternDemo , nuestra clase de demostración utiliza objetos Criteria para filtrar la Lista de objetos Persona en función de varios criterios y sus combinaciones.

Paso 1

Cree una clase sobre la que se aplicarán los criterios.

Person.java

public class Person {
	
   private String name;
   private String gender;
   private String maritalStatus;

   public Person(String name,String gender,String maritalStatus){
      this.name = name;
      this.gender = gender;
      this.maritalStatus = maritalStatus;		
   }

   public String getName() {
      return name;
   }
   public String getGender() {
      return gender;
   }
   public String getMaritalStatus() {
      return maritalStatus;
   }	
}

Paso 2

Cree una interfaz para Criterios.

Criteria.java

import java.util.List;

public interface Criteria {
   public List<Person> meetCriteria(List<Person> persons);
}

Paso 3

Cree clases concretas implementando la interfaz Criteria .

CriteriaMale.java

import java.util.ArrayList;
import java.util.List;

public class CriteriaMale implements Criteria {

   @Override
   public List<Person> meetCriteria(List<Person> persons) {
      List<Person> malePersons = new ArrayList<Person>(); 
      for (Person person : persons) {
         if(person.getGender().equalsIgnoreCase("MALE")){
            malePersons.add(person);
         }
      }
      return malePersons;
   }
}

CriteriaFemale.java

import java.util.ArrayList;
import java.util.List;

public class CriteriaFemale implements Criteria {

   @Override
   public List<Person> meetCriteria(List<Person> persons) {
      List<Person> femalePersons = new ArrayList<Person>(); 
      for (Person person : persons) {
         if(person.getGender().equalsIgnoreCase("FEMALE")){
            femalePersons.add(person);
         }
      }
      return femalePersons;
   }
}

CriteriaSingle.java

import java.util.ArrayList;
import java.util.List;

public class CriteriaSingle implements Criteria {

   @Override
   public List<Person> meetCriteria(List<Person> persons) {
      List<Person> singlePersons = new ArrayList<Person>(); 
      for (Person person : persons) {
         if(person.getMaritalStatus().equalsIgnoreCase("SINGLE")){
            singlePersons.add(person);
         }
      }
      return singlePersons;
   }
}

AndCriteria.java

import java.util.List;

public class AndCriteria implements Criteria {

   private Criteria criteria;
   private Criteria otherCriteria;

   public AndCriteria(Criteria criteria, Criteria otherCriteria) {
      this.criteria = criteria;
      this.otherCriteria = otherCriteria; 
   }

   @Override
   public List<Person> meetCriteria(List<Person> persons) {
      List<Person> firstCriteriaPersons = criteria.meetCriteria(persons);		
      return otherCriteria.meetCriteria(firstCriteriaPersons);
   }
}

OrCriteria.java

import java.util.List;

public class AndCriteria implements Criteria {

   private Criteria criteria;
   private Criteria otherCriteria;

   public AndCriteria(Criteria criteria, Criteria otherCriteria) {
      this.criteria = criteria;
      this.otherCriteria = otherCriteria; 
   }

   @Override
   public List<Person> meetCriteria(List<Person> persons) {
      List<Person> firstCriteriaItems = criteria.meetCriteria(persons);
      List<Person> otherCriteriaItems = otherCriteria.meetCriteria(persons);

      for (Person person : otherCriteriaItems) {
         if(!firstCriteriaItems.contains(person)){
	        firstCriteriaItems.add(person);
         }
      }	
      return firstCriteriaItems;
   }
}

Etapa 4

Utilice diferentes Criterios y su combinación para filtrar personas.

CriteriaPatternDemo.java

import java.util.ArrayList;
import java.util.List;

public class CriteriaPatternDemo {
   public static void main(String[] args) {
      List<Person> persons = new ArrayList<Person>();

      persons.add(new Person("Robert","Male", "Single"));
      persons.add(new Person("John","Male", "Married"));
      persons.add(new Person("Laura","Female", "Married"));
      persons.add(new Person("Diana","Female", "Single"));
      persons.add(new Person("Mike","Male", "Single"));
      persons.add(new Person("Bobby","Male", "Single"));

      Criteria male = new CriteriaMale();
      Criteria female = new CriteriaFemale();
      Criteria single = new CriteriaSingle();
      Criteria singleMale = new AndCriteria(single, male);
      Criteria singleOrFemale = new OrCriteria(single, female);

      System.out.println("Males: ");
      printPersons(male.meetCriteria(persons));

      System.out.println("\nFemales: ");
      printPersons(female.meetCriteria(persons));

      System.out.println("\nSingle Males: ");
      printPersons(singleMale.meetCriteria(persons));

      System.out.println("\nSingle Or Females: ");
      printPersons(singleOrFemale.meetCriteria(persons));
   }

   public static void printPersons(List<Person> persons){
      for (Person person : persons) {
         System.out.println("Person : [ Name : " + person.getName() 
            +", Gender : " + person.getGender() 
            +", Marital Status : " + person.getMaritalStatus()
            +" ]");
      }
   }      
}

Paso 5

Verifique la salida.

Males: 
Person : [ Name : Robert, Gender : Male, Marital Status : Single ]
Person : [ Name : John, Gender : Male, Marital Status : Married ]
Person : [ Name : Mike, Gender : Male, Marital Status : Single ]
Person : [ Name : Bobby, Gender : Male, Marital Status : Single ]

Females: 
Person : [ Name : Laura, Gender : Female, Marital Status : Married ]
Person : [ Name : Diana, Gender : Female, Marital Status : Single ]

Single Males: 
Person : [ Name : Robert, Gender : Male, Marital Status : Single ]
Person : [ Name : Mike, Gender : Male, Marital Status : Single ]
Person : [ Name : Bobby, Gender : Male, Marital Status : Single ]

Single Or Females: 
Person : [ Name : Robert, Gender : Male, Marital Status : Single ]
Person : [ Name : Diana, Gender : Female, Marital Status : Single ]
Person : [ Name : Mike, Gender : Male, Marital Status : Single ]
Person : [ Name : Bobby, Gender : Male, Marital Status : Single ]
Person : [ Name : Laura, Gender : Female, Marital Status : Married ]

El patrón compuesto se usa cuando necesitamos tratar un grupo de objetos de manera similar como un solo objeto. El patrón compuesto compone objetos en términos de una estructura de árbol para representar una parte y una jerarquía completa. Este tipo de patrón de diseño viene bajo patrón estructural ya que este patrón crea una estructura de árbol de grupo de objetos.

Este patrón crea una clase que contiene un grupo de sus propios objetos. Esta clase proporciona formas de modificar su grupo de mismos objetos.

Estamos demostrando el uso del patrón compuesto a través del siguiente ejemplo en el que se muestra la jerarquía de empleados de una organización.

Implementación

Tenemos una clase Employee que actúa como una clase de actor de patrón compuesto. CompositePatternDemo , nuestra clase de demostración utilizará la clase de empleado para agregar una jerarquía de nivel de departamento e imprimir todos los empleados.

Paso 1

Cree una clase de empleado que tenga una lista de objetos de empleado .

Employee.java

import java.util.ArrayList;
import java.util.List;

public class Employee {
   private String name;
   private String dept;
   private int salary;
   private List<Employee> subordinates;

   // constructor
   public Employee(String name,String dept, int sal) {
      this.name = name;
      this.dept = dept;
      this.salary = sal;
      subordinates = new ArrayList<Employee>();
   }

   public void add(Employee e) {
      subordinates.add(e);
   }

   public void remove(Employee e) {
      subordinates.remove(e);
   }

   public List<Employee> getSubordinates(){
     return subordinates;
   }

   public String toString(){
      return ("Employee :[ Name : "+ name 
      +", dept : "+ dept + ", salary :"
      + salary+" ]");
   }   
}

Paso 2

Utilice la clase Empleado para crear e imprimir la jerarquía de empleados.

CompositePatternDemo.java

public class CompositePatternDemo {
   public static void main(String[] args) {
      Employee CEO = new Employee("John","CEO", 30000);

      Employee headSales = new Employee("Robert","Head Sales", 20000);

      Employee headMarketing = new Employee("Michel","Head Marketing", 20000);

      Employee clerk1 = new Employee("Laura","Marketing", 10000);
      Employee clerk2 = new Employee("Bob","Marketing", 10000);

      Employee salesExecutive1 = new Employee("Richard","Sales", 10000);
      Employee salesExecutive2 = new Employee("Rob","Sales", 10000);

      CEO.add(headSales);
      CEO.add(headMarketing);

      headSales.add(salesExecutive1);
      headSales.add(salesExecutive2);

      headMarketing.add(clerk1);
      headMarketing.add(clerk2);

      //print all employees of the organization
      System.out.println(CEO); 
      for (Employee headEmployee : CEO.getSubordinates()) {
         System.out.println(headEmployee);
         for (Employee employee : headEmployee.getSubordinates()) {
            System.out.println(employee);
         }
      }		
   }
}

Paso 3

Verifique la salida.

Employee :[ Name : John, dept : CEO, salary :30000 ]
Employee :[ Name : Robert, dept : Head Sales, salary :20000 ]
Employee :[ Name : Richard, dept : Sales, salary :10000 ]
Employee :[ Name : Rob, dept : Sales, salary :10000 ]
Employee :[ Name : Michel, dept : Head Marketing, salary :20000 ]
Employee :[ Name : Laura, dept : Marketing, salary :10000 ]
Employee :[ Name : Bob, dept : Marketing, salary :10000 ]

El patrón de decorador permite agregar una nueva funcionalidad a un objeto existente sin alterar su estructura. Este tipo de patrón de diseño se incluye en el patrón estructural, ya que este patrón actúa como un envoltorio para la clase existente.

Este patrón crea una clase decoradora que envuelve la clase original y proporciona funcionalidad adicional manteniendo intacta la firma de los métodos de clase.

Estamos demostrando el uso del patrón Decorator a través del siguiente ejemplo en el que decoraremos una forma con algún color sin alterar la clase de forma.

Implementación

Vamos a crear una interfaz Shape y clases concretas implementando la interfaz Shape . Luego creamos una clase decoradora abstracta ShapeDecorator implementando la interfaz Shape y teniendo el objeto Shape como su variable de instancia.

RedShapeDecorator es una clase concreta que implementa ShapeDecorator .

DecoratorPatternDemo , nuestra clase de demostración utilizará RedShapeDecorator para decorar objetos Shape .

Paso 1

Crea una interfaz.

Shape.java

public interface Shape {
   void draw();
}

Paso 2

Crea clases concretas implementando la misma interfaz.

Rectangle.java

public class Rectangle implements Shape {

   @Override
   public void draw() {
      System.out.println("Shape: Rectangle");
   }
}

Circle.java

public class Circle implements Shape {

   @Override
   public void draw() {
      System.out.println("Shape: Circle");
   }
}

Paso 3

Cree una clase decoradora abstracta implementando la interfaz Shape .

ShapeDecorator.java

public abstract class ShapeDecorator implements Shape {
   protected Shape decoratedShape;

   public ShapeDecorator(Shape decoratedShape){
      this.decoratedShape = decoratedShape;
   }

   public void draw(){
      decoratedShape.draw();
   }	
}

Etapa 4

Cree una clase de decorador concreta ampliando la clase ShapeDecorator .

RedShapeDecorator.java

public class RedShapeDecorator extends ShapeDecorator {

   public RedShapeDecorator(Shape decoratedShape) {
      super(decoratedShape);		
   }

   @Override
   public void draw() {
      decoratedShape.draw();	       
      setRedBorder(decoratedShape);
   }

   private void setRedBorder(Shape decoratedShape){
      System.out.println("Border Color: Red");
   }
}

Paso 5

Utilice RedShapeDecorator para decorar objetos Shape .

DecoratorPatternDemo.java

public class DecoratorPatternDemo {
   public static void main(String[] args) {

      Shape circle = new Circle();

      Shape redCircle = new RedShapeDecorator(new Circle());

      Shape redRectangle = new RedShapeDecorator(new Rectangle());
      System.out.println("Circle with normal border");
      circle.draw();

      System.out.println("\nCircle of red border");
      redCircle.draw();

      System.out.println("\nRectangle of red border");
      redRectangle.draw();
   }
}

Paso 6

Verifique la salida.

Circle with normal border
Shape: Circle

Circle of red border
Shape: Circle
Border Color: Red

Rectangle of red border
Shape: Rectangle
Border Color: Red

El patrón de fachada oculta las complejidades del sistema y proporciona una interfaz al cliente mediante la cual el cliente puede acceder al sistema. Este tipo de patrón de diseño viene bajo patrón estructural ya que este patrón agrega una interfaz al sistema de salida para ocultar sus complejidades.

Este patrón involucra una sola clase que proporciona métodos simplificados que son requeridos por el cliente y delega llamadas a métodos de clases del sistema existentes.

Implementación

Vamos a crear una interfaz Shape y clases concretas implementando la interfaz Shape . Una clase de fachada ShapeMaker se define como el siguiente paso.

La clase ShapeMaker usa las clases concretas para delegar las llamadas de los usuarios a estas clases. FacadePatternDemo , nuestra clase de demostración utilizará la clase ShapeMaker para mostrar los resultados.

Paso 1

Crea una interfaz.

Shape.java

public interface Shape {
   void draw();
}

Paso 2

Crea clases concretas implementando la misma interfaz.

Rectangle.java

public class Rectangle implements Shape {

   @Override
   public void draw() {
      System.out.println("Rectangle::draw()");
   }
}

Square.java

public class Square implements Shape {

   @Override
   public void draw() {
      System.out.println("Square::draw()");
   }
}

Circle.java

public class Circle implements Shape {

   @Override
   public void draw() {
      System.out.println("Circle::draw()");
   }
}

Paso 3

Crea una clase de fachada.

ShapeMaker.java

public class ShapeMaker {
   private Shape circle;
   private Shape rectangle;
   private Shape square;

   public ShapeMaker() {
      circle = new Circle();
      rectangle = new Rectangle();
      square = new Square();
   }

   public void drawCircle(){
      circle.draw();
   }
   public void drawRectangle(){
      rectangle.draw();
   }
   public void drawSquare(){
      square.draw();
   }
}

Etapa 4

Usa la fachada para dibujar varios tipos de formas.

FacadePatternDemo.java

public class FacadePatternDemo {
   public static void main(String[] args) {
      ShapeMaker shapeMaker = new ShapeMaker();

      shapeMaker.drawCircle();
      shapeMaker.drawRectangle();
      shapeMaker.drawSquare();		
   }
}

Paso 5

Verifique la salida.

Circle::draw()
Rectangle::draw()
Square::draw()

El patrón Flyweight se usa principalmente para reducir la cantidad de objetos creados, disminuir la huella de memoria y aumentar el rendimiento. Este tipo de patrón de diseño se incluye en el patrón estructural, ya que este patrón proporciona formas de disminuir el recuento de objetos, mejorando así la estructura de objetos requerida por la aplicación.

El patrón Flyweight intenta reutilizar objetos de tipo similar ya existentes almacenándolos y crea un nuevo objeto cuando no se encuentra ningún objeto coincidente. Demostraremos este patrón dibujando 20 círculos de diferentes ubicaciones, pero crearemos solo 5 objetos. Solo hay 5 colores disponibles, por lo que la propiedad de color se usa para verificar los objetos Circle ya existentes .

Implementación

Vamos a crear una interfaz Shape y una clase concreta Circle implementando la interfaz Shape . Una clase de fábrica ShapeFactory se define como el siguiente paso.

ShapeFactory tiene un HashMap of Circle que tiene la clave como color del objeto Circle . Siempre que llegue una solicitud para crear un círculo de color particular para ShapeFactory . ShapeFactory comprueba el objeto de círculo en su HashMap , si se encuentra un objeto de Circle , ese objeto se devuelve; de ​​lo contrario, se crea un nuevo objeto, se almacena en hashmap para uso futuro y se devuelve al cliente.

FlyWeightPatternDemo , nuestra clase de demostración utilizará ShapeFactory para obtener un objeto Shape . Pasará información ( rojo / verde / azul / negro / blanco ) a ShapeFactory para obtener el círculo del color deseado que necesita.

Paso 1

Crea una interfaz.

Shape.java

public interface Shape {
   void draw();
}

Paso 2

Cree una clase concreta implementando la misma interfaz.

Circle.java

public class Circle implements Shape {
   private String color;
   private int x;
   private int y;
   private int radius;

   public Circle(String color){
      this.color = color;		
   }

   public void setX(int x) {
      this.x = x;
   }

   public void setY(int y) {
      this.y = y;
   }

   public void setRadius(int radius) {
      this.radius = radius;
   }

   @Override
   public void draw() {
      System.out.println("Circle: Draw() [Color : " + color 
         +", x : " + x +", y :" + y +", radius :" + radius);
   }
}

Paso 3

Cree una fábrica para generar un objeto de clase concreta basado en información dada.

ShapeFactory.java

import java.util.HashMap;

public class ShapeFactory {

   // Uncomment the compiler directive line and
   // javac *.java will compile properly.
   // @SuppressWarnings("unchecked")
   private static final HashMap circleMap = new HashMap();

   public static Shape getCircle(String color) {
      Circle circle = (Circle)circleMap.get(color);

      if(circle == null) {
         circle = new Circle(color);
         circleMap.put(color, circle);
         System.out.println("Creating circle of color : " + color);
      }
      return circle;
   }
}

Etapa 4

Utilice Factory para obtener un objeto de clase concreta pasando información como el color.

FlyweightPatternDemo.java

public class FlyweightPatternDemo {
   private static final String colors[] = 
      { "Red", "Green", "Blue", "White", "Black" };
   public static void main(String[] args) {

      for(int i=0; i < 20; ++i) {
         Circle circle = 
            (Circle)ShapeFactory.getCircle(getRandomColor());
         circle.setX(getRandomX());
         circle.setY(getRandomY());
         circle.setRadius(100);
         circle.draw();
      }
   }
   private static String getRandomColor() {
      return colors[(int)(Math.random()*colors.length)];
   }
   private static int getRandomX() {
      return (int)(Math.random()*100 );
   }
   private static int getRandomY() {
      return (int)(Math.random()*100);
   }
}

Paso 5

Verifique la salida.

Creating circle of color : Black
Circle: Draw() [Color : Black, x : 36, y :71, radius :100
Creating circle of color : Green
Circle: Draw() [Color : Green, x : 27, y :27, radius :100
Creating circle of color : White
Circle: Draw() [Color : White, x : 64, y :10, radius :100
Creating circle of color : Red
Circle: Draw() [Color : Red, x : 15, y :44, radius :100
Circle: Draw() [Color : Green, x : 19, y :10, radius :100
Circle: Draw() [Color : Green, x : 94, y :32, radius :100
Circle: Draw() [Color : White, x : 69, y :98, radius :100
Creating circle of color : Blue
Circle: Draw() [Color : Blue, x : 13, y :4, radius :100
Circle: Draw() [Color : Green, x : 21, y :21, radius :100
Circle: Draw() [Color : Blue, x : 55, y :86, radius :100
Circle: Draw() [Color : White, x : 90, y :70, radius :100
Circle: Draw() [Color : Green, x : 78, y :3, radius :100
Circle: Draw() [Color : Green, x : 64, y :89, radius :100
Circle: Draw() [Color : Blue, x : 3, y :91, radius :100
Circle: Draw() [Color : Blue, x : 62, y :82, radius :100
Circle: Draw() [Color : Green, x : 97, y :61, radius :100
Circle: Draw() [Color : Green, x : 86, y :12, radius :100
Circle: Draw() [Color : Green, x : 38, y :93, radius :100
Circle: Draw() [Color : Red, x : 76, y :82, radius :100
Circle: Draw() [Color : Blue, x : 95, y :82, radius :100

En el patrón Proxy, una clase representa la funcionalidad de otra clase. Este tipo de patrón de diseño viene bajo patrón estructural.

En el patrón Proxy, creamos un objeto que tiene un objeto original para interconectar su funcionalidad al mundo exterior.

Implementación

Vamos a crear una interfaz de imagen y clases concretas implementando la interfaz de imagen . ProxyImage es una clase de proxy para reducir la huella de memoria de la carga de objetos RealImage .

ProxyPatternDemo , nuestra clase de demostración utilizará ProxyImage para que un objeto Image se cargue y se muestre según sea necesario.

Paso 1

Crea una interfaz.

Image.java

public interface Image {
   void display();
}

Paso 2

Crea clases concretas implementando la misma interfaz.

RealImage.java

public class RealImage implements Image {

   private String fileName;

   public RealImage(String fileName){
      this.fileName = fileName;
      loadFromDisk(fileName);
   }

   @Override
   public void display() {
      System.out.println("Displaying " + fileName);
   }

   private void loadFromDisk(String fileName){
      System.out.println("Loading " + fileName);
   }
}

ProxyImage.java

public class ProxyImage implements Image{

   private RealImage realImage;
   private String fileName;

   public ProxyImage(String fileName){
      this.fileName = fileName;
   }

   @Override
   public void display() {
      if(realImage == null){
         realImage = new RealImage(fileName);
      }
      realImage.display();
   }
}

Paso 3

Utilice ProxyImage para obtener el objeto de la clase RealImage cuando sea necesario.

ProxyPatternDemo.java

public class ProxyPatternDemo {
	
   public static void main(String[] args) {
      Image image = new ProxyImage("test_10mb.jpg");

      //image will be loaded from disk
      image.display(); 
      System.out.println("");
      //image will not be loaded from disk
      image.display(); 	
   }
}

Etapa 4

Verifique la salida.

Loading test_10mb.jpg
Displaying test_10mb.jpg

Displaying test_10mb.jpg

Como sugiere el nombre, el patrón de cadena de responsabilidad crea una cadena de objetos receptores para una solicitud. Este patrón desacopla al remitente y al receptor de una solicitud según el tipo de solicitud. Este patrón viene bajo patrones de comportamiento.

En este patrón, normalmente cada receptor contiene una referencia a otro receptor. Si un objeto no puede manejar la solicitud, pasa la misma al siguiente receptor y así sucesivamente.

Implementación

Hemos creado una clase abstracta AbstractLogger con un nivel de registro. Luego, hemos creado tres tipos de registradores que amplían AbstractLogger . Cada registrador verifica el nivel del mensaje a su nivel e imprime en consecuencia; de lo contrario, no imprime y pasa el mensaje al siguiente registrador.

Paso 1

Cree una clase de registrador abstracto.

AbstractLogger.java

public abstract class AbstractLogger {
   public static int INFO = 1;
   public static int DEBUG = 2;
   public static int ERROR = 3;

   protected int level;

   //next element in chain or responsibility
   protected AbstractLogger nextLogger;

   public void setNextLogger(AbstractLogger nextLogger){
      this.nextLogger = nextLogger;
   }

   public void logMessage(int level, String message){
      if(this.level <= level){
         write(message);
      }
      if(nextLogger !=null){
         nextLogger.logMessage(level, message);
      }
   }

   abstract protected void write(String message);
	
}

Paso 2

Crea clases concretas ampliando el registrador.

ConsoleLogger.java

public class ConsoleLogger extends AbstractLogger {

   public ConsoleLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {		
      System.out.println("Standard Console::Logger: " + message);
   }
}

ErrorLogger.java

public class ErrorLogger extends AbstractLogger {

   public ErrorLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {		
      System.out.println("Error Console::Logger: " + message);
   }
}

FileLogger.java

public class FileLogger extends AbstractLogger {

   public FileLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {		
      System.out.println("File::Logger: " + message);
   }
}

Paso 3

Crea diferentes tipos de registradores. Asígneles niveles de error y configure el siguiente registrador en cada registrador. El siguiente registrador en cada registrador representa la parte de la cadena.

ChainPatternDemo.java

public class ChainPatternDemo {
	
   private static AbstractLogger getChainOfLoggers(){

      AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
      AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
      AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);

      errorLogger.setNextLogger(fileLogger);
      fileLogger.setNextLogger(consoleLogger);

      return errorLogger;	
   }

   public static void main(String[] args) {
      AbstractLogger loggerChain = getChainOfLoggers();

      loggerChain.logMessage(AbstractLogger.INFO, 
         "This is an information.");

      loggerChain.logMessage(AbstractLogger.DEBUG, 
         "This is an debug level information.");

      loggerChain.logMessage(AbstractLogger.ERROR, 
         "This is an error information.");
   }
}

Etapa 4

Verifique la salida.

Standard Console::Logger: This is an information.
File::Logger: This is an debug level information.
Standard Console::Logger: This is an debug level information.
Error Console::Logger: This is an error information.
File::Logger: This is an error information.
Standard Console::Logger: This is an error information.

El patrón de comando es un patrón de diseño basado en datos y se incluye en la categoría de patrón de comportamiento. Una solicitud se envuelve bajo un objeto como comando y se pasa al objeto invocador. El objeto invocador busca el objeto apropiado que puede manejar este comando y pasar el comando al objeto correspondiente y ese objeto ejecuta el comando.

Implementación

Hemos creado una orden de interfaz que actúa como un comando. Hemos creado un archivo de clase que actúa como una petición. Tenemos clases de comando concretas BuyStock y SellStock que implementan la interfaz de pedidos que realizarán el procesamiento real del comando. Se crea una clase Broker que actúa como objeto invocador. Puede tomar pedidos y realizar pedidos.

El objeto de intermediario utiliza un patrón de comando para identificar qué objeto ejecutará qué comando según el tipo de comando. CommandPatternDemo , nuestra clase de demostración utilizará la clase Broker para demostrar el patrón de comando.

Paso 1

Cree una interfaz de comando.

Order.java

public interface Order {
   void execute();
}

Paso 2

Crea una clase de solicitud.

Stock.java

public class Stock {
	
   private String name = "ABC";
   private int quantity = 10;

   public void buy(){
      System.out.println("Stock [ Name: "+name+", 
         Quantity: " + quantity +" ] bought");
   }
   public void sell(){
      System.out.println("Stock [ Name: "+name+", 
         Quantity: " + quantity +" ] sold");
   }
}

Paso 3

Cree clases concretas implementando la interfaz Order .

BuyStock.java

public class BuyStock implements Order {
   private Stock abcStock;

   public BuyStock(Stock abcStock){
      this.abcStock = abcStock;
   }

   public void execute() {
      abcStock.buy();
   }
}

SellStock.java

public class SellStock implements Order {
   private Stock abcStock;

   public SellStock(Stock abcStock){
      this.abcStock = abcStock;
   }

   public void execute() {
      abcStock.sell();
   }
}

Etapa 4

Crea una clase de invocador de comandos.

Broker.java

import java.util.ArrayList;
import java.util.List;

   public class Broker {
   private List<Order> orderList = new ArrayList<Order>(); 

   public void takeOrder(Order order){
      orderList.add(order);		
   }

   public void placeOrders(){
      for (Order order : orderList) {
         order.execute();
      }
      orderList.clear();
   }
}

Paso 5

Utilice la clase Broker para tomar y ejecutar comandos.

CommandPatternDemo.java

public class CommandPatternDemo {
   public static void main(String[] args) {
      Stock abcStock = new Stock();

      BuyStock buyStockOrder = new BuyStock(abcStock);
      SellStock sellStockOrder = new SellStock(abcStock);

      Broker broker = new Broker();
      broker.takeOrder(buyStockOrder);
      broker.takeOrder(sellStockOrder);

      broker.placeOrders();
   }
}

Paso 6

Verifique la salida.

Stock [ Name: ABC, Quantity: 10 ] bought
Stock [ Name: ABC, Quantity: 10 ] sold

El patrón de intérprete proporciona una forma de evaluar la gramática o la expresión del lenguaje. Este tipo de patrón viene bajo patrones de comportamiento. Este patrón implica implementar una interfaz de expresión que indica interpretar un contexto particular. Este patrón se utiliza en el análisis de SQL, el motor de procesamiento de símbolos, etc.

Implementación

Vamos a crear una interfaz Expression y clases concretas implementando la interfaz Expression . Se define una clase TerminalExpression que actúa como intérprete principal del contexto en cuestión. Otras clases OrExpression , AndExpression se utilizan para crear expresiones combinacionales.

InterpreterPatternDemo , nuestra clase de demostración utilizará la clase Expression para crear reglas y demostrar el análisis de expresiones.

Paso 1

Crea una interfaz de expresión.

Expression.java

public interface Expression {
   public boolean interpret(String context);
}

Paso 2

Cree clases concretas implementando la interfaz anterior.

TerminalExpression.java

public class TerminalExpression implements Expression {
	
   private String data;

   public TerminalExpression(String data){
      this.data = data; 
   }

   @Override
   public boolean interpret(String context) {
      if(context.contains(data)){
         return true;
      }
      return false;
   }
}

OrExpression.java

public class OrExpression implements Expression {
	 
   private Expression expr1 = null;
   private Expression expr2 = null;

   public OrExpression(Expression expr1, Expression expr2) { 
      this.expr1 = expr1;
      this.expr2 = expr2;
   }

   @Override
   public boolean interpret(String context) {		
      return expr1.interpret(context) || expr2.interpret(context);
   }
}

AndExpression.java

public class AndExpression implements Expression {
	 
   private Expression expr1 = null;
   private Expression expr2 = null;

   public AndExpression(Expression expr1, Expression expr2) { 
      this.expr1 = expr1;
      this.expr2 = expr2;
   }

   @Override
   public boolean interpret(String context) {		
      return expr1.interpret(context) && expr2.interpret(context);
   }
}

Paso 3

InterpreterPatternDemo usa la clase Expression para crear reglas y luego analizarlas.

InterpreterPatternDemo.java

public class InterpreterPatternDemo {

   //Rule: Robert and John are male
   public static Expression getMaleExpression(){
      Expression robert = new TerminalExpression("Robert");
      Expression john = new TerminalExpression("John");
      return new OrExpression(robert, john);		
   }

   //Rule: Julie is a married women
   public static Expression getMarriedWomanExpression(){
      Expression julie = new TerminalExpression("Julie");
      Expression married = new TerminalExpression("Married");
      return new AndExpression(julie, married);		
   }

   public static void main(String[] args) {
      Expression isMale = getMaleExpression();
      Expression isMarriedWoman = getMarriedWomanExpression();

      System.out.println("John is male? " + isMale.interpret("John"));
      System.out.println("Julie is a married women? " 
      + isMarriedWoman.interpret("Married Julie"));
   }
}

Etapa 4

Verifique la salida.

John is male? true
Julie is a married women? true

El patrón de iterador es un patrón de diseño muy utilizado en el entorno de programación Java y .Net. Este patrón se utiliza para obtener una forma de acceder a los elementos de un objeto de colección de manera secuencial sin necesidad de conocer su representación subyacente.

El patrón de iterador se incluye en la categoría de patrón de comportamiento.

Implementación

Vamos a crear una interfaz de iterador que narra el método de navegación y una interfaz de contenedor que devuelve el iterador. Las clases concretas que implementen la interfaz Container serán responsables de implementar la interfaz Iterator y usarla

IteratorPatternDemo , nuestra clase de demostración usará NamesRepository , una implementación de clase concreta para imprimir un Names almacenado como una colección en NamesRepository .

Paso 1

Crea interfaces.

Iterator.java

public interface Iterator {
   public boolean hasNext();
   public Object next();
}

Container.java

public interface Container {
   public Iterator getIterator();
}

Paso 2

Cree una clase concreta implementando la interfaz Container . Esta clase tiene clase interna NameIterator aplicación del iterador interfaz.

NameRepository.java

public class NameRepository implements Container {
   public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};

   @Override
   public Iterator getIterator() {
      return new NameIterator();
   }

   private class NameIterator implements Iterator {

      int index;

      @Override
      public boolean hasNext() {
         if(index < names.length){
            return true;
         }
         return false;
      }

      @Override
      public Object next() {
         if(this.hasNext()){
            return names[index++];
         }
         return null;
      }		
   }
}

Paso 3

Utilice NameRepository para obtener iteradores e imprimir nombres.

IteratorPatternDemo.java

public class IteratorPatternDemo {
	
   public static void main(String[] args) {
      NameRepository namesRepository = new NameRepository();

      for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){
         String name = (String)iter.next();
         System.out.println("Name : " + name);
      } 	
   }
}

Etapa 4

Verifique la salida.

Name : Robert
Name : John
Name : Julie
Name : Lora

El patrón de mediador se utiliza para reducir la complejidad de la comunicación entre múltiples objetos o clases. Este patrón proporciona una clase mediadora que normalmente maneja todas las comunicaciones entre diferentes clases y soporta la facilidad de mantenimiento del código mediante un acoplamiento flexible. El patrón de mediador se incluye en la categoría de patrón de comportamiento.

Implementación

Estamos demostrando un patrón de mediador con un ejemplo de una sala de chat donde varios usuarios pueden enviar mensajes a la sala de chat y es responsabilidad de la sala de chat mostrar los mensajes a todos los usuarios. Hemos creado dos clases ChatRoom y User . Los objetos de usuario utilizarán el método ChatRoom para compartir sus mensajes.

MediatorPatternDemo , nuestra clase de demostración utilizará objetos de usuario para mostrar la comunicación entre ellos.

Paso 1

Crea una clase de mediador.

ChatRoom.java

import java.util.Date;

public class ChatRoom {
   public static void showMessage(User user, String message){
      System.out.println(new Date().toString()
         + " [" + user.getName() +"] : " + message);
   }
}

Paso 2

Crear clase de usuario

User.java

public class User {
   private String name;

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public User(String name){
      this.name  = name;
   }

   public void sendMessage(String message){
      ChatRoom.showMessage(this,message);
   }
}

Paso 3

Utilice el objeto Usuario para mostrar las comunicaciones entre ellos.

MediatorPatternDemo.java

public class MediatorPatternDemo {
   public static void main(String[] args) {
      User robert = new User("Robert");
      User john = new User("John");

      robert.sendMessage("Hi! John!");
      john.sendMessage("Hello! Robert!");
   }
}

Etapa 4

Verifique la salida.

Thu Jan 31 16:05:46 IST 2013 [Robert] : Hi! John!
Thu Jan 31 16:05:46 IST 2013 [John] : Hello! Robert!

El patrón Memento se usa para reducir dónde queremos restaurar el estado de un objeto a un estado anterior. El patrón de recuerdo se incluye en la categoría de patrón de comportamiento.

Implementación

El patrón Memento utiliza tres clases de actores. Memento contiene el estado de un objeto que se va a restaurar. Originator crea y almacena estados en objetos Memento y el objeto Caretaker que es responsable de restaurar el estado del objeto desde Memento. Hemos creado las clases Memento , Originator y CareTaker .

MementoPatternDemo , nuestra clase de demostración utilizará los objetos CareTaker y Originator para mostrar la restauración de los estados de los objetos.

Paso 1

Crea una clase Memento.

Memento.java

public class Memento {
   private String state;

   public Memento(String state){
      this.state = state;
   }

   public String getState(){
      return state;
   }	
}

Paso 2

Crear clase Originador

Originator.java

public class Originator {
   private String state;

   public void setState(String state){
      this.state = state;
   }

   public String getState(){
      return state;
   }

   public Memento saveStateToMemento(){
      return new Memento(state);
   }

   public void getStateFromMemento(Memento Memento){
      state = memento.getState();
   }
}

Paso 3

Crear clase CareTaker

CareTaker.java

import java.util.ArrayList;
import java.util.List;

public class CareTaker {
   private List<Memento> mementoList = new ArrayList<Memento>();

   public void add(Memento state){
      mementoList.add(state);
   }

   public Memento get(int index){
      return mementoList.get(index);
   }
}

Etapa 4

Utilice los objetos CareTaker y Originator .

MementoPatternDemo.java

public class MementoPatternDemo {
   public static void main(String[] args) {
      Originator originator = new Originator();
      CareTaker careTaker = new CareTaker();
      originator.setState("State #1");
      originator.setState("State #2");
      careTaker.add(originator.saveStateToMemento());
      originator.setState("State #3");
      careTaker.add(originator.saveStateToMemento());
      originator.setState("State #4");

      System.out.println("Current State: " + originator.getState());		
      originator.getStateFromMemento(careTaker.get(0));
      System.out.println("First saved State: " + originator.getState());
      originator.getStateFromMemento(careTaker.get(1));
      System.out.println("Second saved State: " + originator.getState());
   }
}

Paso 5

Verifique la salida.

Current State: State #4
First saved State: State #2
Second saved State: State #3

El patrón de observador se utiliza cuando hay una relación de uno a varios entre objetos, por ejemplo, si un objeto se modifica, sus objetos dependientes deben ser notificados automáticamente. El patrón de observador se incluye en la categoría de patrón de comportamiento.

Implementación

El patrón de observador utiliza tres clases de actores. Sujeto, observador y cliente. Sujeto, un objeto que tiene métodos para adjuntar y desvincular observadores a un objeto cliente. Hemos creado clases de sujeto , clase abstracta de observador y clases concretas ampliando la clase abstracta de observador .

ObserverPatternDemo , nuestra clase de demostración utilizará Subject y objetos de clase concretos para mostrar el patrón del observador en acción.

Paso 1

Crear clase de asignatura.

Subject.java

import java.util.ArrayList;
import java.util.List;

public class Subject {
	
   private List<Observer> observers 
      = new ArrayList<Observer>();
   private int state;

   public int getState() {
      return state;
   }

   public void setState(int state) {
      this.state = state;
      notifyAllObservers();
   }

   public void attach(Observer observer){
      observers.add(observer);		
   }

   public void notifyAllObservers(){
      for (Observer observer : observers) {
         observer.update();
      }
   } 	
}

Paso 2

Crear clase de observador.

Observer.java

public abstract class Observer {
   protected Subject subject;
   public abstract void update();
}

Paso 3

Crea clases de observadores concretas

BinaryObserver.java

public class BinaryObserver extends Observer{

   public BinaryObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }

   @Override
   public void update() {
      System.out.println( "Binary String: " 
      + Integer.toBinaryString( subject.getState() ) ); 
   }
}

OctalObserver.java

public class OctalObserver extends Observer{

   public OctalObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }

   @Override
   public void update() {
     System.out.println( "Octal String: " 
     + Integer.toOctalString( subject.getState() ) ); 
   }
}

HexaObserver.java

public class HexaObserver extends Observer{

   public HexaObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }

   @Override
   public void update() {
      System.out.println( "Hex String: " 
      + Integer.toHexString( subject.getState() ).toUpperCase() ); 
   }
}

Etapa 4

Utilice Sujeto y objetos observadores concretos.

ObserverPatternDemo.java

public class ObserverPatternDemo {
   public static void main(String[] args) {
      Subject subject = new Subject();

      new HexaObserver(subject);
      new OctalObserver(subject);
      new BinaryObserver(subject);

      System.out.println("First state change: 15");	
      subject.setState(15);
      System.out.println("Second state change: 10");	
      subject.setState(10);
   }
}

Paso 5

Verifique la salida.

First state change: 15
Hex String: F
Octal String: 17
Binary String: 1111
Second state change: 10
Hex String: A
Octal String: 12
Binary String: 1010

En el patrón de estado, el comportamiento de una clase cambia según su estado. Este tipo de patrón de diseño se incluye en el patrón de comportamiento.

En el patrón de estado, creamos objetos que representan varios estados y un objeto de contexto cuyo comportamiento varía a medida que cambia su objeto de estado.

Implementación

Vamos a crear una interfaz de estado que defina una acción y clases de estado concretas que implementen la interfaz de estado . El contexto es una clase que conlleva un Estado.

StaePatternDemo , nuestra clase de demostración utilizará objetos de contexto y estado para demostrar el cambio en el comportamiento de contexto según el tipo de estado en el que se encuentra.

Paso 1

Crea una interfaz.

Image.java

public interface State {
   public void doAction(Context context);
}

Paso 2

Crea clases concretas implementando la misma interfaz.

StartState.java

public class StartState implements State {

   public void doAction(Context context) {
      System.out.println("Player is in start state");
      context.setState(this);	
   }

   public String toString(){
      return "Start State";
   }
}

StopState.java

public class StopState implements State {

   public void doAction(Context context) {
      System.out.println("Player is in stop state");
      context.setState(this);	
   }

   public String toString(){
      return "Stop State";
   }
}

Paso 3

Crear clase de contexto .

Context.java

public class Context {
   private State state;

   public Context(){
      state = null;
   }

   public void setState(State state){
      this.state = state;		
   }

   public State getState(){
      return state;
   }
}

Etapa 4

Utilice el contexto para ver cambios en el comportamiento cuando cambia el estado .

StatePatternDemo.java

public class StatePatternDemo {
   public static void main(String[] args) {
      Context context = new Context();

      StartState startState = new StartState();
      startState.doAction(context);

      System.out.println(context.getState().toString());

      StopState stopState = new StopState();
      stopState.doAction(context);

      System.out.println(context.getState().toString());
   }
}

Paso 5

Verifique la salida.

Player is in start state
Start State
Player is in stop state
Stop State

En el patrón de objeto nulo, un objeto nulo reemplaza la comprobación de la instancia de objeto NULO. En lugar de poner if check para un valor nulo, Null Object refleja una relación de no hacer nada. Dicho objeto nulo también se puede utilizar para proporcionar un comportamiento predeterminado en caso de que los datos no estén disponibles.

En el patrón de objeto nulo, creamos una clase abstracta que especifica las diversas operaciones que se realizarán, concreamos clases que extienden esta clase y una clase de objeto nulo que proporciona una implementación sin hacer nada de esta clase y se usará sin problemas cuando necesitemos verificar el valor nulo.

Implementación

Vamos a crear una clase abstracta AbstractCustomer que defina las operaciones, aquí el nombre del cliente y las clases concretas que extienden la clase AbstractCustomer . Se crea una clase de fábrica CustomerFactory para devolver objetos RealCustomer o NullCustomer en función del nombre del cliente que se le ha pasado.

NullPatternDemo , nuestra clase de demostración utilizará CustomerFactory para demostrar el uso del patrón de objeto nulo.

Paso 1

Crea una clase abstracta.

AbstractCustomer.java

public abstract class AbstractCustomer {
   protected String name;
   public abstract boolean isNil();
   public abstract String getName();
}

Paso 2

Cree clases concretas ampliando la clase anterior.

RealCustomer.java

public class RealCustomer extends AbstractCustomer {

   public RealCustomer(String name) {
      this.name = name;		
   }
   
   @Override
   public String getName() {
      return name;
   }
   
   @Override
   public boolean isNil() {
      return false;
   }
}

NullCustomer.java

public class NullCustomer extends AbstractCustomer {

   @Override
   public String getName() {
      return "Not Available in Customer Database";
   }

   @Override
   public boolean isNil() {
      return true;
   }
}

Paso 3

Crear clase CustomerFactory .

CustomerFactory.java

public class CustomerFactory {
	
   public static final String[] names = {"Rob", "Joe", "Julie"};

   public static AbstractCustomer getCustomer(String name){
      for (int i = 0; i < names.length; i++) {
         if (names[i].equalsIgnoreCase(name)){
            return new RealCustomer(name);
         }
      }
      return new NullCustomer();
   }
}

Etapa 4

Utilice CustomerFactory para obtener objetos RealCustomer o NullCustomer en función del nombre del cliente que se le haya pasado.

NullPatternDemo.java

public class NullPatternDemo {
   public static void main(String[] args) {

      AbstractCustomer customer1 = CustomerFactory.getCustomer("Rob");
      AbstractCustomer customer2 = CustomerFactory.getCustomer("Bob");
      AbstractCustomer customer3 = CustomerFactory.getCustomer("Julie");
      AbstractCustomer customer4 = CustomerFactory.getCustomer("Laura");

      System.out.println("Customers");
      System.out.println(customer1.getName());
      System.out.println(customer2.getName());
      System.out.println(customer3.getName());
      System.out.println(customer4.getName());
   }
}

Paso 5

Verifique la salida.

Customers
Rob
Not Available in Customer Database
Julie
Not Available in Customer Database

En el patrón de estrategia, el comportamiento de una clase o su algoritmo se puede cambiar en tiempo de ejecución. Este tipo de patrón de diseño se incluye en el patrón de comportamiento.

En el patrón de estrategia, creamos objetos que representan varias estrategias y un objeto de contexto cuyo comportamiento varía según su objeto de estrategia. El objeto de estrategia cambia el algoritmo de ejecución del objeto de contexto.

Implementación

Vamos a crear una interfaz de estrategia que defina una acción y clases de estrategia concretas que implementen la interfaz de estrategia . El contexto es una clase que usa una estrategia.

StrategyPatternDemo , nuestra clase de demostración utilizará objetos de contexto y estrategia para demostrar el cambio en el comportamiento del contexto en función de la estrategia que implementa o utiliza.

Paso 1

Crea una interfaz.

Strategy.java

public interface Strategy {
   public int doOperation(int num1, int num2);
}

Paso 2

Crea clases concretas implementando la misma interfaz.

OperationAdd.java

public class OperationAdd implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 + num2;
   }
}

OperationSubstract.java

public class OperationSubstract implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 - num2;
   }
}

OperationMultiply.java

public class OperationMultiply implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 * num2;
   }
}

Paso 3

Crear clase de contexto .

Context.java

public class Context {
   private Strategy strategy;

   public Context(Strategy strategy){
      this.strategy = strategy;
   }

   public int executeStrategy(int num1, int num2){
      return strategy.doOperation(num1, num2);
   }
}

Etapa 4

Utilice el contexto para ver cambios en el comportamiento cuando cambia su estrategia .

StatePatternDemo.java

public class StrategyPatternDemo {
   public static void main(String[] args) {
      Context context = new Context(new OperationAdd());		
      System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

      context = new Context(new OperationSubstract());		
      System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

      context = new Context(new OperationMultiply());		
      System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
   }
}

Paso 5

Verifique la salida.

10 + 5 = 15
10 - 5 = 5
10 * 5 = 50

En el patrón de plantilla, una clase abstracta expone formas / plantillas definidas para ejecutar sus métodos. Sus subclases pueden anular las implementaciones del método según la necesidad, pero la invocación debe realizarse de la misma manera que la definida por una clase abstracta. Este patrón se incluye en la categoría de patrón de comportamiento.

Implementación

Vamos a crear una clase abstracta de Juego que defina operaciones con un método de plantilla configurado como final para que no se pueda anular. El críquet y el fútbol son clases concretas que amplían el juego y anulan sus métodos.

TemplatePatternDemo , nuestra clase de demostración usará Game para demostrar el uso del patrón de plantilla.

Paso 1

Cree una clase abstracta con un método de plantilla que sea definitivo.

Game.java

public abstract class Game {
   abstract void initialize();
   abstract void startPlay();
   abstract void endPlay();

   //template method
   public final void play(){

      //initialize the game
      initialize();

      //start game
      startPlay();

      //end game
      endPlay();
   }
}

Paso 2

Cree clases concretas ampliando la clase anterior.

Cricket.java

public class Cricket extends Game {

   @Override
   void endPlay() {
      System.out.println("Cricket Game Finished!");
   }

   @Override
   void initialize() {
      System.out.println("Cricket Game Initialized! Start playing.");
   }

   @Override
   void startPlay() {
      System.out.println("Cricket Game Started. Enjoy the game!");
   }
}

Football.java

public class Football extends Game {
   @Override
   void endPlay() {
      System.out.println("Football Game Finished!");
   }

   @Override
   void initialize() {
      System.out.println("Football Game Initialized! Start playing.");
   }

   @Override
   void startPlay() {
      System.out.println("Football Game Started. Enjoy the game!");
   }
}

Paso 3

Utilice el juego de la plantilla método play 's () para demostrar de una manera definida de jugar el juego.

TemplatePatternDemo.java

public class TemplatePatternDemo {
   public static void main(String[] args) {

      Game game = new Cricket();
      game.play();
      System.out.println();
      game = new Football();
      game.play();		
   }
}

Etapa 4

Verifique la salida.

Cricket Game Initialized! Start playing.
Cricket Game Started. Enjoy the game!
Cricket Game Finished!

Football Game Initialized! Start playing.
Football Game Started. Enjoy the game!
Football Game Finished!

En el patrón de visitante, usamos una clase de visitante que cambia el algoritmo de ejecución de una clase de elemento. De esta manera, el algoritmo de ejecución del elemento puede variar a medida que varía el visitante. Este patrón se incluye en la categoría de patrón de comportamiento. Según el patrón, el objeto del elemento tiene que aceptar el objeto del visitante para que el objeto del visitante maneje la operación en el objeto del elemento.

Implementación

Vamos a crear una interfaz ComputerPart que defina aceptar la operación. Teclado , Ratón , Monitor y Computadora son clases concretas que implementan la interfaz ComputerPart . Definiremos otra interfaz ComputerPartVisitor que definirá las operaciones de una clase de visitante. La computadora utiliza al visitante concreto para realizar la acción correspondiente.

VisitorPatternDemo , nuestra clase de demostración utilizará las clases Computer , ComputerPartVisitor para demostrar el uso del patrón de visitante.

Paso 1

Defina una interfaz para representar el elemento.

ComputerPart.java

public interface class ComputerPart {
   public void accept(ComputerPartVisitor computerPartVisitor);
}

Paso 2

Cree clases concretas ampliando la clase anterior.

Keyboard.java

public class Keyboard  implements ComputerPart {

   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}

Monitor.java

public class Monitor  implements ComputerPart {

   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}

Mouse.java

public class Mouse  implements ComputerPart {

   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}

Computer.java

public class Computer implements ComputerPart {
	
   ComputerPart[] parts;

   public Computer(){
      parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};		
   } 


   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      for (int i = 0; i < parts.length; i++) {
         parts[i].accept(computerPartVisitor);
      }
      computerPartVisitor.visit(this);
   }
}

Paso 3

Defina una interfaz para representar al visitante.

ComputerPartVisitor.java

public interface ComputerPartVisitor {
	public void visit(Computer computer);
	public void visit(Mouse mouse);
	public void visit(Keyboard keyboard);
	public void visit(Monitor monitor);
}

Etapa 4

Cree un visitante concreto implementando la clase anterior.

ComputerPartDisplayVisitor.java

public class ComputerPartDisplayVisitor implements ComputerPartVisitor {

   @Override
   public void visit(Computer computer) {
      System.out.println("Displaying Computer.");
   }

   @Override
   public void visit(Mouse mouse) {
      System.out.println("Displaying Mouse.");
   }

   @Override
   public void visit(Keyboard keyboard) {
      System.out.println("Displaying Keyboard.");
   }

   @Override
   public void visit(Monitor monitor) {
      System.out.println("Displaying Monitor.");
   }
}

Paso 5

Utilice ComputerPartDisplayVisitor para mostrar partes de Computer .

VisitorPatternDemo.java

public class VisitorPatternDemo {
   public static void main(String[] args) {

      ComputerPart computer = new Computer();
      computer.accept(new ComputerPartDisplayVisitor());
   }
}

Paso 6

Verifique la salida.

Displaying Mouse.
Displaying Keyboard.
Displaying Monitor.
Displaying Computer.

MVC Pattern son las siglas de Model-View-Controller Pattern. Este patrón se usa para separar las preocupaciones de la aplicación.

  • Model- El modelo representa un objeto o JAVA POJO que lleva datos. También puede tener lógica para actualizar el controlador si sus datos cambian.

  • View - Vista representa la visualización de los datos que contiene el modelo.

  • Controller- El controlador actúa tanto en el modelo como en la vista. Controla el flujo de datos hacia el objeto del modelo y actualiza la vista cada vez que cambian los datos. Mantiene la vista y el modelo separados.

Implementación

Vamos a crear un objeto Student que actúa como modelo. StudentView será una clase de vista que puede imprimir los detalles del estudiante en la consola y StudentController es la clase controladora responsable de almacenar datos en el objeto Student y actualizar la vista StudentView en consecuencia.

MVCPatternDemo , nuestra clase de demostración utilizará StudentController para demostrar el uso del patrón MVC.

Paso 1

Crear modelo.

Student.java

public class Student {
   private String rollNo;
   private String name;
   public String getRollNo() {
      return rollNo;
   }
   public void setRollNo(String rollNo) {
      this.rollNo = rollNo;
   }
   public String getName() {
      return name;
   }
   public void setName(String name) {
      this.name = name;
   }
}

Paso 2

Crear vista.

StudentView.java

public class StudentView {
   public void printStudentDetails(String studentName, String studentRollNo){
      System.out.println("Student: ");
      System.out.println("Name: " + studentName);
      System.out.println("Roll No: " + studentRollNo);
   }
}

Paso 3

Crear controlador.

StudentController.java

public class StudentController {
   private Student model;
   private StudentView view;

   public StudentController(Student model, StudentView view){
      this.model = model;
      this.view = view;
   }

   public void setStudentName(String name){
      model.setName(name);		
   }

   public String getStudentName(){
      return model.getName();		
   }

   public void setStudentRollNo(String rollNo){
      model.setRollNo(rollNo);		
   }

   public String getStudentRollNo(){
      return model.getRollNo();		
   }

   public void updateView(){				
      view.printStudentDetails(model.getName(), model.getRollNo());
   }	
}

Etapa 4

Utilice los métodos StudentController para demostrar el uso del patrón de diseño MVC.

MVCPatternDemo.java

public class MVCPatternDemo {
   public static void main(String[] args) {

      //fetch student record based on his roll no from the database
      Student model  = retriveStudentFromDatabase();

      //Create a view : to write student details on console
      StudentView view = new StudentView();

      StudentController controller = new StudentController(model, view);

      controller.updateView();

      //update model data
      controller.setStudentName("John");

      controller.updateView();
   }

   private static Student retriveStudentFromDatabase(){
      Student student = new Student();
      student.setName("Robert");
      student.setRollNo("10");
      return student;
   }
}

Paso 5

Verifique la salida.

Student: 
Name: Robert
Roll No: 10
Student: 
Name: Julie
Roll No: 10

El patrón de delegado comercial se utiliza para desacoplar el nivel de presentación y el nivel comercial. Básicamente se utiliza para reducir la funcionalidad de comunicación o búsqueda remota al código de nivel empresarial en el código de nivel de presentación. En el nivel empresarial, hemos seguido a las entidades.

  • Client - El código del nivel de presentación puede ser JSP, servlet o código java UI.

  • Business Delegate - Una clase de punto de entrada único para que las entidades cliente proporcionen acceso a los métodos de servicios comerciales.

  • LookUp Service - El objeto de servicio de búsqueda es responsable de obtener la implementación comercial relativa y proporcionar acceso al objeto comercial al objeto delegado comercial.

  • Business Service- Interfaz de servicios comerciales. Las clases concretas implementan este servicio empresarial para proporcionar una lógica de implementación empresarial real.

Implementación

Vamos a crear un cliente , BusinessDelegate , BusinessService , LookUpService , JMSService y EJBService que representen varias entidades del patrón Business Delegate.

BusinessDelegatePatternDemo , nuestra clase de demostración utilizará BusinessDelegate y Client para demostrar el uso del patrón Business Delegate.

Paso 1

Cree la interfaz BusinessService.

BusinessService.java

public interface BusinessService {
   public void doProcessing();
}

Paso 2

Crear clases de servicios concretas.

EJBService.java

public class EJBService implements BusinessService {

   @Override
   public void doProcessing() {
      System.out.println("Processing task by invoking EJB Service");
   }
}

JMSService.java

public class JMSService implements BusinessService {

   @Override
   public void doProcessing() {
      System.out.println("Processing task by invoking JMS Service");
   }
}

Paso 3

Crear servicio de búsqueda empresarial.

BusinessLookUp.java

public class BusinessLookUp {
   public BusinessService getBusinessService(String serviceType){
      if(serviceType.equalsIgnoreCase("EJB")){
         return new EJBService();
      }else {
         return new JMSService();
      }
   }
}

Etapa 4

Crear delegado comercial.

BusinessLookUp.java

public class BusinessDelegate {
   private BusinessLookUp lookupService = new BusinessLookUp();
   private BusinessService businessService;
   private String serviceType;

   public void setServiceType(String serviceType){
      this.serviceType = serviceType;
   }

   public void doTask(){
      businessService = lookupService.getBusinessService(serviceType);
      businessService.doProcessing();		
   }
}

Paso 5

Crear cliente.

Student.java

public class Client {
	
   BusinessDelegate businessService;

   public Client(BusinessDelegate businessService){
      this.businessService  = businessService;
   }

   public void doTask(){		
      businessService.doTask();
   }
}

Paso 6

Utilice las clases BusinessDelegate y Client para demostrar el patrón Business Delegate.

BusinessDelegatePatternDemo.java

public class BusinessDelegatePatternDemo {
	
   public static void main(String[] args) {

      BusinessDelegate businessDelegate = new BusinessDelegate();
      businessDelegate.setServiceType("EJB");

      Client client = new Client(businessDelegate);
      client.doTask();

      businessDelegate.setServiceType("JMS");
      client.doTask();
   }
}

Paso 7

Verifique la salida.

Processing task by invoking EJB Service
Processing task by invoking JMS Service

El patrón de entidad compuesta se utiliza en el mecanismo de persistencia de EJB. Una entidad compuesta es un bean de entidad EJB que representa un gráfico de objetos. Cuando se actualiza una entidad compuesta, los beans de objetos dependientes internamente se actualizan automáticamente al ser administrados por el bean de entidad EJB. A continuación se muestran los participantes en Composite Entity Bean.

  • Composite Entity - Es un bean de entidad primario. Puede ser de grano grueso o puede contener un objeto de grano grueso para ser utilizado con fines de persistencia.

  • Coarse-Grained Object-Este objeto contiene objetos dependientes. Tiene su propio ciclo de vida y también gestiona el ciclo de vida de los objetos dependientes.

  • Dependent Object - Objetos dependientes es un objeto que depende del objeto Coarse-Grained para su ciclo de vida de persistencia.

  • Strategies - Estrategias representa cómo implementar una entidad compuesta.

Implementación

Vamos a crear un objeto CompositeEntity actuando como CompositeEntity. CoarseGrainedObject será una clase que contiene objetos dependientes. CompositeEntityPatternDemo , nuestra clase de demostración utilizará la clase Client para demostrar el uso del patrón de entidad compuesta.

Paso 1

Cree objetos dependientes.

DependentObject1.java

public class DependentObject1 {
	
   private String data;

   public void setData(String data){
      this.data = data; 
   } 

   public String getData(){
      return data;
   }
}

DependentObject2.java

public class DependentObject2 {
	
   private String data;

   public void setData(String data){
      this.data = data; 
   } 

   public String getData(){
      return data;
   }
}

Paso 2

Crear objeto de grano grueso.

CoarseGrainedObject.java

public class CoarseGrainedObject {
   DependentObject1 do1 = new DependentObject1();
   DependentObject2 do2 = new DependentObject2();

   public void setData(String data1, String data2){
      do1.setData(data1);
      do2.setData(data2);
   }

   public String[] getData(){
      return new String[] {do1.getData(),do2.getData()};
   }
}

Paso 3

Crear entidad compuesta.

CompositeEntity.java

public class CompositeEntity {
   private CoarseGrainedObject cgo = new CoarseGrainedObject();

   public void setData(String data1, String data2){
      cgo.setData(data1, data2);
   }

   public String[] getData(){
      return cgo.getData();
   }
}

Etapa 4

Cree una clase de cliente para usar la entidad compuesta.

Client.java

public class Client {
   private CompositeEntity compositeEntity = new CompositeEntity();

   public void printData(){
      for (int i = 0; i < compositeEntity.getData().length; i++) {
         System.out.println("Data: " + compositeEntity.getData()[i]);
      }
   }

   public void setData(String data1, String data2){
      compositeEntity.setData(data1, data2);
   }
}

Paso 5

Utilice el cliente para demostrar el uso del patrón de diseño de la entidad compuesta.

CompositeEntityPatternDemo.java

public class CompositeEntityPatternDemo {
   public static void main(String[] args) {
       Client client = new Client();
       client.setData("Test", "Data");
       client.printData();
       client.setData("Second Test", "Data1");
       client.printData();
   }
}

Paso 6

Verifique la salida.

Data: Test
Data: Data
Data: Second Test
Data: Data1

El patrón de objeto de acceso a datos o patrón DAO se utiliza para separar las operaciones o la API de acceso a datos de bajo nivel de los servicios comerciales de alto nivel. A continuación se muestran los participantes en el patrón de objetos de acceso a datos.

  • Data Access Object Interface - Esta interfaz define las operaciones estándar que se realizarán en un objeto (s) modelo.

  • Data Access Object concrete class-Esta clase implementa la interfaz anterior. Esta clase es responsable de obtener datos de una fuente de datos que puede ser una base de datos / xml o cualquier otro mecanismo de almacenamiento.

  • Model Object or Value Object - Este objeto es un POJO simple que contiene métodos get / set para almacenar datos recuperados usando la clase DAO.

Implementación

Vamos a crear un objeto Student que actúe como modelo u objeto de valor. StudentDao es una interfaz de objetos de acceso a datos. StudentDaoImpl es una clase concreta que implementa la interfaz de objetos de acceso a datos. DaoPatternDemo , nuestra clase de demostración utilizará StudentDao para demostrar el uso del patrón de objeto de acceso a datos.

Paso 1

Crear objeto de valor.

Student.java

public class Student {
   private String name;
   private int rollNo;

   Student(String name, int rollNo){
      this.name = name;
      this.rollNo = rollNo;
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public int getRollNo() {
      return rollNo;
   }

   public void setRollNo(int rollNo) {
      this.rollNo = rollNo;
   }
}

Paso 2

Crear interfaz de objeto de acceso a datos.

StudentDao.java

import java.util.List;

public interface StudentDao {
   public List<Student> getAllStudents();
   public Student getStudent(int rollNo);
   public void updateStudent(Student student);
   public void deleteStudent(Student student);
}

Paso 3

Cree una clase concreta implementando la interfaz anterior.

StudentDaoImpl.java

import java.util.ArrayList;
import java.util.List;

public class StudentDaoImpl implements StudentDao {
	
   //list is working as a database
   List<Student> students;

   public StudentDaoImpl(){
      students = new ArrayList<Student>();
      Student student1 = new Student("Robert",0);
      Student student2 = new Student("John",1);
      students.add(student1);
      students.add(student2);		
   }
   @Override
   public void deleteStudent(Student student) {
      students.remove(student.getRollNo());
      System.out.println("Student: Roll No " + student.getRollNo() 
         +", deleted from database");
   }

   //retrive list of students from the database
   @Override
   public List<Student> getAllStudents() {
      return students;
   }

   @Override
   public Student getStudent(int rollNo) {
      return students.get(rollNo);
   }

   @Override
   public void updateStudent(Student student) {
      students.get(student.getRollNo()).setName(student.getName());
      System.out.println("Student: Roll No " + student.getRollNo() 
         +", updated in the database");
   }
}

Etapa 4

Utilice StudentDao para demostrar el uso del patrón de objetos de acceso a datos.

CompositeEntityPatternDemo.java

public class DaoPatternDemo {
   public static void main(String[] args) {
      StudentDao studentDao = new StudentDaoImpl();

      //print all students
      for (Student student : studentDao.getAllStudents()) {
         System.out.println("Student: [RollNo : "
            +student.getRollNo()+", Name : "+student.getName()+" ]");
      }


      //update student
      Student student =studentDao.getAllStudents().get(0);
      student.setName("Michael");
      studentDao.updateStudent(student);

      //get the student
      studentDao.getStudent(0);
      System.out.println("Student: [RollNo : "
         +student.getRollNo()+", Name : "+student.getName()+" ]");		
   }
}

Paso 5

Verifique la salida.

Student: [RollNo : 0, Name : Robert ]
Student: [RollNo : 1, Name : John ]
Student: Roll No 0, updated in the database
Student: [RollNo : 0, Name : Michael ]

El patrón de diseño del controlador frontal se utiliza para proporcionar un mecanismo de manejo de solicitudes centralizado de modo que todas las solicitudes sean manejadas por un solo controlador. Este controlador puede realizar la autenticación / autorización / registro o seguimiento de la solicitud y luego pasar las solicitudes a los controladores correspondientes. A continuación se muestran las entidades de este tipo de patrón de diseño.

  • Front Controller - Manejador único para todo tipo de solicitud que llega a la aplicación (ya sea basada en web / basada en escritorio).

  • Dispatcher - El controlador frontal puede usar un objeto despachador que puede enviar la solicitud al controlador específico correspondiente.

  • View - Las vistas son el objeto para el que se realizan las solicitudes.

Implementación

Vamos a crear un FrontController , Dispatcher para que actúe como Front Controller y Dispatcher correspondientemente. Homeview y StudentView representan diversas vistas para los que las peticiones pueden venir a controlador frontal.

FrontControllerPatternDemo , nuestra clase de demostración utilizará FrontController para demostrar el patrón de diseño del controlador frontal.

Paso 1

Crear vistas.

HomeView.java

public class HomeView {
   public void show(){
      System.out.println("Displaying Home Page");
   }
}

StudentView.java

public class StudentView {
   public void show(){
      System.out.println("Displaying Student Page");
   }
}

Paso 2

Crear despachador.

Dispatcher.java

public class Dispatcher {
   private StudentView studentView;
   private HomeView homeView;
   public Dispatcher(){
      studentView = new StudentView();
      homeView = new HomeView();
   }

   public void dispatch(String request){
      if(request.equalsIgnoreCase("STUDENT")){
         studentView.show();
      }else{
         homeView.show();
      }	
   }
}

Paso 3

Crear FrontController

Context.java

public class FrontController {
	
   private Dispatcher dispatcher;

   public FrontController(){
      dispatcher = new Dispatcher();
   }

   private boolean isAuthenticUser(){
      System.out.println("User is authenticated successfully.");
      return true;
   }

   private void trackRequest(String request){
      System.out.println("Page requested: " + request);
   }

   public void dispatchRequest(String request){
      //log each request
      trackRequest(request);
      //authenticate the user
      if(isAuthenticUser()){
         dispatcher.dispatch(request);
      }	
   }
}

Etapa 4

Utilice FrontController para demostrar el patrón de diseño del controlador frontal.

FrontControllerPatternDemo.java

public class FrontControllerPatternDemo {
   public static void main(String[] args) {
      FrontController frontController = new FrontController();
      frontController.dispatchRequest("HOME");
      frontController.dispatchRequest("STUDENT");
   }
}

Paso 5

Verifique la salida.

Page requested: HOME
User is authenticated successfully.
Displaying Home Page
Page requested: STUDENT
User is authenticated successfully.
Displaying Student Page

El patrón de diseño del filtro de interceptación se utiliza cuando queremos hacer un preprocesamiento / posprocesamiento con la solicitud o respuesta de la aplicación. Los filtros se definen y aplican en la solicitud antes de pasar la solicitud a la aplicación de destino real. Los filtros pueden realizar la autenticación / autorización / registro o seguimiento de la solicitud y luego pasar las solicitudes a los controladores correspondientes. A continuación se muestran las entidades de este tipo de patrón de diseño.

  • Filter - Filtro que realizará determinada tarea antes o después de la ejecución de la solicitud por parte del controlador de solicitudes.

  • Filter Chain - Filter Chain lleva varios filtros y ayuda a ejecutarlos en un orden definido en el objetivo.

  • Target - El objeto de destino es el controlador de solicitudes

  • Filter Manager - Filter Manager gestiona los filtros y la cadena de filtros.

  • Client - El cliente es el objeto que envía la solicitud al objeto de destino.

Implementación

Vamos a crear un FilterChain , FilterManager , Target , Client como varios objetos que representan nuestras entidades. AuthenticationFilter y DebugFilter representan filtros concretos.

InterceptingFilterDemo , nuestra clase de demostración utilizará Client para demostrar el patrón de diseño de filtro de interceptación.

Paso 1

Crear interfaz de filtro.

Filter.java

public interface Filter {
   public void execute(String request);
}

Paso 2

Crea filtros de hormigón.

AuthenticationFilter.java

public class AuthenticationFilter implements Filter {
   public void execute(String request){
      System.out.println("Authenticating request: " + request);
   }
}

DebugFilter.java

public class DebugFilter implements Filter {
   public void execute(String request){
      System.out.println("request log: " + request);
   }
}

Paso 3

Crear objetivo

Target.java

public class Target {
   public void execute(String request){
      System.out.println("Executing request: " + request);
   }
}

Etapa 4

Crear cadena de filtros

FilterChain.java

import java.util.ArrayList;
import java.util.List;

public class FilterChain {
   private List<Filter> filters = new ArrayList<Filter>();
   private Target target;

   public void addFilter(Filter filter){
      filters.add(filter);
   }

   public void execute(String request){
      for (Filter filter : filters) {
         filter.execute(request);
      }
      target.execute(request);
   }

   public void setTarget(Target target){
      this.target = target;
   }
}

Paso 5

Crear administrador de filtros

FilterManager.java

public class FilterManager {
   FilterChain filterChain;

   public FilterManager(Target target){
      filterChain = new FilterChain();
      filterChain.setTarget(target);
   }
   public void setFilter(Filter filter){
      filterChain.addFilter(filter);
   }

   public void filterRequest(String request){
      filterChain.execute(request);
   }
}

Paso 6

Crear cliente

Client.java

public class Client {
   FilterManager filterManager;

   public void setFilterManager(FilterManager filterManager){
      this.filterManager = filterManager;
   }

   public void sendRequest(String request){
      filterManager.filterRequest(request);
   }
}

Paso 7

Utilice el cliente para demostrar el patrón de diseño de filtro de interceptación.

FrontControllerPatternDemo.java

public class InterceptingFilterDemo {
   public static void main(String[] args) {
      FilterManager filterManager = new FilterManager(new Target());
      filterManager.setFilter(new AuthenticationFilter());
      filterManager.setFilter(new DebugFilter());

      Client client = new Client();
      client.setFilterManager(filterManager);
      client.sendRequest("HOME");
   }
}

Paso 8

Verifique la salida.

Authenticating request: HOME
request log: HOME
Executing request: HOME

El patrón de diseño del localizador de servicios se usa cuando queremos ubicar varios servicios usando la búsqueda JNDI. Teniendo en cuenta el alto costo de buscar JNDI para un servicio, el patrón del localizador de servicios hace uso de la técnica de almacenamiento en caché. Por primera vez se requiere un servicio, Service Locator busca en JNDI y almacena en caché el objeto de servicio. Se realizan búsquedas adicionales o el mismo servicio a través de Service Locator en su caché, lo que mejora el rendimiento de la aplicación en gran medida. A continuación se muestran las entidades de este tipo de patrón de diseño.

  • Service- Servicio real que tramitará la solicitud. La referencia de dicho servicio debe consultarse en el servidor JNDI.

  • Context / Initial Context -JNDI Context, lleva la referencia al servicio utilizado para fines de búsqueda.

  • Service Locator - Service Locator es un único punto de contacto para obtener servicios mediante búsqueda JNDI, almacenando en caché los servicios.

  • Cache - Caché para almacenar referencias de servicios para reutilizarlos

  • Client - Cliente es el objeto que invoca los servicios a través de ServiceLocator.

Implementación

Vamos a crear un ServiceLocator , InitialContext , Cache , Service como varios objetos que representan nuestras entidades. Service1 y Service2 representan servicios concretos.

ServiceLocatorPatternDemo , nuestra clase de demostración actúa como cliente aquí y utilizará ServiceLocator para demostrar el patrón de diseño del localizador de servicios.

Paso 1

Crear interfaz de servicio.

Service.java

public interface Service {
   public String getName();
   public void execute();
}

Paso 2

Crea servicios concretos.

Service1.java

public class Service1 implements Service {
   public void execute(){
      System.out.println("Executing Service1");
   }

   @Override
   public String getName() {
      return "Service1";
   }
}

Service2.java

public class Service2 implements Service {
   public void execute(){
      System.out.println("Executing Service2");
   }

   @Override
   public String getName() {
      return "Service2";
   }
}

Paso 3

Crear InitialContext para la búsqueda JNDI

InitialContext.java

public class InitialContext {
   public Object lookup(String jndiName){
      if(jndiName.equalsIgnoreCase("SERVICE1")){
         System.out.println("Looking up and creating a new Service1 object");
         return new Service1();
      }else if (jndiName.equalsIgnoreCase("SERVICE2")){
         System.out.println("Looking up and creating a new Service2 object");
         return new Service2();
      }
      return null;		
   }
}

Etapa 4

Crear caché

Cache.java

import java.util.ArrayList;
import java.util.List;

public class Cache {

   private List<Service> services;

   public Cache(){
      services = new ArrayList<Service>();
   }

   public Service getService(String serviceName){
      for (Service service : services) {
         if(service.getName().equalsIgnoreCase(serviceName)){
            System.out.println("Returning cached  "+serviceName+" object");
            return service;
         }
      }
      return null;
   }

   public void addService(Service newService){
      boolean exists = false;
      for (Service service : services) {
         if(service.getName().equalsIgnoreCase(newService.getName())){
            exists = true;
         }
      }
      if(!exists){
         services.add(newService);
      }
   }
}

Paso 5

Crear localizador de servicios

ServiceLocator.java

public class ServiceLocator {
   private static Cache cache;

   static {
      cache = new Cache();		
   }

   public static Service getService(String jndiName){

      Service service = cache.getService(jndiName);

      if(service != null){
         return service;
      }

      InitialContext context = new InitialContext();
      Service service1 = (Service)context.lookup(jndiName);
      cache.addService(service1);
      return service1;
   }
}

Paso 6

Utilice ServiceLocator para demostrar el patrón de diseño del localizador de servicios.

ServiceLocatorPatternDemo.java

public class ServiceLocatorPatternDemo {
   public static void main(String[] args) {
      Service service = ServiceLocator.getService("Service1");
      service.execute();
      service = ServiceLocator.getService("Service2");
      service.execute();
      service = ServiceLocator.getService("Service1");
      service.execute();
      service = ServiceLocator.getService("Service2");
      service.execute();		
   }
}

Paso 7

Verifique la salida.

Looking up and creating a new Service1 object
Executing Service1
Looking up and creating a new Service2 object
Executing Service2
Returning cached  Service1 object
Executing Service1
Returning cached  Service2 object
Executing Service2

El patrón Transferir objeto se utiliza cuando queremos pasar datos con múltiples atributos en una sola toma de cliente a servidor. El objeto de transferencia también se conoce como objeto de valor. Transfer Object es una clase POJO simple que tiene métodos getter / setter y es serializable para que pueda transferirse a través de la red. No tiene ningún comportamiento. La clase de negocio del lado del servidor normalmente obtiene datos de la base de datos y llena el POJO y lo envía al cliente o lo pasa por valor. Para el cliente, el objeto de transferencia es de solo lectura. El cliente puede crear su propio objeto de transferencia y pasarlo al servidor para actualizar los valores en la base de datos de una sola vez. A continuación se muestran las entidades de este tipo de patrón de diseño.

  • Business Object - Servicio comercial que llena de datos el objeto de transferencia.

  • Transfer Object -Simple POJO, que tiene métodos para establecer / obtener atributos únicamente.

  • Client - El cliente solicita o envía el objeto de transferencia al objeto comercial.

Implementación

Vamos a crear un StudentBO como Business Object, Student como Transfer Object que representa nuestras entidades.

TransferObjectPatternDemo , nuestra clase de demostración actúa como un cliente aquí y utilizará StudentBO y Student para demostrar Transfer Object Design Pattern.

Paso 1

Crear objeto de transferencia.

StudentVO.java

public class StudentVO {
   private String name;
   private int rollNo;

   StudentVO(String name, int rollNo){
      this.name = name;
      this.rollNo = rollNo;
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public int getRollNo() {
      return rollNo;
   }

   public void setRollNo(int rollNo) {
      this.rollNo = rollNo;
   }
}

Paso 2

Crear objeto comercial.

StudentBO.java

import java.util.ArrayList;
import java.util.List;

public class StudentBO {
	
   //list is working as a database
   List<StudentVO> students;

   public StudentBO(){
      students = new ArrayList<StudentVO>();
      StudentVO student1 = new StudentVO("Robert",0);
      StudentVO student2 = new StudentVO("John",1);
      students.add(student1);
      students.add(student2);		
   }
   public void deleteStudent(StudentVO student) {
      students.remove(student.getRollNo());
      System.out.println("Student: Roll No " 
      + student.getRollNo() +", deleted from database");
   }

   //retrive list of students from the database
   public List<StudentVO> getAllStudents() {
      return students;
   }

   public StudentVO getStudent(int rollNo) {
      return students.get(rollNo);
   }

   public void updateStudent(StudentVO student) {
      students.get(student.getRollNo()).setName(student.getName());
      System.out.println("Student: Roll No " 
      + student.getRollNo() +", updated in the database");
   }
}

Paso 3

Utilice StudentBO para demostrar el patrón de diseño de objetos de transferencia.

TransferObjectPatternDemo.java

public class TransferObjectPatternDemo {
   public static void main(String[] args) {
      StudentBO studentBusinessObject = new StudentBO();

      //print all students
      for (StudentVO student : studentBusinessObject.getAllStudents()) {
         System.out.println("Student: [RollNo : "
         +student.getRollNo()+", Name : "+student.getName()+" ]");
      }

      //update student
      StudentVO student =studentBusinessObject.getAllStudents().get(0);
      student.setName("Michael");
      studentBusinessObject.updateStudent(student);

      //get the student
      studentBusinessObject.getStudent(0);
      System.out.println("Student: [RollNo : "
      +student.getRollNo()+", Name : "+student.getName()+" ]");
   }
}

Etapa 4

Verifique la salida.

Student: [RollNo : 0, Name : Robert ]
Student: [RollNo : 1, Name : John ]
Student: Roll No 0, updated in the database
Student: [RollNo : 0, Name : Michael ]