Guia rápido de Design Pattern

Os padrões de projeto representam as melhores práticas usadas por desenvolvedores experientes de software orientado a objetos. Os padrões de design são soluções para problemas gerais que os desenvolvedores de software enfrentaram durante o desenvolvimento de software. Essas soluções foram obtidas por tentativa e erro por vários desenvolvedores de software durante um período de tempo considerável.

O que é Gang of Four (GOF)?

Em 1994, quatro autores Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides publicaram um livro intitulado Design Patterns - Elements of Reusable Object-Oriented Software que deu início ao conceito de Design Pattern no desenvolvimento de Software.

Esses autores são conhecidos coletivamente como Gang of Four (GOF). De acordo com esses autores, os padrões de design são baseados principalmente nos seguintes princípios de design orientado a objetos.

  • Programa para uma interface, não uma implementação

  • Favorece a composição do objeto em vez da herança

Uso do Design Pattern

Os Design Patterns têm dois usos principais no desenvolvimento de software.

Plataforma comum para desenvolvedores

Os padrões de design fornecem uma terminologia padrão e são específicos para um cenário particular. Por exemplo, um padrão de design singleton significa o uso de um único objeto, de modo que todos os desenvolvedores familiarizados com o padrão de design único farão uso de um único objeto e podem dizer uns aos outros que o programa está seguindo um padrão singleton.

Melhores Práticas

Os padrões de design evoluíram ao longo de um longo período de tempo e fornecem as melhores soluções para certos problemas enfrentados durante o desenvolvimento de software. Aprender esses padrões ajuda desenvolvedores inexperientes a aprender design de software de maneira fácil e rápida.

Tipos de Design Pattern

De acordo com o livro de referência de padrão de design Design Patterns - Elements of Reusable Object-Oriented Software, existem 23 padrões de design. Esses padrões podem ser classificados em três categorias: padrões criacionais, estruturais e comportamentais. Também discutiremos outra categoria de padrões de projeto: padrões de projeto J2EE.

SN Padrão e Descrição
1 Creational Patterns
Esses padrões de projeto fornecem uma maneira de criar objetos enquanto oculta a lógica de criação, em vez de instanciar objetos diretamente usando o novo operador. Isso dá ao programa mais flexibilidade para decidir quais objetos precisam ser criados para um determinado caso de uso.
2 Structural Patterns
Esses padrões de projeto dizem respeito à composição de classes e objetos. O conceito de herança é utilizado para compor interfaces e definir formas de compor objetos para obter novas funcionalidades.
3 Behavioral Patterns
Esses padrões de projeto estão especificamente preocupados com a comunicação entre objetos.
4 J2EE Patterns
Esses padrões de design estão especificamente relacionados à camada de apresentação. Esses padrões são identificados pelo Sun Java Center.

O padrão de fábrica é um dos padrões de design mais usados ​​em Java. Esse tipo de padrão de design está incluído no padrão de criação, pois esse padrão fornece uma das melhores maneiras de criar um objeto.

No padrão Factory, criamos o objeto sem expor a lógica de criação ao cliente e nos referimos ao objeto recém-criado usando uma interface comum.

Implementação

Vamos criar uma interface Shape e classes concretas implementando a interface Shape . Uma classe de fábrica ShapeFactory é definida como uma próxima etapa.

FactoryPatternDemo , nossa classe de demonstração usará ShapeFactory para obter um objeto Shape . Ele passará informações ( CÍRCULO / RETANGLE / QUADRADO ) para a ShapeFactory para obter o tipo de objeto de que precisa.

Passo 1

Crie uma interface.

Shape.java

public interface Shape {
   void draw();
}

Passo 2

Crie classes concretas implementando a mesma interface.

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.");
   }
}

etapa 3

Crie uma Factory para gerar objetos de classe concreta com base nas informações fornecidas.

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;
   }
}

Passo 4

Use o Factory para obter o objeto da classe concreta passando uma informação como 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();
   }
}

Etapa 5

Verifique a saída.

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

Os padrões abstratos de fábrica funcionam em torno de uma super-fábrica que cria outras fábricas. Esta fábrica também é chamada de fábrica de fábricas. Esse tipo de padrão de design está incluído no padrão de criação, pois esse padrão fornece uma das melhores maneiras de criar um objeto.

No padrão Abstract Factory, uma interface é responsável por criar uma fábrica de objetos relacionados sem especificar explicitamente suas classes. Cada fábrica gerada pode fornecer os objetos de acordo com o padrão de fábrica.

Implementação

Vamos criar uma interface Shape e uma classe concreta para implementá-la. Criamos uma classe de fábrica abstrata AbstractFactory como a próxima etapa. A classe de fábrica ShapeFactory é definida, o que estende AbstractFactory. Um criador de fábrica / classe gerador FactoryProducer é criado.

AbstractFactoryPatternDemo, nossa classe de demonstração usa FactoryProducer para obter um objeto AbstractFactory. Ele irá passar informações (CIRCLE / RECTANGLE / SQUARE para Shape) para AbstractFactory para obter o tipo de objeto de que precisa.

Passo 1

Crie uma interface para Shapes.

Shape.java

public interface Shape {
   void draw();
}

Passo 2

Crie classes concretas implementando a mesma interface.

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.");
   }
}

etapa 3

Crie uma classe Abstract para obter fábricas para objetos de forma normal e arredondada.

AbstractFactory.java

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

Passo 4

Crie classes Factory estendendo AbstractFactory para gerar objetos de classes concretas com base nas informações fornecidas.

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;
   }
}

Etapa 5

Crie uma classe de gerador / produtor de fábrica para obter fábricas passando uma informação como Forma

FactoryProducer.java

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

Etapa 6

Use o FactoryProducer para obter AbstractFactory a fim de obter fábricas de classes concretas, passando uma informação como 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();
      
   }
}

Etapa 7

Verifique a saída.

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

O padrão Singleton é um dos padrões de design mais simples em Java. Esse tipo de padrão de design está incluído no padrão de criação, pois esse padrão fornece uma das melhores maneiras de criar um objeto.

Este padrão envolve uma única classe que é responsável por criar o próprio objeto, garantindo que apenas um único objeto seja criado. Esta classe fornece uma maneira de acessar seu único objeto que pode ser acessado diretamente sem a necessidade de instanciar o objeto da classe.

Implementação

Vamos criar uma classe SingleObject . A classe SingleObject tem seu construtor como privado e tem uma instância estática de si mesma.

A classe SingleObject fornece um método estático para levar sua instância estática para o mundo externo. SingletonPatternDemo , nossa classe demo usará a classe SingleObject para obter um objeto SingleObject .

Passo 1

Crie uma classe 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!");
   }
}

Passo 2

Obtenha o único objeto da classe 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();
   }
}

etapa 3

Verifique a saída.

Hello World!

O padrão Builder constrói um objeto complexo usando objetos simples e usando uma abordagem passo a passo. Esse tipo de padrão de design está incluído no padrão de criação, pois esse padrão fornece uma das melhores maneiras de criar um objeto.

Uma classe Builder constrói o objeto final passo a passo. Este construtor é independente de outros objetos.

Implementação

Nós consideramos um caso de negócio de restaurante de fast-food onde uma refeição típica poderia ser um hambúrguer e uma bebida gelada. Hambúrguer pode ser um Hambúrguer Veg ou Hambúrguer de Frango e será embalado em uma embalagem. A bebida gelada pode ser uma coca ou pepsi e será embalada em uma garrafa.

Nós vamos criar um item de interface representando alimentos como hambúrgueres e bebidas frias e classes concretas de aplicação do item de interface e uma embalagem de interface que representa a embalagem de alimentos e classes concretas de execução da Embalagem interface como hambúrguer seria embalada em invólucro e frio a bebida seria embalada em garrafa.

Em seguida, criamos uma classe Meal tendo ArrayList of Item e um MealBuilder para construir diferentes tipos de objeto Meal combinando Item . BuilderPatternDemo , nossa classe de demonstração usará MealBuilder para construir uma refeição .

Passo 1

Crie um item de interface representando o item alimentar e a embalagem.

Item.java

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

Packing.java

public interface Packing {
   public String pack();
}

Passo 2

Crie classes concreate implementando a interface 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";
   }
}

etapa 3

Crie classes abstratas implementando a interface do item, fornecendo funcionalidades padrão.

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();
}

Passo 4

Crie classes concretas estendendo as classes Burger e 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";
   }
}

Etapa 5

Crie uma classe Refeição com os objetos Item definidos acima.

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());
      }		
   }	
}

Etapa 6

Crie uma classe MealBuilder, a classe construtora real responsável por criar 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;
   }
}

Etapa 7

BuiderPatternDemo usa MealBuider para demonstrar o padrão do construtor.

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());
   }
}

Etapa 8

Verifique a saída.

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

O padrão de protótipo se refere à criação de objetos duplicados, mantendo o desempenho em mente. Esse tipo de padrão de design está incluído no padrão de criação, pois esse padrão fornece uma das melhores maneiras de criar um objeto.

Esse padrão envolve a implementação de uma interface de protótipo que informa para criar um clone do objeto atual. Este padrão é usado quando a criação de um objeto diretamente é cara. Por exemplo, um objeto deve ser criado após uma operação de banco de dados cara. Podemos armazenar o objeto em cache, retornar seu clone na próxima solicitação e atualizar o banco de dados como e quando necessário, reduzindo assim as chamadas de banco de dados.

Implementação

Vamos criar uma classe abstrata Shape e classes concretas estendendo a classe Shape . Uma classe ShapeCache é definida como uma próxima etapa que armazena objetos de forma em um Hashtable e retorna seu clone quando solicitado.

PrototypPatternDemo , nossa classe demo usará a classe ShapeCache para obter um objeto Shape .

Passo 1

Crie uma classe abstrata implementando a interface 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;
   }
}

Passo 2

Crie classes concretas estendendo a classe acima.

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.");
   }
}

etapa 3

Crie uma classe para obter classes concreate do banco de dados e armazená-los em um Hashtable .

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);
   }
}

Passo 4

PrototypePatternDemo usa a classe ShapeCache para obter clones de formas armazenadas em um 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());		
   }
}

Etapa 5

Verifique a saída.

Shape : Circle
Shape : Square
Shape : Rectangle

O padrão do adaptador funciona como uma ponte entre duas interfaces incompatíveis. Esse tipo de padrão de design vem sob o padrão estrutural, pois esse padrão combina a capacidade de duas interfaces independentes.

Este padrão envolve uma única classe que é responsável por unir funcionalidades de interfaces independentes ou incompatíveis. Um exemplo da vida real poderia ser um caso de leitor de cartão que atua como um adaptador entre o cartão de memória e um laptop. Você conecta o cartão de memória no leitor de cartão e o leitor de cartão no laptop para que o cartão de memória possa ser lido no laptop.

Estamos demonstrando o uso do padrão Adapter por meio do exemplo a seguir, no qual um dispositivo reprodutor de áudio pode reproduzir apenas arquivos mp3 e deseja usar um reprodutor de áudio avançado capaz de reproduzir arquivos vlc e mp4.

Implementação

Temos uma interface MediaPlayer interface e uma classe concreta AudioPlayer implementando a interface MediaPlayer . AudioPlayer pode reproduzir arquivos de áudio no formato mp3 por padrão.

Estamos tendo outra interface AdvancedMediaPlayer e classes concretas implementando a interface AdvancedMediaPlayer. Essas classes podem reproduzir arquivos de formato vlc e mp4.

Queremos fazer o AudioPlayer tocar outros formatos também. Para conseguir isso, criamos uma classe de adaptador MediaAdapter que implementa a interface MediaPlayer e usa objetos AdvancedMediaPlayer para reproduzir o formato necessário.

AudioPlayer usa a classe do adaptador MediaAdapter passando o tipo de áudio desejado sem saber a classe real que pode reproduzir o formato desejado. AdapterPatternDemo , nossa classe de demonstração usará a classe AudioPlayer para reproduzir vários formatos.

Passo 1

Crie interfaces para Media Player e 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);
}

Passo 2

Crie classes concretas implementando a interface 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);		
   }
}

etapa 3

Crie uma classe de adaptador implementando a interface 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);
      }
   }
}

Passo 4

Crie uma classe concreta implementando a interface 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");
      }
   }   
}

Etapa 5

Use o AudioPlayer para reproduzir diferentes tipos de formatos de áudio.

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");
   }
}

Etapa 6

Verifique a saída.

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 é usado onde precisamos separar uma abstração de sua implementação para que as duas possam variar independentemente. Esse tipo de padrão de design está incluído no padrão estrutural, pois esse padrão desacopla a classe de implementação e a classe abstrata, fornecendo uma estrutura de ponte entre elas.

Esse padrão envolve uma interface que atua como uma ponte que torna a funcionalidade de classes concretas independente das classes implementadoras de interface. Ambos os tipos de classes podem ser alterados estruturalmente sem afetar um ao outro.

Estamos demonstrando o uso do padrão Bridge por meio do exemplo a seguir, no qual um círculo pode ser desenhado em cores diferentes usando o mesmo método de classe abstrata, mas diferentes classes implementadoras de bridge.

Implementação

Temos uma interface DrawAPI que atua como um implementador de bridge e classes concretas RedCircle , GreenCircle implementando a interface DrawAPI . Shape é uma classe abstrata e usará o objeto de DrawAPI . BridgePatternDemo , nossa classe de demonstração usará a classe Shape para desenhar diferentes círculos coloridos.

Passo 1

Crie a interface do implementador de ponte.

DrawAPI.java

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

Passo 2

Crie classes de implementador de ponte concreta implementando a interface 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 +"]");
   }
}

etapa 3

Crie uma classe abstrata Shape usando a interface DrawAPI .

Shape.java

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

Passo 4

Crie uma classe concreta implementando a interface 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);
   }
}

Etapa 5

Use as classes Shape e DrawAPI para desenhar círculos de cores diferentes.

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();
   }
}

Etapa 6

Verifique a saída.

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

Padrão de filtro ou padrão de critérios é um padrão de design que permite aos desenvolvedores filtrar um conjunto de objetos, usando diferentes critérios, encadeando-os de forma desacoplada por meio de operações lógicas. Esse tipo de padrão de projeto está sob o padrão estrutural, pois esse padrão combina vários critérios para obter um único critério.

Implementação

Vamos criar um objeto Person , interface Criteria e classes concretas implementando esta interface para filtrar a lista de objetos Person . CriteriaPatternDemo , nossa classe demo usa objetos Criteria para filtrar objetos Lista de Pessoa com base em vários critérios e suas combinações.

Passo 1

Crie uma classe na qual os critérios sejam aplicados.

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;
   }	
}

Passo 2

Crie uma interface para Critérios.

Criteria.java

import java.util.List;

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

etapa 3

Crie classes concretas implementando a interface 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;
   }
}

Passo 4

Use critérios diferentes e suas combinações para filtrar as pessoas.

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()
            +" ]");
      }
   }      
}

Etapa 5

Verifique a saída.

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 ]

O padrão composto é usado quando precisamos tratar um grupo de objetos de maneira semelhante a um único objeto. O padrão composto compõe objetos em termos de uma estrutura de árvore para representar parte, bem como toda a hierarquia. Esse tipo de padrão de projeto está incluído no padrão estrutural, pois esse padrão cria uma estrutura em árvore de um grupo de objetos.

Este padrão cria uma classe contém um grupo de seus próprios objetos. Esta classe fornece maneiras de modificar seu grupo de mesmos objetos.

Estamos demonstrando o uso do padrão Composite por meio do exemplo a seguir, que mostra a hierarquia de funcionários de uma organização.

Implementação

Temos uma classe Employee que atua como classe de ator de padrão composto. CompositePatternDemo , nossa classe de demonstração usará a classe Employee para adicionar hierarquia de nível de departamento e imprimir todos os funcionários.

Passo 1

Crie a classe Employee com uma lista de objetos Employee .

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+" ]");
   }   
}

Passo 2

Use a classe Employee para criar e imprimir a hierarquia de funcionários.

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);
         }
      }		
   }
}

etapa 3

Verifique a saída.

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 ]

O padrão Decorator permite adicionar novas funcionalidades a um objeto existente sem alterar sua estrutura. Esse tipo de padrão de design vem sob o padrão estrutural, pois esse padrão atua como um invólucro para a classe existente.

Esse padrão cria uma classe decoradora que envolve a classe original e fornece funcionalidade adicional, mantendo a assinatura dos métodos de classe intacta.

Estamos demonstrando o uso do padrão Decorator por meio do exemplo a seguir, no qual decoraremos uma forma com algumas cores sem alterar a classe da forma.

Implementação

Vamos criar uma interface Shape e classes concretas implementando a interface Shape . Em seguida, criamos uma classe decoradora abstrata ShapeDecorator implementando a interface Shape e tendo o objeto Shape como sua variável de instância.

RedShapeDecorator é uma classe concreta que implementa ShapeDecorator .

DecoratorPatternDemo , nossa classe demo usará RedShapeDecorator para decorar objetos Shape .

Passo 1

Crie uma interface.

Shape.java

public interface Shape {
   void draw();
}

Passo 2

Crie classes concretas implementando a mesma interface.

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");
   }
}

etapa 3

Crie uma classe de decorador abstrata implementando a interface Shape .

ShapeDecorator.java

public abstract class ShapeDecorator implements Shape {
   protected Shape decoratedShape;

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

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

Passo 4

Crie uma classe de decorador concreta estendendo a classe 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");
   }
}

Etapa 5

Use o 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();
   }
}

Etapa 6

Verifique a saída.

Circle with normal border
Shape: Circle

Circle of red border
Shape: Circle
Border Color: Red

Rectangle of red border
Shape: Rectangle
Border Color: Red

O padrão de fachada oculta as complexidades do sistema e fornece uma interface para o cliente, por meio da qual o cliente pode acessar o sistema. Este tipo de padrão de projeto vem sob o padrão estrutural, pois este padrão adiciona uma interface ao sistema existente para ocultar suas complexidades.

Esse padrão envolve uma única classe que fornece métodos simplificados que são exigidos pelo cliente e delega chamadas para métodos de classes de sistema existentes.

Implementação

Vamos criar uma interface Shape e classes concretas implementando a interface Shape . Uma classe de fachada ShapeMaker é definida como uma próxima etapa.

A classe ShapeMaker usa as classes concretas para delegar chamadas de usuário a essas classes. FacadePatternDemo , nossa classe de demonstração usará a classe ShapeMaker para mostrar os resultados.

Passo 1

Crie uma interface.

Shape.java

public interface Shape {
   void draw();
}

Passo 2

Crie classes concretas implementando a mesma interface.

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()");
   }
}

etapa 3

Crie uma classe 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();
   }
}

Passo 4

Use a fachada para desenhar vários tipos de formas.

FacadePatternDemo.java

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

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

Etapa 5

Verifique a saída.

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

O padrão Flyweight é usado principalmente para reduzir o número de objetos criados, para diminuir o consumo de memória e aumentar o desempenho. Esse tipo de padrão de design vem sob o padrão estrutural, pois esse padrão fornece maneiras de diminuir a contagem de objetos, melhorando assim a estrutura dos objetos exigidos pelo aplicativo.

O padrão Flyweight tenta reutilizar objetos de tipo semelhantes já existentes, armazenando-os e cria um novo objeto quando nenhum objeto correspondente é encontrado. Demonstraremos esse padrão desenhando 20 círculos de locais diferentes, mas criaremos apenas 5 objetos. Apenas 5 cores estão disponíveis, portanto, a propriedade de cor é usada para verificar os objetos Círculo já existentes .

Implementação

Vamos criar uma interface Shape e uma classe concreta Circle implementando a interface Shape . Uma classe de fábrica ShapeFactory é definida como uma próxima etapa.

A ShapeFactory tem um HashMap de Circle tendo a chave como cor do objeto Circle . Sempre que chega um pedido para criar um círculo de cor particular para a ShapeFactory . A ShapeFactory verifica o objeto de círculo em seu HashMap , se o objeto de Círculo for encontrado, esse objeto é retornado, caso contrário, um novo objeto é criado, armazenado em hashmap para uso futuro e devolvido ao cliente.

FlyWeightPatternDemo , nossa classe de demonstração usará ShapeFactory para obter um objeto Shape . Ele passará informações ( vermelho / verde / azul / preto / branco ) para a ShapeFactory para obter o círculo da cor desejada de que precisa.

Passo 1

Crie uma interface.

Shape.java

public interface Shape {
   void draw();
}

Passo 2

Crie uma classe concreta implementando a mesma interface.

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);
   }
}

etapa 3

Crie uma Factory para gerar objetos de classe concreta com base nas informações fornecidas.

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;
   }
}

Passo 4

Use o Factory para obter o objeto da classe concreta passando uma informação como a cor.

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);
   }
}

Etapa 5

Verifique a saída.

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

No padrão Proxy, uma classe representa a funcionalidade de outra classe. Este tipo de padrão de projeto vem sob o padrão estrutural.

No padrão Proxy, criamos um objeto com objeto original para fazer a interface de sua funcionalidade com o mundo exterior.

Implementação

Vamos criar uma interface Image e classes concretas implementando a interface Image . ProxyImage é uma classe proxy para reduzir a pegada de memória do carregamento de objetos RealImage .

ProxyPatternDemo , nossa classe de demonstração usará ProxyImage para fazer com que um objeto Image seja carregado e exibido conforme necessário.

Passo 1

Crie uma interface.

Image.java

public interface Image {
   void display();
}

Passo 2

Crie classes concretas implementando a mesma interface.

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();
   }
}

etapa 3

Use o ProxyImage para obter o objeto da classe RealImage quando necessário.

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(); 	
   }
}

Passo 4

Verifique a saída.

Loading test_10mb.jpg
Displaying test_10mb.jpg

Displaying test_10mb.jpg

Como o nome sugere, o padrão de cadeia de responsabilidade cria uma cadeia de objetos receptores para uma solicitação. Esse padrão separa o emissor e o receptor de uma solicitação com base no tipo de solicitação. Esse padrão vem em padrões de comportamento.

Neste padrão, normalmente cada receptor contém referência a outro receptor. Se um objeto não pode lidar com a solicitação, ele passa o mesmo para o próximo receptor e assim por diante.

Implementação

Criamos uma classe abstrata AbstractLogger com um nível de registro. Em seguida, criamos três tipos de loggers que estendem o AbstractLogger . Cada registrador verifica o nível da mensagem até o seu nível e imprime de acordo, caso contrário, não imprime e passa a mensagem para o próximo registrador.

Passo 1

Crie uma classe abstrata de logger.

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);
	
}

Passo 2

Crie classes concretas estendendo o logger.

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);
   }
}

etapa 3

Crie diferentes tipos de loggers. Atribua a eles níveis de erro e defina o próximo registrador em cada registrador. O próximo logger em cada logger representa a parte da cadeia.

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.");
   }
}

Passo 4

Verifique a saída.

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.

O padrão de comando é um padrão de design orientado por dados e se enquadra na categoria de padrão comportamental. Uma solicitação é agrupada em um objeto como comando e passada ao objeto invocador. O objeto Invoker procura o objeto apropriado que pode manipular este comando e passar o comando para o objeto correspondente e esse objeto executa o comando.

Implementação

Criamos uma interface Order que atua como um comando. Criamos uma classe Stock que atua como uma solicitação. Temos classes de comando concretas BuyStock e SellStock implementando a interface Order que fará o processamento real do comando. É criada uma classe Broker que atua como um objeto invocador. Pode receber pedidos e fazer pedidos.

O objeto Broker usa o padrão de comando para identificar qual objeto executará qual comando com base no tipo de comando. CommandPatternDemo , nossa classe de demonstração usará a classe Broker para demonstrar o padrão de comando.

Passo 1

Crie uma interface de comando.

Order.java

public interface Order {
   void execute();
}

Passo 2

Crie uma classe de solicitação.

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");
   }
}

etapa 3

Crie classes concretas implementando a interface 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();
   }
}

Passo 4

Crie uma classe de invocador de comando.

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();
   }
}

Etapa 5

Use a classe Broker para receber e executar 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();
   }
}

Etapa 6

Verifique a saída.

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

O padrão de intérprete fornece uma maneira de avaliar a gramática ou expressão da linguagem. Esse tipo de padrão vem em padrões de comportamento. Esse padrão envolve a implementação de uma interface de expressão que informa para interpretar um contexto específico. Este padrão é usado na análise SQL, mecanismo de processamento de símbolo etc.

Implementação

Vamos criar uma interface Expression e classes concretas implementando a interface Expression . É definida uma classe TerminalExpression que atua como um interpretador principal do contexto em questão. Outras classes OrExpression , AndExpression são usadas para criar expressões combinacionais.

InterpreterPatternDemo , nossa classe de demonstração usará a classe Expression para criar regras e demonstrar a análise de expressões.

Passo 1

Crie uma interface de expressão.

Expression.java

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

Passo 2

Crie classes concretas implementando a interface acima.

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);
   }
}

etapa 3

InterpreterPatternDemo usa a classe Expression para criar regras e depois analisá-las.

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"));
   }
}

Passo 4

Verifique a saída.

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

O padrão de iterador é um padrão de design muito comumente usado no ambiente de programação Java e .Net. Esse padrão é usado para obter uma maneira de acessar os elementos de um objeto de coleção de maneira sequencial, sem a necessidade de saber sua representação subjacente.

O padrão do iterador se enquadra na categoria de padrão comportamental.

Implementação

Vamos criar uma interface Iterator que narra o método de navegação e uma interface Container que executa novamente o iterador. Classes concretas que implementam a interface Container serão responsáveis ​​por implementar a interface Iterator e usá-la

IteratorPatternDemo , nossa classe de demonstração usará NamesRepository , uma implementação de classe concreta para imprimir um Names armazenado como uma coleção em NamesRepository .

Passo 1

Crie interfaces.

Iterator.java

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

Container.java

public interface Container {
   public Iterator getIterator();
}

Passo 2

Crie uma classe concreta implementando a interface Container . Esta classe possui a classe interna NameIterator que implementa a interface Iterator .

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;
      }		
   }
}

etapa 3

Use o NameRepository para obter nomes de iteradores e de impressão.

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);
      } 	
   }
}

Passo 4

Verifique a saída.

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

O padrão de mediador é usado para reduzir a complexidade da comunicação entre vários objetos ou classes. Esse padrão fornece uma classe mediadora que normalmente lida com todas as comunicações entre classes diferentes e oferece suporte à facilidade de manutenção do código por meio de acoplamento fraco. O padrão do mediador se enquadra na categoria de padrão de comportamento.

Implementação

Estamos demonstrando o padrão do mediador por exemplo de uma sala de chat onde vários usuários podem enviar mensagens para a sala de chat e é responsabilidade da sala de chat mostrar as mensagens a todos os usuários. Criamos duas classes ChatRoom e User . Os objetos do usuário usarão o método ChatRoom para compartilhar suas mensagens.

MediatorPatternDemo , nossa classe demo usará objetos User para mostrar a comunicação entre eles.

Passo 1

Crie uma classe 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);
   }
}

Passo 2

Criar classe de usuário

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);
   }
}

etapa 3

Use o objeto Usuário para mostrar as comunicações entre eles.

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!");
   }
}

Passo 4

Verifique a saída.

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

O padrão memento é usado para reduzir onde queremos restaurar o estado de um objeto a um estado anterior. O padrão de memento se enquadra na categoria de padrão comportamental.

Implementação

O padrão Memento usa três classes de ator. Memento contém o estado de um objeto a ser restaurado. O Originador cria e armazena estados nos objetos Memento e no objeto Caretaker, que é responsável por restaurar o estado do objeto do Memento. Criamos as classes Memento , Originator e CareTaker .

MementoPatternDemo , nossa classe demo usará os objetos CareTaker e Originator para mostrar a restauração dos estados do objeto.

Passo 1

Crie a classe Memento.

Memento.java

public class Memento {
   private String state;

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

   public String getState(){
      return state;
   }	
}

Passo 2

Criar classe Originator

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();
   }
}

etapa 3

Criar classe 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);
   }
}

Passo 4

Use os objetos CareTaker e 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());
   }
}

Etapa 5

Verifique a saída.

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

O padrão do observador é usado quando há um relacionamento de muitos entre os objetos, como se um objeto for modificado, seus objetos dependentes devem ser notificados automaticamente. O padrão do observador se enquadra na categoria de padrão comportamental.

Implementação

O padrão Observer usa três classes de ator. Sujeito, observador e cliente. Assunto, um objeto que possui métodos para anexar e desanexar observadores a um objeto cliente. Criamos classes Assunto , classe abstrata Observer e classes concretas estendendo a classe abstrata Observer .

ObserverPatternDemo , nossa classe demo usará objetos de classe Subject e concretos para mostrar o padrão do observador em ação.

Passo 1

Crie uma classe de assunto.

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();
      }
   } 	
}

Passo 2

Crie a classe Observer.

Observer.java

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

etapa 3

Crie classes de observador 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() ); 
   }
}

Passo 4

Use o sujeito e 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);
   }
}

Etapa 5

Verifique a saída.

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

No padrão State, o comportamento de uma classe muda com base em seu estado. Este tipo de padrão de projeto está incluído no padrão de comportamento.

No padrão State, criamos objetos que representam vários estados e um objeto de contexto cujo comportamento varia conforme seu objeto de estado muda.

Implementação

Vamos criar uma interface State definindo uma ação e classes de estado concreto implementando a interface State . Contexto é uma classe que carrega um Estado.

StaePatternDemo , nossa classe demo usará Context e objetos de estado para demonstrar a mudança no comportamento do Context com base no tipo de estado em que se encontra.

Passo 1

Crie uma interface.

Image.java

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

Passo 2

Crie classes concretas implementando a mesma interface.

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";
   }
}

etapa 3

Crie uma classe 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;
   }
}

Passo 4

Use o contexto para ver a mudança no comportamento quando o estado muda.

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());
   }
}

Etapa 5

Verifique a saída.

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

No padrão Null Object, um objeto nulo substitui a verificação da instância do objeto NULL. Em vez de colocar if check para um valor nulo, Null Object reflete um relacionamento não fazer nada. Esse objeto Nulo também pode ser usado para fornecer comportamento padrão caso os dados não estejam disponíveis.

No padrão Objeto Nulo, criamos uma classe abstrata especificando as várias operações a serem feitas, concreamos classes que estendem essa classe e uma classe de objeto nulo, fornecendo nenhuma implementação desta classe e será usada sem problemas onde precisamos verificar o valor nulo.

Implementação

Vamos criar uma classe abstrata AbstractCustomer definindo operações, aqui o nome do cliente e classes concretas estendendo a classe AbstractCustomer . A classe de fábrica CustomerFactory é criado para retornar tanto RealCustomer ou NullCustomer objetos com base no nome do cliente passado para ele.

NullPatternDemo , nossa classe de demonstração usará CustomerFactory para demonstrar o uso do padrão Null Object.

Passo 1

Crie uma classe abstrata.

AbstractCustomer.java

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

Passo 2

Crie classes concretas estendendo a classe acima.

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;
   }
}

etapa 3

Crie a classe 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();
   }
}

Passo 4

Use o CustomerFactory obter qualquer RealCustomer ou NullCustomer objetos com base no nome do cliente passado para ele.

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());
   }
}

Etapa 5

Verifique a saída.

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

No padrão Strategy, o comportamento de uma classe ou seu algoritmo pode ser alterado em tempo de execução. Este tipo de padrão de projeto está incluído no padrão de comportamento.

No padrão Strategy, criamos objetos que representam várias estratégias e um objeto de contexto cujo comportamento varia de acordo com seu objeto de estratégia. O objeto de estratégia muda o algoritmo de execução do objeto de contexto.

Implementação

Vamos criar uma interface de estratégia definindo uma ação e classes de estratégia concretas implementando a interface de estratégia . Context é uma classe que usa uma estratégia.

StrategyPatternDemo , nossa classe de demonstração usará objetos de contexto e estratégia para demonstrar a mudança no comportamento do contexto com base na estratégia que ele implanta ou usa.

Passo 1

Crie uma interface.

Strategy.java

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

Passo 2

Crie classes concretas implementando a mesma interface.

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;
   }
}

etapa 3

Crie uma classe 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);
   }
}

Passo 4

Use o Contexto para ver a mudança no comportamento quando ele muda sua Estratégia .

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));
   }
}

Etapa 5

Verifique a saída.

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

No padrão Template, uma classe abstrata expõe formas / modelos definidos para executar seus métodos. Suas subclasses podem substituir as implementações de método conforme a necessidade, mas a invocação deve ser da mesma maneira definida por uma classe abstrata. Este padrão está incluído na categoria de padrão de comportamento.

Implementação

Vamos criar uma classe abstrata Game definindo operações com um método de template definido como final para que não possa ser sobrescrito. Cricket e Football são classes concretas que estendem Game e substituem seus métodos.

TemplatePatternDemo , nossa classe demo usará Game para demonstrar o uso do padrão de template.

Passo 1

Crie uma classe abstrata com um método de modelo sendo final.

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();
   }
}

Passo 2

Crie classes concretas estendendo a classe acima.

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!");
   }
}

etapa 3

Use o método play () do template do Game para demonstrar uma maneira definida de jogar.

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();		
   }
}

Passo 4

Verifique a saída.

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!

No padrão Visitor, usamos uma classe visitante que muda o algoritmo de execução de uma classe de elemento. Desta forma, o algoritmo de execução do elemento pode variar conforme o visitante varia. Este padrão está incluído na categoria de padrão de comportamento. De acordo com o padrão, o objeto do elemento deve aceitar o objeto do visitante para que o objeto do visitante controle a operação no objeto do elemento.

Implementação

Vamos criar uma interface ComputerPart definindo a operação de aceitação. Teclado , mouse , monitor e computador são classes concretas que implementam a interface ComputerPart . Definiremos outra interface ComputerPartVisitor que definirá as operações de uma classe de visitante. O computador usa o visitante concreto para fazer a ação correspondente.

VisitorPatternDemo , nossa classe de demonstração usará as classes Computer , ComputerPartVisitor para demonstrar o uso do padrão de visitante.

Passo 1

Defina uma interface para representar o elemento.

ComputerPart.java

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

Passo 2

Crie classes concretas estendendo a classe acima.

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);
   }
}

etapa 3

Defina uma interface para representar o 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);
}

Passo 4

Crie um visitante concreto implementando a classe acima.

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.");
   }
}

Etapa 5

Use o ComputerPartDisplayVisitor para exibir partes do computador .

VisitorPatternDemo.java

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

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

Etapa 6

Verifique a saída.

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

MVC Pattern significa Model-View-Controller Pattern. Esse padrão é usado para separar os interesses do aplicativo.

  • Model- O modelo representa um objeto ou JAVA POJO carregando dados. Ele também pode ter lógica para atualizar o controlador se seus dados mudarem.

  • View - View representa a visualização dos dados que o modelo contém.

  • Controller- O controlador atua no modelo e na visualização. Ele controla o fluxo de dados no objeto do modelo e atualiza a visualização sempre que os dados são alterados. Ele mantém a visualização e o modelo separados.

Implementação

Vamos criar um objeto Student atuando como um modelo. StudentView será uma classe de visualização que pode imprimir detalhes do aluno no console e StudentController é a classe controladora responsável por armazenar os dados no objeto Student e atualizar a visualização StudentView de acordo.

MVCPatternDemo , nossa classe de demonstração usará StudentController para demonstrar o uso do padrão MVC.

Passo 1

Criar 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;
   }
}

Passo 2

Criar visualização.

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);
   }
}

etapa 3

Criar 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());
   }	
}

Passo 4

Use os métodos StudentController para demonstrar o uso do padrão de design 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;
   }
}

Etapa 5

Verifique a saída.

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

O Business Delegate Pattern é usado para separar a camada de apresentação da camada de negócios. É basicamente usado para reduzir a comunicação ou a funcionalidade de pesquisa remota para o código da camada de negócios no código da camada de apresentação. Na camada de negócios, seguimos entidades.

  • Client - O código da camada de apresentação pode ser JSP, servlet ou código UI java.

  • Business Delegate - Uma classe de ponto de entrada único para entidades cliente para fornecer acesso aos métodos de serviço comercial.

  • LookUp Service - O objeto de serviço de pesquisa é responsável por obter a implementação de negócios relativa e fornecer acesso de objeto de negócios ao objeto de delegado de negócios.

  • Business Service- Interface de serviço comercial. Classes concretas implementam este serviço de negócios para fornecer lógica de implementação de negócios real.

Implementação

Vamos criar um Client , BusinessDelegate , BusinessService , LookUpService , JMSService e EJBService representando várias entidades do padrão Business Delegate.

BusinessDelegatePatternDemo , nossa classe de demonstração usará BusinessDelegate e Client para demonstrar o uso do padrão Business Delegate.

Passo 1

Crie uma interface BusinessService.

BusinessService.java

public interface BusinessService {
   public void doProcessing();
}

Passo 2

Crie classes de serviço Concreate.

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");
   }
}

etapa 3

Criar serviço de pesquisa de negócios.

BusinessLookUp.java

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

Passo 4

Criar delegado de negócios.

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();		
   }
}

Etapa 5

Crie o cliente.

Student.java

public class Client {
	
   BusinessDelegate businessService;

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

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

Etapa 6

Use as classes BusinessDelegate e Client para demonstrar o padrão 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();
   }
}

Etapa 7

Verifique a saída.

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

O padrão Composite Entity é usado no mecanismo de persistência EJB. Uma entidade Composite é um bean de entidade EJB que representa um gráfico de objetos. Quando uma entidade composta é atualizada, os beans de objetos internamente dependentes são atualizados automaticamente como sendo gerenciados pelo bean de entidade EJB. A seguir estão os participantes do Composite Entity Bean.

  • Composite Entity - É um bean de entidade primário. Ele pode ser de granulação grossa ou pode conter um objeto de granulação grossa a ser usado para fins de persistência.

  • Coarse-Grained Object-Este objeto contém objetos dependentes. Ele tem seu próprio ciclo de vida e também gerencia o ciclo de vida de objetos dependentes.

  • Dependent Object - Objetos dependentes é um objeto que depende do objeto de granulação grossa para seu ciclo de vida de persistência.

  • Strategies - Estratégias representa como implementar uma entidade composta.

Implementação

Vamos criar o objeto CompositeEntity atuando como CompositeEntity. CoarseGrainedObject será uma classe que contém objetos dependentes. CompositeEntityPatternDemo , nossa classe de demonstração usará a classe Client para demonstrar o uso do padrão Composite Entity.

Passo 1

Crie objetos dependentes.

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;
   }
}

Passo 2

Crie um objeto granulado grosso.

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()};
   }
}

etapa 3

Crie uma entidade composta.

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();
   }
}

Passo 4

Crie a classe Client para usar Composite Entity.

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);
   }
}

Etapa 5

Use o cliente para demonstrar o uso do padrão de design de entidade composta.

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();
   }
}

Etapa 6

Verifique a saída.

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

Data Access Object Pattern ou DAO pattern é usado para separar dados de baixo nível acessando API ou operações de serviços de negócios de alto nível. A seguir estão os participantes do Data Access Object Pattern.

  • Data Access Object Interface - Esta interface define as operações padrão a serem realizadas em um (s) objeto (s) de modelo.

  • Data Access Object concrete class-Esta classe implementa a interface acima. Esta classe é responsável por obter dados de uma fonte de dados que pode ser database / xml ou qualquer outro mecanismo de armazenamento.

  • Model Object or Value Object - Este objeto é um POJO simples contendo métodos get / set para armazenar dados recuperados usando a classe DAO.

Implementação

Vamos criar um objeto Student atuando como um objeto modelo ou valor. StudentDao é uma interface de objeto de acesso a dados. StudentDaoImpl é uma classe concreta que implementa a Interface de Objeto de Acesso a Dados. DaoPatternDemo , nossa classe de demonstração usará StudentDao para demonstrar o uso do padrão Data Access Object.

Passo 1

Crie um 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;
   }
}

Passo 2

Crie uma interface de objeto de acesso a dados.

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);
}

etapa 3

Crie uma classe concreate implementando a interface acima.

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");
   }
}

Passo 4

Use o StudentDao para demonstrar o uso do padrão Data Access Object.

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()+" ]");		
   }
}

Etapa 5

Verifique a saída.

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

O padrão de design do front controller é usado para fornecer um mecanismo centralizado de tratamento de solicitações, de forma que todas as solicitações sejam tratadas por um único tratador. Esse manipulador pode fazer a autenticação / autorização / registro ou rastreamento da solicitação e, em seguida, passar as solicitações aos manipuladores correspondentes. A seguir estão as entidades desse tipo de padrão de design.

  • Front Controller - Um único manipulador para todos os tipos de solicitação que chegam ao aplicativo (seja baseado na web / desktop).

  • Dispatcher - O Front Controller pode usar um objeto dispatcher que pode despachar a solicitação para o manipulador específico correspondente.

  • View - As visualizações são o objeto para o qual as solicitações são feitas.

Implementação

Vamos criar um FrontController , Dispatcher para atuar como Front Controller e Dispatcher correspondentemente. HomeView e StudentView representam várias visualizações para as quais as solicitações podem chegar ao controlador frontal.

FrontControllerPatternDemo , nossa classe demo usará o FrontController para demonstrar o Front Controller Design Pattern.

Passo 1

Crie visualizações.

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");
   }
}

Passo 2

Crie o Dispatcher.

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();
      }	
   }
}

etapa 3

Criar 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);
      }	
   }
}

Passo 4

Use o FrontController para demonstrar o Front Controller Design Pattern.

FrontControllerPatternDemo.java

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

Etapa 5

Verifique a saída.

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

O padrão de design de filtro de interceptação é usado quando queremos fazer algum pré-processamento / pós-processamento com solicitação ou resposta do aplicativo. Filtros são definidos e aplicados na solicitação antes de passar a solicitação para o aplicativo de destino real. Os filtros podem fazer a autenticação / autorização / registro ou rastreamento da solicitação e, em seguida, passar as solicitações aos manipuladores correspondentes. A seguir estão as entidades desse tipo de padrão de design.

  • Filter - Filtro que executará determinada tarefa antes ou depois da execução da solicitação pelo manipulador de solicitações.

  • Filter Chain - Cadeia de filtros carrega vários filtros e ajuda a executá-los em uma ordem definida no destino.

  • Target - O objeto de destino é o manipulador de solicitação

  • Filter Manager - Filter Manager gerencia os filtros e Filter Chain.

  • Client - Cliente é o objeto que envia a solicitação ao objeto Destino.

Implementação

Vamos criar um FilterChain , FilterManager , Target , Client como vários objetos que representam nossas entidades. AuthenticationFilter e DebugFilter representam filtros concretos.

InterceptingFilterDemo , nossa classe de demonstração usará Client para demonstrar o padrão de design de filtro de interceptação.

Passo 1

Criar interface de filtro.

Filter.java

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

Passo 2

Crie filtros concretos.

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);
   }
}

etapa 3

Criar alvo

Target.java

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

Passo 4

Criar Cadeia 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;
   }
}

Etapa 5

Criar Gerenciador 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);
   }
}

Etapa 6

Criar Cliente

Client.java

public class Client {
   FilterManager filterManager;

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

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

Etapa 7

Use o cliente para demonstrar o padrão de design de filtro de interceptação.

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");
   }
}

Etapa 8

Verifique a saída.

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

O padrão de design do localizador de serviço é usado quando queremos localizar vários serviços usando a pesquisa JNDI. Considerando o alto custo de pesquisa de JNDI para um serviço, o padrão Service Locator faz uso da técnica de armazenamento em cache. Pela primeira vez, um serviço é necessário, o Service Locator procura no JNDI e armazena em cache o objeto de serviço. Pesquisa adicional ou mesmo serviço através do Service Locator é feito em seu cache, o que melhora o desempenho do aplicativo em grande medida. A seguir estão as entidades desse tipo de padrão de design.

  • Service- Serviço real que processará a solicitação. A referência de tal serviço deve ser consultada no servidor JNDI.

  • Context / Initial Context -JNDI Context, carrega a referência ao serviço usado para fins de pesquisa.

  • Service Locator - Service Locator é um único ponto de contato para obter serviços por consulta JNDI, armazenando os serviços em cache.

  • Cache - Cache para armazenar referências de serviços para reutilizá-los

  • Client - Cliente é o objeto que invoca os serviços via ServiceLocator.

Implementação

Vamos criar um ServiceLocator , InitialContext , Cache , Service como vários objetos que representam nossas entidades. Service1 e Service2 representam serviços concretos.

ServiceLocatorPatternDemo , nossa classe de demonstração está atuando como um cliente aqui e usará o ServiceLocator para demonstrar o padrão de design do Service Locator.

Passo 1

Crie uma interface de serviço.

Service.java

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

Passo 2

Crie serviços 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";
   }
}

etapa 3

Criar InitialContext para pesquisa 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;		
   }
}

Passo 4

Criar Cache

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);
      }
   }
}

Etapa 5

Criar localizador de serviço

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;
   }
}

Etapa 6

Use o ServiceLocator para demonstrar o padrão de design do Service Locator.

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();		
   }
}

Etapa 7

Verifique a saída.

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

O padrão Transfer Object é usado quando queremos passar dados com vários atributos de uma vez do cliente para o servidor. O objeto de transferência também é conhecido como objeto de valor. Transfer Object é uma classe POJO simples com métodos getter / setter e é serializável para que possa ser transferido pela rede. Não tem nenhum comportamento. A classe de negócios do lado do servidor normalmente busca dados do banco de dados e preenche o POJO e os envia para o cliente ou os passa por valor. Para o cliente, o objeto de transferência é somente leitura. O cliente pode criar seu próprio objeto de transferência e passá-lo ao servidor para atualizar os valores no banco de dados de uma só vez. A seguir estão as entidades desse tipo de padrão de design.

  • Business Object - Business Service que preenche o Transfer Object com dados.

  • Transfer Object - POJO simples, tendo métodos para definir / obter apenas atributos.

  • Client - O cliente solicita ou envia o Transfer Object para o Business Object.

Implementação

Vamos criar um StudentBO como Business Object, Student como Transfer Object representando nossas entidades.

TransferObjectPatternDemo , nossa classe de demonstração está atuando como um cliente aqui e usará StudentBO e Student para demonstrar o Transfer Object Design Pattern.

Passo 1

Crie um objeto de transferência.

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;
   }
}

Passo 2

Crie um objeto de negócios.

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");
   }
}

etapa 3

Use o StudentBO para demonstrar o Transfer Object Design Pattern.

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()+" ]");
   }
}

Passo 4

Verifique a saída.

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