Monday, June 20, 2022

Design Pattern: Observer Pattern

Chapters

Observer Pattern

Observer pattern is a design pattern that consists of observers and a subject. Subject is an entity that is being observed by observers. Observer is an entity that observes the Subject. Most event handling systems implement observer pattern.

This example demonstrates observer pattern.
import java.util.ArrayList;

public class ClientCode{

  public static void main(String[] args){
    Subject playerOne = new Controller("Player One");
    Subject playerTwo = new Controller("Player Two");
    Subject.Observer input = new InputManager();
    
    playerOne.addObserver(input);
    playerTwo.addObserver(input);
    
    playerOne.moveUp();
    playerTwo.moveDown();
  }
}

abstract class Subject{
  private String name;
  private ArrayList<Observer> obsList = 
  new ArrayList<>();
  
  Subject(String name){
    this.name = name;
  }
  
  public void addObserver(Observer obs){
    obsList.add(obs);
  }
  
  public void removeObserver(Observer obs){
    obsList.remove(obs);
  }
  
  private void notifyObservers(String name, String event){
    obsList.
    stream().
    forEach(obs -> obs.update(name, event));
  }
  
  public static abstract class Observer{

    protected abstract void update(String name, String event);
  }
  
  public void moveUp(){
    notifyObservers(name, "up");
  }
  
  public void moveDown(){
    notifyObservers(name, "down");
  }
  
}

class Controller extends Subject{
  
  Controller(String name){
    super(name);
  }
}

class InputManager extends Subject.Observer{
  
  @Override
  protected void update(String name, String event){
    System.out.println
    (name + " pressed " + event + " button.");
    System.out.println
    ("InputManager updates the monitor screen and"+ 
    " moves its character " + event + ".");
  }
}

Result
Player One pressed up button.
InputManager updates the monitor screen and moves its character up.
Player Two pressed down button.
InputManager updates the monitor screen and moves its character down.
In the example above, observer and subject are still tightly coupled. The observer design above is alright but if you want to decouple the two further, you may wanna use publish-subscribe pattern where publishers are subjects and subscribers are observers.

Unlike in the example above, publish-subscribe pattern doesn't send a message directly to observers. It instead sends the message to an entity and that entity sends the message to observers. In java, Flow API implements publish-subscribe pattern. I discussed the API in this article.

If your observers are being notified in quick succession, you may want to implement a timer in order to improve the performance of your application. One example that this scenario may happen is redrawing a GUI window. Everytime we redraw the window on our screen, observers that observe the redrawing process will be notified.

Friday, June 17, 2022

Design Pattern: Memento Pattern

Chapters

Memento Pattern

Memento pattern is a design pattern that exposes the private internal state of an object. One example of how this can be used is to restore an object to its previous state (undo via rollback), another is versioning, another is custom serialization.

This pattern consists of three parts: Originator, Caretaker and Memento. Originator is an object with an internal state. Caretaker is an object that retrieves states from the originator and handles them. Memento is an object that contains a state of Originator. Take note that when implementing this pattern, the Originator must be the only one that can retrieve the state in a Memento.

This diagram shows how to implement a memento pattern Diagram
Courtesy of Wikipedia

This example demonstrates memento pattern.
import java.util.ArrayDeque;

public class ClientCode{

  public static void main(String[] args){
    Caretaker stateManager = 
    new Caretaker("A");
    
    System.out.println("State: " + 
    stateManager.getValue());
    stateManager.saveState();
    
    stateManager.append("B");
    stateManager.saveState();
    System.out.println("State: " + 
    stateManager.getValue());
    
    stateManager.append("C");
    stateManager.saveState();
    System.out.println("State: " + 
    stateManager.getValue());
    System.out.println();
    
    System.out.println("Undo...");
    stateManager.loadState();
    System.out.println("State: " + 
    stateManager.getValue());
    
    stateManager.loadState();
    System.out.println("State: " + 
    stateManager.getValue());
    
    stateManager.loadState();
    System.out.println("State: " + 
    stateManager.getValue());
  }
}

//Originator can have other members
//that is not part of the state of
//this class
class Originator{
  private StringBuilder state;
  
  Originator(String value){
    state = new StringBuilder(value);
  }
  
  void append(String value){
    state.append(value);
  }
  
  String getValue(){
    return state.toString();
  }
  
  Memento saveState(){
    return new Memento(
    new StringBuilder(state.toString()));
  }
  
  void loadState(Memento memento){
    state = memento.getSavedState();
  }
  
  public static class Memento{
  private final StringBuilder savedState;
    
    Memento(StringBuilder state){
      savedState = state;
    }
    
    //Making this private ensures that
    //only the Originater can retrieve
    //the state in a memento
    private StringBuilder getSavedState(){
      return savedState;
    }
  }
  
}

class Caretaker{
  private Originator originator;
  private ArrayDeque<Originator.Memento> states;
  
  public Caretaker(String value){
    originator = new Originator(value);
    states = new ArrayDeque<>();
  }
  
  public void append(String value){
    originator.append(value);
  }
  
  public String getValue(){
    return originator.getValue();
  }
  
  public void saveState(){
    states.addFirst(originator.saveState());
  }
  
  public void loadState(){
    if(states.isEmpty()){
      System.out.println("No states!");
      return;
    }
    originator.loadState(states.removeFirst());
  }
  
}

Result
State: A
State: AB
State: ABC

Undo...
State: ABC
State: AB
State: A

Thursday, June 16, 2022

Design Pattern: Mediator Pattern

Chapters

Mediator Pattern

Mediator Pattern is a design pattern that encapsulates interactions between objects. This pattern promotes loose coupling between objects and their interactions. Thus, making our code more flexible and maintainable.

This example demonstrates mediator pattern.
public class ClientCode{

  public static void main(String[] args){
    BookShelf bookShelf1 = 
    new RoomBookShelf(new String[]{"1", "2", "3"});
    BookShelf bookShelf2 = 
    new RoomBookShelf(new String[]{"A", "B", "C"});
    
    Mediator mediator = 
    new BookShelfInteractions(bookShelf1, bookShelf2);
    
    System.out.println("Shelf1: " + bookShelf1.getBook(0));
    System.out.println("Shelf2: " + bookShelf2.getBook(2));
    mediator.swapBooks("1", "C");
    System.out.println("After Swap...");
    System.out.println("Shelf1: " + bookShelf1.getBook(0));
    System.out.println("Shelf2: " + bookShelf2.getBook(2));
  }
}

/*
Assume classes below are in different package
*/

interface Mediator{
  void swapBooks(String bookInShelf, 
                 String bookInAnotherShelf);
}

class BookShelfInteractions implements Mediator{
  private BookShelf bookShelf1, bookShelf2;
  
  BookShelfInteractions(BookShelf bookShelf1,
                        BookShelf bookShelf2){
    this.bookShelf1 = bookShelf1;
    this.bookShelf2 = bookShelf2;
  }
  
  @Override
  public void swapBooks(String bookInShelf1, 
                        String bookInShelf2){
    boolean bookIsInShelf1 = false;
    boolean bookIsInShelf2 = false;
    
    String[] shelf1 = 
    bookShelf1.getBookShelf();
    String[] shelf2 = 
    bookShelf2.getBookShelf();
    
    int bookShelf1Index = 0;
    int bookShelf2Index = 0;
    for(int i = 0; i < shelf1.length; i++)
      if(shelf1[i].equals(bookInShelf1)){
        bookIsInShelf1 = true;
        bookShelf1Index = i;
        break;
      }
    if(!bookIsInShelf1){
      System.out.println
      ("Book " + bookInShelf1 + 
       "Doesn't exist!");
       return;
    }
    
    for(int i = 0; i < shelf2.length; i++)
      if(shelf2[i].equals(bookInShelf2)){
        bookIsInShelf2 = true;
        bookShelf2Index = i;
        break;
      }
    if(!bookIsInShelf2){
      System.out.println
      ("Book " + bookInShelf2 + 
       "Doesn't exist!");
       return;
    }
    
    String tempShelf = shelf2[bookShelf2Index];
    shelf2[bookShelf2Index] = 
    shelf1[bookShelf1Index];
    shelf1[bookShelf1Index] = tempShelf;
    System.out.println("Books have been swapped!");
  }
  
}

abstract class BookShelf{
  private String[] books;
  
  BookShelf(String[] books){
    this.books = books;
  }
  
  public String getBook(int index){
    if(index < 0 || index >= books.length)
      throw new ArrayIndexOutOfBoundsException();
    
    return books[index];
  }
  
  String[] getBookShelf(){
    return books;
  }
}

class RoomBookShelf extends BookShelf{
  
  RoomBookShelf(String[] books){
    super(books);
  }
  
}

Result
Shelf1: 1
Shelf2: C
Books have been swapped!
After Swap...
Shelf1: C
Shelf2: 1

Wednesday, June 15, 2022

Design Pattern: Iterator Pattern

Chapters

Iterator Pattern

Iterator pattern is a design pattern used to access and traverse a collection such as a list. This pattern decouples algorithms from containers.

Some programming languages have built-in iterator in them. Those built-in and general-purpose iterators are can solve most problems and I recommend using them. For example, java provides Iterator interface that is used to traverse collections such as ArrayList and others.

This diagram shows a structure of an iterator pattern Diagram
Courtesy of Wikipedia

This example demonstrates iterator pattern. Take note that this example is just a mere demonstration and not recommended to be reproduced in production.
import java.util.List;
import java.util.ArrayList;

public class ClientCode{

  public static void main(String[] args){
    Aggregate collection = 
    new ConcreteAggregate();
    
    collection.add("A");
    collection.add("B");
    collection.add("C");
    collection.add("D");
    collection.add("E");
    
    SampleIterator iterator =
    collection.createIterator();
    
    while(iterator.hasNext())
      System.out.println(iterator.next());
  }
}

/*
Assume classes below are in different package
and they're all public except for 
ConcreteIterator class
*/
interface Aggregate{

  void add(String element);
  SampleIterator createIterator();
}

class ConcreteAggregate implements Aggregate{
  private List<String> list;
  private SampleIterator iterator;
  
  ConcreteAggregate(){
    list = new ArrayList<>();
  }
  
  @Override
  public void add(String element){
    list.add(element);
  }
  
  @Override
  public SampleIterator createIterator(){
    return new ConcreteIterator(list);
  }
}

interface SampleIterator{

  String next();
  boolean hasNext();
}

class ConcreteIterator implements SampleIterator{
  private List<String> list;
  private int pointer;
  
  ConcreteIterator(List<String> list){
    this.list = list;
  }
  
  @Override
  public String next(){
    if(pointer >= list.size())
      throw new ArrayIndexOutOfBoundsException();
    String result = list.get(pointer);
    pointer++;
    return result;
  }
  
  @Override
  public boolean hasNext(){
    if(pointer >= list.size())
      return false;
    else
      return true;
  }
  
}

Result
A
B
C
D
E

Tuesday, June 14, 2022

Design Pattern: Command Pattern

Chapters

Command Pattern

Command pattern is a design pattern that wraps an object (receiver) to another object (command) with necessary information that can be processed by a handler (invoker). Command pattern consists of four entities: Client, Command, Receiver and Invoker.

Client is the one that uses our code. Could be a programmer or class. Command are classes that instantiate command objects. Command objects are objects that contain a receiver object and necessary information, sucn as function (instruction) to be called and variables, that is needed by an ivoker in order to perform requests that clients want.

Receiver are classes that instantiate receiver objects. Receiver objects are objects that are receiving commands. Invoker are classes that instantiate invoker objects. These objects contain commands that are executed by them.

This pattern promotes loose coupling between commands and handlers or executors. It means that commands and handlers don't need to be tighly coupled in order to function properly. Thus, increasing the flexibility of our code. Moreover, command pattern is often used in conjunction with chain-of-responsibility pattern.

This diagram shows how to implement a command pattern Diagram
Courtesy of Wikipedia

This example demonstrates command pattern.
//Client
public class ClientCode{
  
  public static void main(String[] args){
    
    //Receiver instance
    Controller computerController = 
    new ComputerController();
    Controller consoleController = 
    new ConsoleController();
   
    //command instance
    Command moveUp = 
    new MoveCommand(computerController,
                    Controller.DirectMove.UP);
    Command moveTopLeft = 
    new MoveDiagonalCommand(
    computerController,
    Controller.DiagonalMove.TOP_LEFT);
    
    //Invoker instance
    MoveInput controllerInput = 
    new MoveInput(moveUp, moveTopLeft);  
    controllerInput.move();
    controllerInput.moveDiagonally();
    System.out.println();  
    
    //Command instance
    Command moveDown = 
    new MoveCommand(consoleController,
                    Controller.DirectMove.DOWN);
    Command moveBotRight = 
    new MoveDiagonalCommand(
    consoleController,
    Controller.DiagonalMove.BOTTOM_RIGHT);
    
    MoveInput consoleInput = 
    new MoveInput(moveDown, moveBotRight);
    consoleInput.move();
    consoleInput.moveDiagonally();
  
  }
}

//Receiver interface
interface Controller{
  public enum DirectMove{
    UP, RIGHT, DOWN, LEFT
  }
  
  public enum DiagonalMove{
    TOP_LEFT, TOP_RIGHT, 
    BOTTOM_LEFT, BOTTOM_RIGHT
  }
  
  void move(DirectMove direction);
  void moveDiagonally(DiagonalMove direction);
}
  
//Receiver
class ConsoleController implements Controller{
  
  @Override
  public void move(DirectMove direction){
    System.out.println
    ("Console controller moves " + direction);
  }
  
  @Override
  public void moveDiagonally(DiagonalMove direction){
    System.out.println
    ("Console controller diagonally moves " + direction);
  }
}
  
//Receiver
class ComputerController implements Controller{
  
  @Override
  public void move(DirectMove direction){
    System.out.println
    ("Computer controller moves " + direction);
  }
  
  @Override
  public void moveDiagonally(DiagonalMove direction){
    System.out.println
    ("Console controller diagonally moves " + direction);
  }
}

//command interface
interface Command{
  
  void execute();
}
  
//command
class MoveCommand implements Command{
  
  private Controller controller;
  private Controller.DirectMove movement;
  
  public MoveCommand(Controller controller,
                     Controller.DirectMove movement){
    this.controller = controller;
    this.movement = movement;
  }
  
  @Override
  public void execute(){
    controller.move(movement);
  }
}
  
//command
class MoveDiagonalCommand implements Command{
  
  private Controller controller;
  private Controller.DiagonalMove movement;
  
  public MoveDiagonalCommand(Controller controller,
                             Controller.DiagonalMove movement){
    this.controller = controller;
    this.movement = movement;
  }
  
  @Override
  public void execute(){
    controller.moveDiagonally(movement);
  }
}
  
//invoker
class MoveInput{
  private Command directMovement;
  private Command diagonalMovement;
  
  public MoveInput(Command directMovement,
                   Command diagonalMovement){
    this.directMovement = directMovement;
    this.diagonalMovement = diagonalMovement;
  }
  
  public void move(){
    directMovement.execute();
  }
  
  public void moveDiagonally(){
    diagonalMovement.execute();
  }
}
  
Result
Computer controller move UP
Computer controller diagonally moves TOP_LEFT
  
Console controller moves DOWN
Console controller diagonally moves BOTTOM_RIGHT

Sunday, June 12, 2022

Design Pattern: Chain-of-responsibility pattern

Chapters

Chain-of-responsibility pattern

Chain-of-responsibility pattern is a behavioral design pattern that consists of command objects and processing objects. Command objects are objects that are being processed by processing objects.

Typically, every class in the chain has different responsibilities from one another. However, many implementations(such as UI event handling, servlet filters in Java and the example below) breaks this concept and allow several classes in the chain to take the same responsibility. This pattern promotes loose coupling as its processing objects are not closely tied up to command objects.

This example demonstrates chain-of-responsibility pattern.
import java.util.List;
import java.util.Arrays;

public class ClientCode{
  
  public static void main(String[] args){
  
    Handler handler = 
    new Adult(Arrays.asList(Handler.Fruits.all()), "Timothy").
    addHandler(
     new YoungAdult(
     Arrays.asList(Handler.Fruits.APPLE, Handler.Fruits.GUAVA),
                   "Samantha")).
    addHandler(
     new Child(
     Arrays.asList(Handler.Fruits.APPLE, Handler.Fruits.ORANGE),
                   "Louis"));
                   
     handler.offer(Handler.Fruits.APPLE);
     System.out.println();
     handler.offer(Handler.Fruits.GUAVA);
     System.out.println();
     handler.offer(Handler.Fruits.ORANGE);
     System.out.println();
     handler.offer(Handler.Fruits.MELON);
  }
}

//functional interface
interface Handler{
  public enum Fruits{
    AVOCADO, ORANGE, APPLE, GUAVA, MELON;
    
    public static Fruits[] all(){
      return values();
    }
  }
  
  //No need to add Handler reference after
  //Fruits reference. This method is in the
  //scope of Handler already
  //
  //classes that implement this method also
  //don't need to add Handler reference after
  //Fruits reference
  void offer(Fruits fruit);
  
  default Handler addHandler(Handler nextHandler){
    return (fruit) -> {
      offer(fruit);
      nextHandler.offer(fruit);
    };
  }
  
}

abstract class Patron{
  protected List<Handler.Fruits> preferredFruit;
  protected String name;
  
  Patron(List<Handler.Fruits> preferredFruit, 
         String name){
    this.preferredFruit = preferredFruit;
    this.name = name;
  }
  
  protected boolean checkPreferredFruit(Handler.Fruits fruit){
    boolean result = false;
    
    for(Handler.Fruits f : preferredFruit)
      if(f == fruit)
        result = true;
    return result;
  }
  
}

class Child extends Patron implements Handler{
  
  Child(List<Handler.Fruits> preferredFruit, 
         String name){
    super(preferredFruit, name);
  }
  
  @Override
  public void offer(Fruits fruit){
    if(!checkPreferredFruit(fruit))
      return;
    
    System.out.println
    (name + ", a child, took " + fruit);
  }
}

class YoungAdult extends Patron implements Handler{
  
  YoungAdult(List<Handler.Fruits> preferredFruit, 
         String name){
    super(preferredFruit, name);
  }
  
  @Override
  public void offer(Fruits fruit){
    if(!checkPreferredFruit(fruit))
      return;
  
    System.out.println
    (name + ", a young adult, took " + fruit);
  }
}

class Adult extends Patron implements Handler{
  
  Adult(List<Handler.Fruits> preferredFruit, 
         String name){
    super(preferredFruit, name);
  }
  
  @Override
  public void offer(Fruits fruit){
    if(!checkPreferredFruit(fruit))
      return;
    
    System.out.println
    (name + ", an adult, took " + fruit);
  }
}

Result
Timothy, an adult, took APPLE
Samantha, a young adult, took APPLE
Louis, a child, took APPLE

Timothy, an adult, took GUAVA
Samantha, a young adult, took GUAVA

Timothy, an adult, took ORANGE
Louis, a child, took ORANGE

Timothy, an adult, took MELON
In the example above, fruits in the Fruits enum are command objects whereas Adult, Child and YoungAdult instances are processing objects.

Friday, June 10, 2022

Design Pattern: Proxy Pattern

Chapters

Proxy Pattern

proxy pattern is a software design pattern. A proxy, in its most general form, is a class functioning as an interface to something else.

The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate. In short, a proxy is a wrapper or agent object that is being called by the client to access the real serving object behind the scenes.

Use of the proxy can simply be forwarding to the real object, or can provide additional logic. In the proxy, extra functionality can be provided, for example caching when operations on the real object are resource intensive, or checking preconditions before operations on the real object are invoked. For the client, usage of a proxy object is similar to using the real object, because both implement the same interface.

This diagram shows how to implement a proxy pattern Diagram
Courtesy of Wikipedia

This example demonstrates proxy pattern.
public class ClientCode{

  public static void main(String[] args){
    StringConcatInterface sci = 
    new StringConcatProxy(new StringConcat("My "));
    
    sci.concat("String!");
    System.out.println(sci.getText());
    sci.concat("String!String!");
    System.out.println(sci.getText());
    sci.concat("String!String!String!");
    System.out.println(sci.getText());
  }
}

interface StringConcatInterface{

  void concat(String str);
  String getText();
}

class StringConcat implements StringConcatInterface{
  private StringBuilder builder;
  
  StringConcat(String text){
    builder = new StringBuilder(text);
  }
  
  @Override
  public void concat(String str){
    builder.append(str);
  }
  
  @Override
  public String getText(){
    return builder.toString();
  }
}

class StringConcatProxy implements StringConcatInterface{
  private StringConcat sc;
  
  StringConcatProxy(StringConcat sc){
    this.sc = sc;
  }
  
  @Override
  public void concat(String str){
    if(sc.getText().length() > 10){
      System.out.println
      ("Max characters has been reached!");
    }
    else
      sc.concat(str);
  }
  
  @Override
  public String getText(){
    return sc.getText();
  }
  
}

Result
My String!
My String!String!String!
Max characters has been reached!
My String!String!String!
You might have noticed that proxy pattern is similar to decorator pattern. Their structure is similar but their purpose are not. We use decorator pattern if we want to add functionalities to a class while not affecting other related classes. We use proxy pattern if we want some kind of mirror that mirrors our class.

Design Pattern: Flyweight Pattern

Chapters

Flyweight Pattern

Flyweight pattern refers to an object that minimizes memory usage by sharing some of its data with other similar objects. The flyweight pattern is useful when dealing with large numbers of objects with simple repeated elements that would use a large amount of memory if individually stored.

This example demonstrates flyweight pattern.
import java.util.Queue;
import java.util.ArrayDeque;

public class ClientCode{

  public static void main(String[] args){
    int x = 0;
    int y = 0;
    
    TileFlyWeightFactory tileFactory =
    TileFlyWeightFactory.getInstance();
    
    //4x4 grid
    for(int i = 0; i < 4; i++){
      for(int j = 0; j < 4; j++){
        tileFactory.
        useThenAdd().
        draw(x, y);
        x += 64;
      }
      x = 0;
      y += 64;
      System.out.println();
    }
    
  }
}

interface TileInterface{

  void draw(int posx, int posy);
}

class Tile implements TileInterface{
  //intrinsic state
  private final String tileSource;
  
  Tile(String tileSource){
    this.tileSource = tileSource;
  }
  
  @Override
  public void draw(int posx, int posy){
    System.out.println
    (tileSource + " is drawn at " +
     posx + ", " + posy);
  }
  
}

//singleton
class TileFlyWeightFactory{
  private static TileFlyWeightFactory instance;
  private Queue<TileInterface> cache;
  
  private TileFlyWeightFactory(){
    cache = new ArrayDeque<>();
    cache.add(new Tile("Tile1.jpg"));
    cache.add(new Tile("Tile2.jpg"));
    cache.add(new Tile("Tile3.jpg"));
    cache.add(new Tile("Tile4.jpg"));
  }
  
  public static TileFlyWeightFactory getInstance(){
    if(instance == null)
      instance = new TileFlyWeightFactory();
    return instance;
  }
  
  public TileInterface useThenAdd(){
    TileInterface target = null;
    
    if(!cache.isEmpty()){
      target = cache.poll();
      cache.add(target);
    }
    return target;
  }
  
}

Result
Tile1.jpg is drawn at 0, 0
Tile2.jpg is drawn at 64, 0
Tile3.jpg is drawn at 128, 0
Tile4.jpg is drawn at 192, 0

Tile1.jpg is drawn at 0, 64
Tile2.jpg is drawn at 64, 64
Tile3.jpg is drawn at 128, 64
Tile4.jpg is drawn at 192, 64

Tile1.jpg is drawn at 0, 128
Tile2.jpg is drawn at 64, 128
Tile3.jpg is drawn at 128, 128
Tile4.jpg is drawn at 192, 128

Tile1.jpg is drawn at 0, 192
Tile2.jpg is drawn at 64, 192
Tile3.jpg is drawn at 128, 192
Tile4.jpg is drawn at 192, 192
First off, we store two states to our flyweight class (Tile class in this case). One of them is the intrinsic state. This state is constant. The next one is the extrinsic state. This state is not constant and likely to change overtime. In the example above, tileSource variable is the intristic state; posx and posy are extrinsic states.

In the example above, I reuse instantiated Tile objects in order to reuse their tileSource constants (intrinsic) and draw them anywhere on the screen using posx and posy variables (extrinsic). This technique saves a lot of system resources because I don't need to create a new instance of Tile class in order to reuse a tile image that's been already used.

Other information such as caching, retrieval, concurrency can be found in this wiki.

Thursday, June 9, 2022

Design Pattern: Facade Pattern

Chapters

Facade Pattern

facade pattern (also spelled façade) is a software-design pattern commonly used in object-oriented programming. Analogous to a facade in architecture, a facade is an object that serves as a front-facing interface masking more complex underlying or structural code in order to simplify the complexity of the system behind the facade.

To implement this pattern, we need a class that implements an interface and the implementations of the methods of the interface are delegated to the system behind the facade. Although, the interface may perform additional functionality before/after forwarding a request. Take a look at this example.

/*
Client
*/

public class ClientCode{

  public static void main(String[] args){
    RoomInterface ri = 
    new RoomController(new RoomControl());
    
    ri.openThenMark();
    System.out.println();
    ri.unmarkThenClose();
  }
}

/*
Facade
*/

interface RoomInterface{

  void openMarkThenClose();
  void openUnmarkThenClose();
  void openThenMark();
  void openThenUnmark();
  void markThenClose();
  void unmarkThenClose();
}

class RoomController implements RoomInterface{
  RoomControlInterface rci;
  
  RoomController(RoomControlInterface rci){
    this.rci = rci;
  }
  
  @Override
  public void openMarkThenClose(){
    rci.openRoom();
    rci.markRoom();
    rci.closeRoom();
  }
  
  @Override
  public void openUnmarkThenClose(){
    rci.openRoom();
    rci.unmarkRoom();
    rci.closeRoom();
  }
  
  @Override
  public void openThenMark(){
    rci.openRoom();
    rci.markRoom();
  }
  
  @Override
  public void openThenUnmark(){
    rci.openRoom();
    rci.unmarkRoom();
  }
  
  @Override
  public void markThenClose(){
    if(!rci.openRoom()){
      System.out.println("Room is not open.");
      System.out.println
      ("Therefore, it can't be closed.");
      return;
    }
    rci.markRoom();
    rci.closeRoom();
  }
  
  @Override
  public void unmarkThenClose(){
    if(!rci.openRoom()){
      System.out.println("Room is not open.");
      System.out.println
      ("Therefore, it can't be closed.");
      return;
    }
    rci.unmarkRoom();
    rci.closeRoom();
  }
  
}

/*
Complex System
*/

interface RoomControlInterface{
  
  boolean openRoom();
  void markRoom();
  void unmarkRoom();
  void closeRoom();
}

class RoomControl implements RoomControlInterface{
  private boolean isMarked = false;
  private boolean isOpen = false;
  
  @Override
  public boolean openRoom(){

    if(isOpen){
      System.out.println
      ("Room is already open!");
    }
    else{
      System.out.println
      ("Room has been opened!");
      isOpen = true;
    }
    return isOpen;
  }
  
  @Override
  public void markRoom(){
     if(isMarked){
      System.out.println
      ("Room is already marked. "+
       "no need to mark again.");
    }
    else{
      isMarked = true;
      System.out.println
      ("Room has been marked!");
    }
  }
  
  @Override
  public void unmarkRoom(){
    if(!isMarked){
      System.out.println
      ("Room is already unmarked. "+
       "no need to unmark again.");
    }
    else{
      isMarked = false;
      System.out.println
      ("Room has been unmarked!");
    }
  }
  
  @Override
  public void closeRoom(){
    
    if(isOpen){
      System.out.println
      ("Room has been closed!");
      isOpen = false;
    }
  }
}

Result
Room has been opened!
Room has been marked!

Room is already open!
Room has been unmarked!
Room has been closed!

Design Pattern: Decorator Pattern

Chapters

Decorator Pattern

Decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class.

The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.

Decorator use can be more efficient than subclassing, because an object's behavior can be augmented without defining an entirely new object. This example demonstrates decorator pattern.
public class ClientCode{

  public static void main(String[] args){
    Package standard = 
    new StandardPackage("My Package");
    Package business = 
    new BusinessPackage("My Company's Package");
    
    standard.pack();
    business.pack();
    System.out.println();
    
    //with decorator
    new PackageGiftWrap(business).pack();
    System.out.println();
    new PackagePlasticWrap(standard).pack();
  }
}

interface Package{

  void pack();
}

abstract class AbstractPackage implements Package{
  protected String packageName;
  
  AbstractPackage(String packageName){
    this.packageName = packageName;
  }
  
  public String getPackageName(){
    return packageName;
  }
  
}

class StandardPackage extends AbstractPackage{
  
  StandardPackage(String packageName){
    super(packageName);
  }
  
  @Override
  public void pack(){
    System.out.println
    (packageName + " with standard packaging " +
    "has been packed.");
  }
}

class BusinessPackage extends AbstractPackage{
  
  BusinessPackage(String packageName){
    super(packageName);
  }
  
  @Override
  public void pack(){
    System.out.println
    (packageName + " with business packaging " +
    "has been packed.");
  }
}

//abstract decorator
abstract class PackageDecorator implements Package{
  
  protected void setAddon
  (String packageName, String addon){
    System.out.println
    (addon + " add-on has been added to " +
     packageName);
  }
}

//concrete decorator
class PackageGiftWrap extends PackageDecorator{
  private Package itemPackage;
  
  public PackageGiftWrap(Package itemPackage){
    this.itemPackage = itemPackage;
  }
  
  @Override
  public void pack(){
    itemPackage.pack(); //delegation
    giftWrap();
  }
  
  private void giftWrap(){
    if(itemPackage instanceof AbstractPackage){
      AbstractPackage ap = 
      (AbstractPackage) itemPackage;
      System.out.println
      (ap.getPackageName() + 
       " has been gift wrapped.");
    }
    else
      System.out.println("Can't find package name!");
  }
}

//concrete decorator
class PackagePlasticWrap extends PackageDecorator{
  private Package itemPackage;
  
  public PackagePlasticWrap(Package itemPackage){
    this.itemPackage = itemPackage;
  }
  
  @Override
  public void pack(){
    itemPackage.pack(); //delegation
    plasticWrap();
  }
  
  private void plasticWrap(){
    if(itemPackage instanceof AbstractPackage){
      AbstractPackage ap = 
      (AbstractPackage) itemPackage;
      System.out.println
      (ap.getPackageName() + 
       " has been plastic wrapped.");
    }
    else
      System.out.println("Can't find package name!");
  }
}

Result
My Package with standard packaging has been packed.
My Company's Package with business packaging has been packed.

My Company's Package with business packaging has been packed.
My Company's Package has been gift wrapped.

My Package with standard packaging has been packed.
My Package has been plastic wrapped.
In the example above, we can independently add new functionalities to StandardPackage and BusinessPackage instances during run-time by using decorator classes. Decorator and bridge patterns have similar structure.

However, they have different purpose. Bridge pattern is used to separate two related hierarchies so that they can be expanded independently whereas decorator pattern is used to statically or dynamically add new functionalities to an instance related to decorator class instance.

Wednesday, June 8, 2022

Design Pattern: Composite Pattern

Chapters

Composite Pattern

Composite pattern is a partitioning design pattern. The composite pattern describes a group of objects that are treated the same way as a single instance of the same type of object.

The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.

These diagrams demonstrates composite pattern.
Diagram
Courtesy of Wikipedia

We can think of this method as a tree structure. A branch holds leaves and leaves are in a branch. In the diagram above, Component is a root branch and Composite is a child branch. Take a look at this example.
import java.util.List;
import java.util.ArrayList;

public class ClientCode{
  
  public static void main(String[] args){
    Package myRedPackage = 
    new RedPackage("Delivery Package", "packages");
    
    myRedPackage.addPackage(
    new RedPackage("My Colleague's Package", 
                   "Ballpens"));
    myRedPackage.addPackage(
    new RedPackage("My Package", 
                   "Pens"));
    myRedPackage.addPackage(
    new BluePackage("My Friend's Package", 
                    "Documents", true));
    
    myRedPackage.packageInfo();
    System.out.println();
    
    myRedPackage.getPackage(2).packageInfo();
    
    System.out.println();
    myRedPackage.removePackage(2);
    System.out.println();
    
    Package subPackage = 
    myRedPackage.getPackage(0);
    subPackage.packageInfo();
    System.out.println();
    
    subPackage.addPackage(
    new RedPackage("Optional Package", "Stamps"));
    subPackage.getPackage(0).packageInfo();
  }
}

//root 
abstract class Package{
  private List<Package> packageList;
  private String content;
  private String packageName;
  
  Package(String packageName, String content){
    packageList = new ArrayList<>();
    this.packageName = packageName;
    this.content = content;
  }
  
  public String getPackageName(){
    return packageName;
  }
  
  public String getContent(){
    return content;
  }
  
  public void addPackage(Package element){
    packageList.add(element);
  }
  
  public Package getPackage(int index){
    Package target = null;
    
    try{
      target = packageList.get(index);
    }
    catch(ArrayIndexOutOfBoundsException e){
      System.out.println("Invalid Index!");
    }
    return target;
  }
  
  public void removePackage(int index){
    String target = null;
  
    try{
      target = packageList.get(index).
      getPackageName();
      packageList.remove(index);
    }
    catch(ArrayIndexOutOfBoundsException e){
      System.out.println("Invalid Index!");
      return;
    }
    System.out.println
    ("Package "+ target +" is removed!");
  }
  
  abstract public void packageInfo();
}

//leaf
class RedPackage extends Package{
  
  RedPackage(String packageName, String content){
    super(packageName, content);
  }
  
  @Override
  public void packageInfo(){
    System.out.println("Name: " + 
    getPackageName());
    System.out.println("Color: Red");
    System.out.println("Parent: Package");
    System.out.println("Content: " + getContent());
  }
}

//composite
abstract class CompositeStandardPackage extends Package{
  private boolean isSpecial;
  
  CompositeStandardPackage(String packageName, 
                           String content, 
                           boolean isSpecial){
    super(packageName, content);
    this.isSpecial = isSpecial;
  }
  
  public boolean isSpecial(){
    return isSpecial;
  }
}

//leaf
class BluePackage extends CompositeStandardPackage{
  
  BluePackage(String packageName, String content,
              boolean isSpecial){
    super(packageName, content, isSpecial);
  }
  
  @Override
  public void packageInfo(){
    System.out.println("Name: " + 
    getPackageName());
    System.out.println("Color: Blue");
    System.out.println
    ("Parent: CompositeStandardPackage");
    System.out.println("Content: " + getContent());
    System.out.println("Is Special? " + isSpecial());
  }
}

Result
Name: Delivery Package
Color: Red
Parent: Package
Content: Packages

Name: My Friend's Package
Color: Blue
Parent: CompositeStandardPackage
Content: Documents
Is Special? true

Package My Friend's Package is removed!

Name: My Colleague's Package
Color: Red
Parent: Package
Content: Ballpens

Name: Optional Package
Color: Red
Parent: Package
Content: Stamps
In the example above, leaf and Composite classes can add, get and remove children(packages in this case). There are two types of child-related designs that we can use to design our composite tree structures. Take a look at this diagram.
Diagram
Courtesy of Wikipedia

In the example above, I used the "Design for Uniformity" design because the composite design pattern emphasizes uniformity over type safety. In "Design for type safety" design, only composite class and its subclasses can have children. This example in wikipedia demonstrates "Design for type safety" design.

Monday, June 6, 2022

Java Tutorial - Bridge Pattern

Chapters

Bridge Pattern

Bridge Pattern is a design pattern that is meant to "decouple an abstraction from its implementation so that the two can vary independently". For example, we have shape and we want to implement a color feature. To do this, we create a class for shape and color; combine them in one hierarchy.

The problem with this is that these two classes are going to be tightly coupled and we, programmers, avoid tight coupling if possible. To avoid tight coupling in this case, we will decouple a class of color from class of shape.

Class of shape will stand as an abstraction for class of shape color. Then, an implementation for coloring shapes of shape class will be delegated to class of shape color. We will use this diagram as reference:
Diagram
Courtesy of Wikipedia

This example demonstrates bridge pattern.
/*
Client
*/

public class ClientCode{
  public static void main(String[] args){
    Shape circ = 
    new Circle(new StandardShapeColorStyle());
    
    circ.setColorStyle("STYLE1");
    System.out.println
    ("Color style of this shape:");
    System.out.println
    (circ.getShapeColorStyle());
  }
}

/*
Abstraction
*/
abstract class Shape{
  private ShapeColorStyle scs;
  private String colorStyle;
  
  Shape(ShapeColorStyle scs){
    this.scs = scs;
  }
  
  //implementation of setColorStyle of
  //Shape class delegated to setColorStyle
  //of ShapeColorStyle
  public void setColorStyle(String style){
    if(scs == null){
      System.err.println
      ("ColorStyle instance not defined!");
      return;
    }
    colorStyle = scs.setColorStyle(style);
  }
  
  String getShapeColorStyle(){
    return colorStyle;
  }
}

class Circle extends Shape{

  Circle(ShapeColorStyle scs){
    super(scs);
  }
  
  //refined version of setColorStyle
  //of Shape class. I find this one
  //optional
  @Override
  public void setColorStyle(String style){
    System.out.println
    ("This shape is circle.");
    System.out.println
    ("Circle has infinite vertices.");
    System.out.println
    ("Setting color style...");
    super.setColorStyle(style);
  }
  
}

/*
Implementor
*/

interface ShapeColorStyle{
  String setColorStyle(String style);
}

class StandardShapeColorStyle implements 
                              ShapeColorStyle{
  private final String STYLE1 = "STYLE1";
  private final String STYLE2 = "STYLE2";
  
  @Override
  public String setColorStyle(String style){
    String colorStyle = null;
    
    switch(style){
      case STYLE1:
      colorStyle = "Color Style One";
      break;
      
      case STYLE2:
      colorStyle = "Color Style Two";
      break;
    }
    return colorStyle;
  }
}

Result
This shape is circle.
Circle has infinite vertices
Setting color style...
Color style of this shape:
Color Style One
One of the advantages of bridge pattern is that we can independently expand two related hierarchies. In the example above, Shape and ShapeColorStyle classes can be expanded without affecting each other.

Sunday, June 5, 2022

Design Pattern: Adapter Pattern

Chapters

Adapter Pattern

Adapter Pattern is a design pattern that makes two incompatible interfaces collaborate. We use adapter if a new codebase that we wanna add to our application is not compatible with our existing interface.

For example, we have an application that lists data in plain text format. Then, we have a third library that process analytics. This library is verified compatible with our application but the problem is that this library returns data in XML format. To resolve this problem, we can look for another library. However, even we find another library we need to verify if it's compatible with our application, which takes time.

Another solution is to modify the library itself. However, a lot of problems may arise. One of them is licensing issue, some libraries are protected by license and thus we can't just modify libraries. Another solution is to create an adapter which is a good solution to this problem.

There are two types of adapter pattern: Object adapter pattern and Class adapter pattern. Object adapter pattern implements the target(interface where another interface is gonna be converted) interface by delegating to an adaptee(interface that is gonna be converted to) object at run-time.

Class adapter pattern implements the target interface by inheriting from an adaptee class at compile-time. This example demonstrates object adapter pattern.
//client
public class ClientCode{

  public static void main(String[] args){
    PlainTextInterface plainText = 
    new XMLToPlainTextAdapter(new XMLAnalytics());
    
    plainText.displayData();
  }
}

//Assume our third party library has an
//interface
interface XMLInterface{

  String getAnalytics();
}

//Assume this is a third party library
class XMLAnalytics implements XMLInterface{
  private String content;
  
  XMLAnalytics(){
    content= "<data>Testing</data>";
  }
  
  @Override
  public String getAnalytics(){
    return content;
  }
  
}

//Our app's interface
interface PlainTextInterface{

  void displayData();
}

//Our app's class
class PlainTextDisplay implements PlainTextInterface{
  private String data;
  
  PlainTextDisplay(String data){
    this.data = data;
  }
  
  @Override
  public void displayData(){
    System.out.println("Data in plain text: " + data);
  }
}

//Adapter
//In object adapter pattern, we wrap adaptee interface and
//implements target interface
class XMLToPlainTextAdapter implements PlainTextInterface{
  private XMLInterface xml_analytics;
  
  XMLToPlainTextAdapter(XMLInterface xml_analytics){
    this.xml_analytics = xml_analytics;
  }
  
  @Override
  public void displayData(){
    String noTags = 
    xml_analytics.getAnalytics().
    replaceAll("[<][/]??.+?[>]","");
    
    System.out.println("XML converted to plain text");
    System.out.println("Data in plain text: " + noTags);
  }
}

Result
XML converted to plain text
Data in plain text: Testing
Next, let's implement class adapter pattern. First off, change XMLToPlainTextAdapter class to this:
class XMLToPlainTextAdapter 
      extends XMLAnalytics
      implements PlainTextInterface{
  
  @Override
  public void displayData(){
    String noTags = getAnalytics().
    replaceAll("[<][/]??.+?[>]","");
    
    System.out.println("XML converted to plain text");
    System.out.println("Data in plain text: " + noTags);
  }
}
Then, change ClientCode class to this:
public class ClientCode{

  public static void main(String[] args){
    PlainTextInterface plainText = 
    new XMLToPlainTextAdapter();
    plainText.displayData();
  }
}
The problem with class adapter pattern is that it doesn't work with all OOP languages. For example, java doesn't support multiple inheritance of classes; this pattern won't work with java if we have multiple adaptees in one adapter.

In the example above, one-way conversion has been implemented to our adapter. However, we can implement two-way conversion which converts XML to plain text and vice-versa.

To do this, we can implement both interfaces in one adapter or create another adapter for conversion from plain text to XML. In my opinion, the latter is more preferrable.

Saturday, June 4, 2022

Design Pattern: Prototype Pattern

Chapters

Prototype Pattern

Prototype pattern is a creational design pattern that copies(clone) an object rather than re-creating it. In Object Oriented Programming (OOP), objects are created by instantiating a class.

In java, there's a clone() method that we can use to clone an object rather than re-creating it via instantiation. In this pattern, we create an interface (or abstract class). Then, the client can use the interface to clone its concrete implementations. Take a look at this example.
import java.util.List;
import java.util.Random;

public class ClientCode{
  
  public static void main(String[] args)
                throws CloneNotSupportedException{
    Prototype protoA = 
    new ConcretePrototypeA("protoA");
    
    System.out.println("Original");
    protoA.displayList();
    System.out.println("\n");
    
    Prototype cloneObj = protoA.clone();
    System.out.println("Clone");
    cloneObj.displayList();
    System.out.println("\n");
    
    System.out.println("Compare Instance");
    System.out.println(
    protoA.compareListInstance(cloneObj.getList()));
  }
}

//factory class
interface Prototype extends Cloneable{
  
  Prototype clone() throws CloneNotSupportedException;
  void displayList();
  boolean compareListInstance(List<Integer> list);
  List<Integer> getList();
}

class ConcretePrototypeA implements Prototype{
  private List<Integer> numbers;
  
  ConcretePrototypeA(String name){
    Random rand = new Random();
    Integer[] values = new Integer[5];
    for(int i = 0; i < values.length; i++)
      values[i] = rand.nextInt(1000);
    numbers = java.util.Arrays.asList(values);
  }
  
  @Override
  public Prototype clone() throws CloneNotSupportedException{
    return (ConcretePrototypeA) super.clone();
  }
  
  public void displayList(){
    numbers.stream().forEach(v -> System.out.print(v + " "));
  }
  
  public boolean compareListInstance(List<Integer> list){
    return list == numbers;
  }
  
  public List<Integer> getList(){
    return numbers;
  }
  
}

Result(may vary)
Original
588 68 607 122 106

Clone
588 68 607 122 106

Compare Instance
true
You can use this pattern when you want to create another Object at runtime that is a true copy of the Object you are cloning. True copy means all the attributes of the newly created Object should be the same as the Object you are cloning.

Friday, June 3, 2022

Design Pattern: Builder Pattern

Chapters

Builder Pattern

Builder pattern is a design pattern that separates object creation from objects. It means that we dedicate a class to assemble every parts of an object that we wanna create instead of assembling parts directly in the class of the object. This pattern reduces the complexity of a class. Take a look at this example.
public class ClientCode{

  public static void main(String[] args){
    House myHouse =
    House.newBuilder("My House").
    installRoof("Gable").
    installWallpaper("Green").
    installTiles("Blue").
    installRooms(3).
    build();
    
    myHouse.houseInfo();
    System.out.println();
    
    myHouse = new 
    Director(
    House.newBuilder("My Another House")).
    construct();
    
    myHouse.houseInfo();
  }
}

class House{
  
  private String houseName;
  private String roof;
  private String wallpaper;
  private String tiles;
  private int rooms;
  private boolean garage;
  
  private House(String houseName){
    this.houseName = houseName;
  }
  
  public static House.HouseBuilder newBuilder(String houseName){
    return new House(houseName).new HouseBuilder();
  }
  
  public void houseInfo(){
    System.out.println("House Name: " + houseName);
    System.out.println("Roof: " + roof);
    System.out.println("Wallpaper: " + wallpaper);
    System.out.println("Tile Color: " + tiles);
    System.out.println("# of Rooms: " + rooms);
    System.out.println("Is there a garage? " + garage);
  }
  
  class HouseBuilder implements Builder{
    
    private HouseBuilder(){
      House.this.roof = "None";
      House.this.wallpaper = "None";
      House.this.tiles = "None";
      House.this.rooms = 0;
      House.this.garage = false;
    }
    
    @Override
    public House.HouseBuilder installRoof(String roof){
      House.this.roof = roof;
      return this;
    }
    
    @Override
    public House.HouseBuilder installWallpaper(String wallpaper){
      House.this.wallpaper = wallpaper;
      return this;
    }
    
    @Override
    public House.HouseBuilder installTiles(String tiles){
      House.this.tiles = tiles;
      return this;
    }
    
    @Override
    public House.HouseBuilder installRooms(int rooms){
      House.this.rooms = rooms;
      return this;
    }
    
    @Override
    public House.HouseBuilder installGarage(boolean install){
      House.this.garage = install;
      return this;
    }
    
    @Override
    public House build(){
      return House.this;
    }
    
  }
}

interface Builder{

  House.HouseBuilder installRoof(String roof);
  House.HouseBuilder installWallpaper(String wallpaper);
  House.HouseBuilder installTiles(String tiles);
  House.HouseBuilder installRooms(int rooms);
  House.HouseBuilder installGarage(boolean install);
  House build();
  
}

class Director{
  private Builder builder; 
  
  public Director(Builder builder){
    this.builder = builder;
  }
  
  public House construct(){
    House houseInstance = null;
    
    if(builder instanceof House.HouseBuilder){
      houseInstance = builder.
      installRoof("Dutch").
      installWallpaper("Blue").
      installRooms(4).
      installGarage(true).
      build();
    }
    return houseInstance;
  }
}

Result
House Name: My House
Roof: Gable
Wallpaper: Green
Tile color: Blue
# of Rooms: 3
Is there a garage? false

House Name: My Another House
Roof: Dutch
Wallpaper: Blue
# of Rooms: 4
Is there a garage? true
You may suggest that there are alternatives to this pattern. Let's assume you're suggesting to use a constructor:

... House(String roof, String wallpaper, String tiles, int rooms, boolean garage)
...


This alternative does make sense in the example above. However, what if want two types of builds of our house? also what if each part of a house in every build has different calculations. In this case, builder pattern is more preferrable. For example, we wanna calculate the size of a roof and each build has different ways of calculating roofs.

In this case, we can just add the calculations to their installRoof methods. Imagine coding two or more types of builds in your House. Your code can become harder to read. Using build pattern, we can encapsulate those builds and their functionalities. Making our House class more clear.

Another advantage of this pattern is that clients can build an object in step-by-step manner. Also, I didn't put any getter or setter methods in the example above but we can put those methods in the House class if we want to.

You might have noticed the Director class. This class contains pre-configured builds of our builders. If you have a build that you often use, you can add that builds in this class, so that you don't need to write your build code everytime you wanna use it. We can also use this class to save client's builds.

In java, some classes implement this pattern. HttpRequest.Builder and DateTimeFormatterBuilder are some examples of classes that implement builder pattern.

Wednesday, June 1, 2022

Degin Pattern: Abstract Factory Pattern

Chapters

Abstract Factory Pattern

Abstract factory pattern is just like factory pattern, but this pattern deals with a family of objects and its variants. For example, we are creating classes for two GUI component providers. Both create GUI components but they have different design.

We can use abstract factory method to create a class design in this scenario. Take a look at this example.
/*
Client
*/
import factories;

public class ClientCode{

  public static void main(String[] args){
    GUIComponentsFactory factory = 
    GUIComponentsFactory.
    selectFactory("Unix");
    Button createdButton = null;
    RadioButton createdRadioButton = null;
    
    if(factory != null){
      createdButton = 
      factory.createButton("MyButton");
      System.out.println
      ("Created Button: " + 
       createdButton.getName());
      System.out.println();
      
      createdRadioButton = 
      factory.createRadioButton("MyRadioButton");
      System.out.println
      ("Created Button: " + 
       createdRadioButton.getName());
    }
  }
}

/*
Factories
Assume classes below are part of factories package
*/

public interface GUIComponentsFactory{
  
  Button createButton(String name);
  RadioButton createRadioButton(String name);
  
  public static GUIComponentsFactory selectFactory(String provider){
    GUIComponentsFactory factory = null;
    
    switch(provider){
    
      case "Windows":
      factory = new CreateWinComponents();
      break;
      
      case "Unix":
      factory = new CreateUnixComponents();
      break;
    }
    return factory;
  }
}

class CreateWinComponents implements GUIComponentsFactory{
  
  CreateWinComponents(){}
  
  @Override
  public Button createButton(String name){
    return new WinButton(name);
  }
  
  @Override
  public RadioButton createRadioButton(String name){
    return new WinRadio(name);
  }
  
}

class CreateUnixComponents implements GUIComponentsFactory{
  
  CreateUnixComponents(){}
  
  @Override
  public Button createButton(String name){
    return new UnixButton(name);
  }
  
  @Override
  public RadioButton createRadioButton(String name){
    return new UnixRadio(name);
  }
}

/*
Objects
*/

pubic abstract class Button{
  private String name;
  
  Button(String name){
    this.name = name;
  }
  
  public String getName(){
    return name;
  }
  
}

class WinButton extends Button{

  WinButton(String name){
    super(name);
    System.out.println(name + 
    " Windows Button has been created!");
  }
}

class UnixButton extends Button{

  UnixButton(String name){
    super(name);
    System.out.println(name + 
    " Unix Button has been created!");
  }
}

public abstract class RadioButton{
  private String name;
  
  RadioButton(String name){
    this.name = name;
  }
  
  public String getName(){
    return name;
  }
  
}

class WinRadio extends RadioButton{

  WinRadio(String name){
    super(name);
    System.out.println(name + 
    " Windows Radio Button has been created!");
  }
}

class UnixRadio extends RadioButton{

  UnixRadio(String name){
    super(name);
    System.out.println(name + 
    " Unix Radio Button has been created!");
  }
}

Result
MyButton Unix Button has been created!
Created Button: MyButton

MyRadioButton Unix Radio Button has been created!
Created Button: MyRadioButton
Just like in factory pattern, factory superclass and objects superclass should be non-instantiable. In short, they can't be instantiated.

One of the advantages of abstract factory pattern over factory pattern is that this pattern implements greater abstraction than factory pattern. However, if you're not producing a family of objects, it's better to use factory pattern.

This pattern inherits the advantages and disadvantages of factory pattern. For more information, you may visit this website

Tuesday, May 31, 2022

Design Pattern: Factory Pattern

Chapters

Factory Pattern

Factory Pattern is a design pattern that provides a platform for creating objects in superclass, but sub-classes determine the structure of the created objects. This pattern consists of factory class, factory method, object to be created and classes that extends/implements factory class.

Factory class is a class that contains factory method. Factory method is a method that is overriden by factory subclasses of factory super class. Then overriding methods generate an object that we wanna create such as buttons, etc. This example demonstrates implementation of Factory pattern in java.
import buttonfactory;

public class ClientCode{

  public static void main(String[] args){
    
    ButtonFactory factory = 
    ButtonFactory.
    selectButtonFactory("Undecorated");
    Button createdButton = null;
    
    if(factory != null){
      createdButton = 
      factory.createButton("MyButton");
      System.out.println
      ("Created Button: " + 
       createdButton.getName());
    }
  }
}

/*
Assume classes below are in buttonfactory package
*/

public interface ButtonFactory{
  
  Button createButton(String name);
  
  static ButtonFactory selectButtonFactory(String name){
    ButtonFactory factory = null;
    switch(name){
    
      case "Standard":
      factory = 
      new CreateStandardButton();
      break;
      
      case "Undecorated":
      factory = 
      new CreateUndecoratedButton();
      break;
    }
    return factory;
  }
}

class CreateStandardButton implements ButtonFactory{
  
  CreateStandardButton(){}
  
  @Override
  public Button createButton(String name){
    return new StandardButton(name);
  }
}

class CreateUndecoratedButton implements ButtonFactory{
  
  CreateUndecoratedButton(){}
  
  @Override
  public Button createButton(String name){
    return new UndecoratedButton(name);
  }
}

public abstract class Button{
  private String name;
  
  public String getName(){
    return name;
  }
  
  Button(String name){
    this.name = name;
  }
  
}

class StandardButton extends Button{
  StandardButton(String name){
    super(name);
    System.out.println(getName() + 
    " standard button has been created!");
  }
}

class UndecoratedButton extends Button{
  UndecoratedButton(String name){
    super(name);
    System.out.println(getName() + 
    " undecorated button has been created!");
  }
}

Result
MyButton undecorated button has been created!
Created Button: MyButton
Typically, factory superclass and objects superclass should be non-instantiable. In short, they can't be instantiated.

One of the advantages of this pattern is that we can add objects, such as new button type, and another factory class subclass without affecting other button types and subclasses of factory class. We can also modify factory sub class and object subclass with little to no effect on other subclasses.

One of the disadvantages of this pattern is that adding more objects or subclasses of factory class can make this pattern complicated real quick.

For more information, you may visit this website.

Monday, May 30, 2022

Java Tutorial: Zip4j - A Java library for zip files/streams

Chapters

Introduction

Zip4J is the most comprehensive Java library for zip files or streams. As of this writing, it is the only Java library which has support for zip encryption, apart from several other features. It tries to make handling zip files/streams a lot more easier. No more clunky boiler plate code with input streams and output streams.

Requirements
JDK 7 or later*

* Zip4j is written on JDK 8, as some of the features (NIO) that Zip4j supports requires features available only in JDK 8. However, considering the fact that Zip4j is widely used in Android, and to support older versions of Android, Zip4j supports JDK 7 as well. In cases where the feature/class from JDK 8 is missing, Zip4j falls back to the features available in JDK 7. In other words, when running on JDK 7, not all features will be supported.

zip4j also supports Zip64. Zip64 removes some limitations that ZIP format has. Zip4j will automatically make a zip file with Zip64 format and add appropriate headers, when it detects the zip file to be crossing these limitations. You do not have to explicitly specify any flag for Zip4j to use this feature.

Note: If you're using maven, add this to your pom.xml and you don't need to add a new classpath in order to use zip4j.
<dependency>
    <groupId>net.lingala.zip4j</groupId>
    <artifactId>zip4j</artifactId>
    <version>2.10.0</version>
</dependency>
</pre>
Latest version can be found here.

Before running the example above, we will temporarily add a new classpath where "src\main\java" folder is located in zip4j folder. Command syntax:
set classpath=[root]:\[path];
e.g.
set classpath=C:\test\zip4j-2.10.0-master\src\main\java;
Once the new classpath is added, we can execute the example above. Once we close cmd/terminal, number of classpaths in our system will return to normal.

Create a Zip File or Add a File to a Zip File

First off, let's create a zip file and add a single file in it.
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException{
    ZipFile zip = null;
    
    try{
      zip = new ZipFile("myzip.zip");
      zip.addFile("img1.jpg");
      zip.addFile("img2.jpg");
    }
    finally{
      if(zip != null)
        zip.close();
    }
    
    System.out.println
    ("myzip.zip has been created!");
  }
}
ZipFile has add*() methods that can be used to create a zip; add a files/folder to a zip file; extract and remove files from zip file. Initializing a ZipFile instance doesn't create a new zip file.

addFile(File fileToAdd) Adds input source file to the zip file with default zip parameters. If zip file does not exist, this method creates a new zip file. This method throws an exception if the file to be added doesn't exist.

ZipParameters Encapsulates the parameters that that control how Zip4J encodes data.

I think closing a ZipFile instance is not necessary because I think the stream that is used by ZipFile is automatically closed. I'm not really sure though that's why I use try-finally clause. Although, in the documentation, the examples there regarding ZipFile don't use try-finally clause.

We can use addFiles(List<File> filesToAdd) to add multiple files to a zip file. This method adds the list of input files to the zip file with default zip parameters. Example:
import java.util.Arrays;
...
ZipFile zip = null;
...
zip = new ZipFile("myzip.zip");
zip.addFiles(Arrays.asList(
new File("img1.jpg"),
new File("img2.jpg")));
...
This method throws an exception if one of the files in the list doesn't exist. We can use addFolder(File folderToAdd) to add a directory and all of its content to our zip file. This method adds the folder in the given file object to the zip file with default zip parameters. Example:
...
zip = new ZipFile("myzip.zip");
zip.addFolder(new File("folder1/folderA"));
//valid in windows
//new File("folder1\\folderA");
...
If we want to filter files that can be put in a zip file, we can use setExcludeFileFilter(ExcludeFileFilter excludeFileFilter) method from ZipParameters. Example:
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import java.io.File;
import java.io.IOException;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException,
                            IOException{
    ZipFile zip = null;
    
    try{
      ZipParameters zp = new ZipParameters();
      zp.setExcludeFileFilter
      (exclude -> {
        if(exclude.toString().endsWith(".JPEG"))
          return true;
        else return false;
      });
      zip = new ZipFile("myzip.zip");
      zip.addFolder(new File("folder"), zp);
    }
    finally{
      if(zip != null)
        zip.close();
    }
    
    System.out.println("Done!");
  }
}
In the example above, a file with .JPEG file extension won't be included in the zip file. ZipParameters() creates a ZipParameters instance with default parameters. ExcludeFileFilter is a functional interface.

Create a Zip File or Add a File to a Zip File Using a Stream

If you need to use an input stream to add data to your zip file, you can use addStream(InputStream inputStream, ZipParameters parameters). For example:
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import java.io.IOException;
import java.io.ByteArrayInputStream;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException,
                            IOException{
    ZipFile zip = null;
    byte[] bytes = {80,81,82,83,84,85};
    
    try{
      try(ByteArrayInputStream bai = 
         new ByteArrayInputStream(bytes)){
         zip = new ZipFile("myzip.zip");
         ZipParameters zp = new ZipParameters();
         zp.setFileNameInZip("text.txt");
         
         zip.addStream(bai, zp);
      }
      
    }
    finally{
      if(zip != null)
        zip.close();
    }
    
    System.out.println("Done!");
  }
}
setFileNameInZip(String fileNameInZip) sets the name of the file where the stream data will be stored. It's required to set the name of the destination file if we're using streams to put data to our zip file. The file extension of the destination file should be equivalent to the intended file extension of the stream data. The path name must be relative and use "/" forward slash as directory separator.

Create a Zip File with STORE Compression Mode

There are two types of compression that are available to zip4j: DEFLATE and STORE. DEFLATE uses Deflate compression algorithm. DEFLATE is the default compression algorithm used by zip4j.

STORE denotes uncompressed zip file. This method just put files in a zip file without any compression. This example demonstrates using STORE compression mode.
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.CompressionMethod;
import java.io.File;
import java.io.IOException;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException,
                            IOException{
    ZipFile zip = null;
    
    try{
      zip = new ZipFile("myzip.zip");
      ZipParameters zp = new ZipParameters();
      zp.setCompressionMethod
      (CompressionMethod.STORE);
      zip.addFolder(new File("folder"), zp);
      zip.addFile(new File("img.jpg"));
    }
    finally{
      if(zip != null)
        zip.close();
    }
    
    System.out.println
    ("main thread end");
  }
}
In the example above, "folder" and all of its content use STORE compression method whereas "img.jpg" uses the default compression method which is DEFLATE. seCompressionMethod sets the ZIP compression method. CompressionMethod is an enum class that contains compression methods.

There are three compression methods in this enum. However, we can only use two because "AES_INTERNAL_ONLY" is for internal use only.

Create a Password Protected Zip File

We can also create a password protected zip file using zip4j library. EncryptionMethod is an enum class that contains encryption methods. There are three encryption methods that we can use. In this example I'm gonna use AES encryption method.

This example demonstrates creating a password protected zip file.
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.EncryptionMethod;
import net.lingala.zip4j.model.enums.AesKeyStrength;
import java.io.File;
import java.io.IOException;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException,
                            IOException{
    ZipFile zip = null;
    
    try{
      ZipParameters zp = new ZipParameters();
      zp.setEncryptFiles(true);
      zp.setEncryptionMethod
      (EncryptionMethod.AES);
      zp.setAesKeyStrength
      (AesKeyStrength.KEY_STRENGTH_256);
      
      zip = new ZipFile("myzip.zip","password".toCharArray());
      zip.addFolder(new File("folder"), zp);
      zip.addFile(new File("img.jpg"));
    }
    finally{
      if(zip != null)
        zip.close();
    }
    
    System.out.println
    ("main thread end");
  }
}
ZipFile(String zipFile, char[] password) Creates a new ZipFile instance with the zip file at the location specified in zipFile parameter. password parameter is the password of our zip file.

setEncryptFiles(boolean encryptFiles) Set the flag indicating that files are to be encrypted. We need to invoke this method in order to enable/disable zip encryption. Once the zip encryption is enabled, we add an encryption method. setEncryptionMethod(EncryptionMethod encryptionMethod) sets the encryption method used to encrypt files.

setAesKeyStrength(AesKeyStrength aesKeyStrength) sets the key strength of the AES encryption key. AesKeyStrength is an enum class that contains AES encryption key length.

There are three available key lengths that we can use. However, KEY_STRENGTH_256 is the best key length that we can use in zip4j. KEY_STRENGTH_128 is too low and KEY_STRENGTH_192 is supported only for extracting.

In the example above, "folder" is password protected inside zip file. However, "img.jpg" is not. add zp to the addFile argument-list to make "img.jpg" password protected. For example:
...
zip.addFile(new File("img.jpg"), zp);
...
If you didn't add the ZipParameters instance with setEncryptFiles(true) and setEncryptionMethod(EncryptionMethod.AES) to one of the add*() methods that you're gonna invoke, your zip file won't be password protected.

Create a Split Zip File

To store files in split zip file we can use createSplitZipFile to split files into multiple zip files/folders and createSplitZipFileFromFolder to split a folder into multiple zip files. Take a look at this example.
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Arrays;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException,
                            IOException{
    ZipFile zip = null;
    
    try{
      zip = new ZipFile("myzip.zip");
      List<File> filesToAdd =
      Arrays.asList(
      new File("img1.jpg"),
      new File("img2.jpg"),
      new File("img3.jpg"),
      new File("img4.jpg"));
      
      zip.createSplitZipFile
      (filesToAdd, 
       new ZipParameters(),
       true,
       81920);
    }
    finally{
      if(zip != null)
        zip.close();
    }
    
    System.out.println
    ("main thread end");
  }
}
If we want to split files/folders, we need to put them in a single folder and invoke createSplitZipFileFromFolder method. Take a look at this example.
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import java.io.File;
import java.io.IOException;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException,
                            IOException{
    ZipFile zip = null;
    
    try{
      zip = new ZipFile("myzip.zip");
      
      zip.createSplitZipFileFromFolder
      (new File("folder"), 
       new ZipParameters(),
       true,
       81920);
    }
    finally{
      if(zip != null)
        zip.close();
    }
    
    System.out.println
    ("main thread end");
  }
}
Now, let's take a look at the methods' forms:

createSplitZipFile(List<File> filesToAdd, ZipParameters parameters, boolean splitArchive, long splitLength)
createSplitZipFileFromFolder(File folderToAdd, ZipParameters parameters, boolean splitArchive, long splitLength)

filesToAdd parameter is the list of files that is gonna be added to our split zip file. folderToAdd is the folder that is gonna be added to our split zip file. parameters parameter consists of parameters that will be applied to a zip file.

splitArchive parameter is a flag that enables/disables split zip file mode. splitLength parameter is the split size in bytes. Note that zip file format has a minimum split size of 65536 bytes (64KB)(1024*64=65536). An exception will be thrown if we choose a split size lower than 64KB.

If we want to create a password protected split zip file, we instantiate a ZipParamaters instance and set the necessary parameters to create a password protected zip file:
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.EncryptionMethod;
import net.lingala.zip4j.model.enums.AesKeyStrength;
import java.io.File;
import java.io.IOException;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException,
                            IOException{
    ZipFile zip = null;
    
    try{
      zip = new ZipFile
      ("myzip.zip", "password".toCharArray());
      ZipParameters zp = new ZipParameters();
      zp.setEncryptFiles(true);
      zp.setEncryptionMethod
      (EncryptionMethod.AES);
      zp.setAesKeyStrength
      (AesKeyStrength.KEY_STRENGTH_256);
      
      zip.createSplitZipFileFromFolder
      (new File("folder"), zp, true, 81920);
    }
    finally{
      if(zip != null)
        zip.close();
    }
    
    System.out.println
    ("main thread end");
  }
}

Extracting Zip File

To extract all files in a zip file, we use extractAll method. Take a look at this example.
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import java.io.IOException;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException,
                             IOException{
    ZipFile zip = null;
    
    try{
      zip = new ZipFile
      ("myzip.zip");
      zip.extractAll("extracted");
    }
    finally{
      if(zip != null)
        zip.close();
    }
    
    System.out.println
    ("main thread end");
  }
}
extractAll(String destinationPath) method one parameter. destinationPath is the destination directory. extractAll method has another form:

extractAll(String destinationPath, UnzipParameters unzipParameters)

We use this form if we're dealing with symbolic links. As of this writing, UnzipParameters is not well-documented. I guess this class refers to symbolic links extraction in a zip file.

To extract a single file/directory in a zip file, we use extractFile method. Take a look at this example:
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import java.io.IOException;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException,
                             IOException{
    ZipFile zip = null;
    
    try{
      zip = new ZipFile
      ("myzip.zip");
      zip.extractFile("img.jpg", "extracted");
      zip.extractFile("folder/", "extracted");
    }
    finally{
      if(zip != null)
        zip.close();
    }
    
    System.out.println
    ("main thread end");
  }
}
extractFile(String fileName, String destinationPath) has two parameters. filename parameter refers to the path in the zip entry. When referring to a zip entry, directory separator must be forward slash("/") and path must be relative. In zip entry, a file name with "/" in the path denotes a directory.

Folder extraction using extractFile method is available to version v2.6.0 and above. destinationPath is the destination path of extracted file. Remember that the file type in destination path must be a directory/folder. Java will create destination directory if it doesn't exist.

If we want to extract a single file and give it a new name once it's extracted, we use this form of extractFile method.

extractFile(String fileName, String destinationPath, String newFileName)

For example:
...
ZipFile zip = new ZipFile
("myzip.zip", password.toCharArray());
zip.extractFile("img.jpg", "extracted", "image.jpg");
...
fileName parameter is the path name of the file in the zip file. destinationPath parameter is the destination directory. newFileName parameter is the new name of the file in the zip file once it's extracted.

Take note that the path in fileName parameter should follow zip specification. It means that the directory separator must be "/" and the path must be relative.

If we want to stream file data in a zip entry, we can get an input stream for an entry. With this, we can read data from the input stream and write the data in an output stream. To do this, we use getInputStream(FileHeader fileHeader) method. For example, we want to get the bytes of an image:
import net.lingala.zip4j.model.FileHeader;
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import java.io.InputStream;
import java.io.IOException;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException,
                             IOException{
    ZipFile zip = null;
    
    try{
      zip = new ZipFile
      ("myzip.zip");
      FileHeader header = 
      zip.getFileHeader("img.jpg");
      
      try(BufferedInputStream bif =
        new BufferedInputStream(zip.getInputStream(header));
        ByteArrayOutputStream baos = 
        new ByteArrayOutputStream()){
          
          bif.transferTo(baos);
          for(byte b : baos.toByteArray())
            System.out.print(b + " ");
          
        }
    }
    finally{
      if(zip != null)
        zip.close();
    }
  }
}
FileHeader is a class that contains file headers of a zip entry. getFileHeader(String fileName) returns FileHeader of a zip entry if a file header with the given path equivalent to fileName parameter exists in the zip model. Otherwise, returns null.

Take note that the path in fileName parameter should follow zip specification. It means that the directory separator must be "/" and the path must be relative.

extractFile has other forms that you can check them out in the documentation.

If we want to extract a password-protected zip file, we use one of ZipFile constructors:
ZipFile(File zipFile, char[] password)
ZipFile(String zipFile, char[] password)
Example:
...
ZipFile zip = new ZipFile
("myzip.zip", password.toCharArray());
zip.extractAll("destination-dir");
...
Using extractFile method.
...
ZipFile zip = 
new ZipFile
("myzip.zip", password.toCharArray());
zip.extractFile("myfile.txt", "destination-dir");
...

Rename Zip Entry

To rename a file in a zip entry, we can use renameFile(String fileNameToRename, String newFileName) method from ZipFile class. Take a look at this example:
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import java.io.IOException;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException,
                             IOException{
    ZipFile zip = null;
    
    try{
      zip = new ZipFile
      ("myzip.zip");
      zip.renameFile
      ("image1.jpg", "img1.jpg");
    }
    finally{
      if(zip != null)
        zip.close();
    }
    
    System.out.println("main thread end.");
  }
}
We can use renameFile method to move an entry to another directory entry. For example:
...
zip.renameFile("image1.jpg", "folder/image1.jpg");
...
In the example above, "image1.jpg" will be moved to "folder" directory entry. We can also move and rename file at the same time. For example:
...
zip.renameFile("image1.jpg", "folder/moved-image1.jpg");
...
In the example above, "image1.jpg" will be moved to "folder" directory entry and will be renamed as "moved-image1.jpg". If the directory where a file is going to be moved doesn't exist, java will create one and place the file there.

If we want to rename multiple files by using renameFiles(Map<String,String> fileNamesMap) method. Take a look at this example:
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import java.io.IOException;
import java.util.HashMap;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException,
                             IOException{
    ZipFile zip = null;
    
    try{
      HashMap<String, String> fileNamesMap =
      new HashMap<>();
      fileNamesMap.put
      ("folder/img1.jpg","folder/image1.jpg");
      fileNamesMap.put
      ("folder/img2.jpg","folder/image2.jpg");
      fileNamesMap.put
      ("folder/img3.jpg","folder/image3.jpg");
      
      zip = new ZipFile
      ("myzip.zip");
      zip.renameFiles(fileNamesMap);
      
    }
    finally{
      if(zip != null)
        zip.close();
    }
    
    System.out.println("main thread end.");
  }
}
A map consists of key-value pairs. In the example above, the keys are the current path name of entries and the values are the new path name of entries.

Note that zip entries can have equivalent file paths. If we rename a file in a zip file, all zip entries that have file names that are equivalent to the target file will be renamed. Also, we can rename a directory. Renaming a directory in a zip entry will update all file paths of entries in the directory.

Note that entry paths should follow zip specification. It means that the directory separator must be "/" and the path must be relative. Zip file format does not allow modifying split zip files, and Zip4j will throw an exception if an attempt is made to rename files in a split zip file.

Remove Zip Entry

If we want to remove an entry from a zip file, we can use removeFile(String fileName) method. Take a look at this example:
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import java.io.IOException;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException,
                             IOException{
    ZipFile zip = null;
    
    try{
      zip = new ZipFile
      ("myzip.zip");
      //remove a file
      zip.removeFile("img.jpg");
      //remove a directory (since v2.5.0 of Zip4j)
      zip.removeFile("folder/folderA/");
    }
    finally{
      if(zip != null)
        zip.close();
    }
    
    System.out.println("main thread end.");
  }
}
If we want to check if the file that we wanna remove exists in a zip file, we can get a FileHeader instance from a zip entry and check if the instance is null or not. If it's null, the file that we wanna delete doesn't exist in the zip file. Take a look at this example:
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.FileHeader;
import java.io.IOException;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException,
                             IOException{
    ZipFile zip = null;
    
    try{
      zip = new ZipFile
      ("myzip.zip");
      FileHeader header = 
      zip.getFileHeader("img.jpg");
      
      if(header != null){
        zip.removeFile(header);
        System.out.println("\"img.jpg\""+
        " has been removed.");
      }
    }
    finally{
      if(zip != null)
        zip.close();
    }
    
  }
}
In the example above, we use another form of removeFile method which is removeFile(FileHeader fileHeader)

If we want to remove multiple files using a single method, we use removeFiles(List<String> fileNames) method. Since v2.5.0 of zip4j, we can include a directory in the fileNames list and all of its content will be removed. This example demonstrates removeFiles method.
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import java.io.IOException;
import java.util.Arrays;

public class SampleClass{

  public static void main(String[] args)
                     throws ZipException,
                             IOException{
    ZipFile zip = null;
    
    try{
      zip = new ZipFile
      ("myzip.zip");
      zip.removeFiles(Arrays.asList(
      "img.jpg", "folder/img1.jpg",
      "folder/img2.jpg","folder/folderA/"));
    }
    finally{
      if(zip != null)
        zip.close();
    }
    
    System.out.println("main thread end.");
  }
}

Working with ZipInputStream and ZipOutputStream

If we want more control on how we compress/extract zip files, we can use ZipInputStream and ZipOutputStream instead of ZipFile class. ZipInputStream and ZipOutputStream in Zip4j is closely similar to ZipInputStream and ZipOutputStream in java.util.zip package.

If you're not familiar with ZipInputStream and ZipOutputStream, you should read this blogpost that I've created. The blogpost contains tutorial about java.util.zip package.

One of the differences between ZipInputStream and ZipOutputStream of java.util.zip package and Zip4j is that the zip input and output streams of Zip4j has constructors that supports password protected zip. java.util.zip package doesn't support password protected zip files.

This example demonstrates creating a password-protected zip file using ZipOutputStream of Zip4j. To view this example with color highlights and enable full screen, Click Here
import net.lingala.zip4j.io.outputstream.ZipOutputStream;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.AesKeyStrength;
import net.lingala.zip4j.model.enums.CompressionMethod;
import net.lingala.zip4j.model.enums.EncryptionMethod;

import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

public class SampleClass{

  public static void main(String[] args)
                     throws IOException{
    
    try(ZipOutputStream zos = 
        new ZipOutputStream(
        new FileOutputStream(
            new File("myzip.zip")), 
            "password".toCharArray())){
        
        ZipParameters zp = 
        new ZipParameters();
        zp.setCompressionMethod
        (CompressionMethod.STORE);
        zp.setEncryptFiles(true);
        zp.setEncryptionMethod
        (EncryptionMethod.AES);
        zp.setAesKeyStrength
        (AesKeyStrength.KEY_STRENGTH_256);
        
        List<FilesToAdd> filesToAdd = 
        Arrays.asList(
        new FilesToAdd
        (new File("folder/img1.jpg"), zp),
        new FilesToAdd
        (new File("folder/img2.jpg"), zp),
        new FilesToAdd
        (new File("folder/img3.jpg"), zp)
        );
        
        for(FilesToAdd fta : filesToAdd){
          
          //Entry size(bytes) must be set if we want to use
          //STORE to be used as compression method in
          //an entry.
          //
          //DEFLATE compression doesn't have this
          //requirement
          if(fta.getZipParameters().
             getCompressionMethod() == CompressionMethod.STORE)
             fta.getZipParameters().
             setEntrySize(fta.getFile().length());
          
          //This parameter must be set if we're sending
          //data to a zip file via stream
          fta.getZipParameters().setFileNameInZip
          (fta.getFile().getName());
          zos.putNextEntry(fta.getZipParameters());
          
          try(InputStream is = 
              new FileInputStream(fta.getFile())){
              is.transferTo(zos);
          }
          
          zos.closeEntry();
        }
     }
  }
}

class FilesToAdd{
  private File file;
  private ZipParameters zp;
  
  FilesToAdd(File file, ZipParameters zp){
    this.file = file;
    this.zp = zp;
  }
  
  ZipParameters getZipParameters(){
    return zp;
  }
  
  File getFile(){
    return file;
  }
  
}
Next, this example demonstrates extracting password-protected zip file using ZipInputStream of Zip4j. To view this example with color highlights and enable full screen, Click Here
import net.lingala.zip4j.io.inputstream.ZipInputStream;
import net.lingala.zip4j.model.LocalFileHeader;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.File;
import java.io.IOException;

public class SampleClass{
  
  public static void main(String[] args)
                     throws IOException{
    LocalFileHeader lfh = null;
    byte[] buffer = new byte[3072];
    
    FileInputStream fis = 
    new FileInputStream(new File("myzip.zip"));
    try(ZipInputStream zis = 
        new ZipInputStream(fis, "password".toCharArray())){
        
      while((lfh = zis.getNextEntry()) != null){
        File extractedFile = new File(lfh.getFileName());
          
        try(FileOutputStream fos = new 
            FileOutputStream("extracted/"+extractedFile)){
            
            int len = 0;
            while((len = zis.read(buffer)) != -1)
              fos.write(buffer);
        }
      }
    }
  }
}
One of the differences between FileHeader and LocalFileHeader is that FileHeader consists of general-purpose zip headers whereas LocalFileHeader consists of headers that are local from an entry.

ProgressMonitor

If we want to monitor progress of a single action, we can use ProgressMonitor. This class can monitor the progress of some methods from ZipFile class such as addFolder, addFiles, removeFiles and extractFiles.

This example demonstrates ProgressMonitor. To view this example with color highlights and enable full screen, Click Here
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.progress.ProgressMonitor;
import net.lingala.zip4j.progress.ProgressMonitor.State;
import net.lingala.zip4j.progress.ProgressMonitor.Result;

import java.io.IOException;

public class SampleClass{

  public static void main(String[] args) 
                     throws IOException,
                            InterruptedException{
    ZipFile zip = 
    new ZipFile("myzip.zip", 
                "password".toCharArray());
    zip.setRunInThread(true);
    
    ProgressMonitor pm = 
    zip.getProgressMonitor();
    zip.extractAll("extracted");
    
    while(!pm.getState().equals
         (ProgressMonitor.State.READY)){
      System.out.println("Percentage: " + 
                         pm.getPercentDone());
      System.out.println("File: " + 
                         pm.getFileName());
      System.out.println("Task: " + 
                         pm.getCurrentTask());
                         
      Thread.sleep(50);
    }
    
    if(pm.getResult().
       equals(ProgressMonitor.Result.SUCCESS))
       System.out.println("Success!");
       
    else if(pm.getResult().
            equals(ProgressMonitor.Result.ERROR))
            System.err.println("Error: " + 
            pm.getException().getMessage());
            
    else if(pm.getResult().
            equals(ProgressMonitor.Result.CANCELLED))
            System.out.println("Operation Cancelled!");
  }
}

Result(may vary)
Percentage: 0
File: null
Task: EXTRACT_ENTRY
...
Success!
Take note that this is just a demonstration. That's why the result is not very pretty. We need to put more time and effort on the example above to make a pretty result. Also take note that ProgressMonitor instance from ZipFile class may not be thread-safe. Therefore, proceed with caution when you want multiple threads to access ProgressMonitor instance from ZipFile class.

Alright, let's discuss the example above. First off, we need to invoke setRunInThread(boolean runInThread) method and set its flag to true.

This enables a background thread that monitors some actions happening in ZipFile class. setRunInThread is used in conjunction with ProgressMonitor. Thus, we need to get a ProgressMonitor instance from ZipFile to manage the progress of a task in ZipFile.

To do that, we use getProgressMonitor method. This method returns a ProgressMonitor instance from a ZipFile instance.

ProgressMonitor monitors results and tasks of an action. ProgressMonitor.State has two states: BUSY and READY. READY means that ProgressMonitor is idle and ready to monitor an action. BUSY means that ProgressMonitor is already monitoring an action.

ProgressMonitor.Task contains constants that denote tasks that may occur during compression and extraction. ProgressMonitor.Result contains constants that denote the result of an operation in ZipFile class.

getPercentDone returns the progress of an action in percentage form. getFileName method from ProgressMonitor class returns the absolute path of a file being processed in our file system. getCurrentTask method returns ProgressMonitor.Task task that is currently monitored.

Some Helpful Methods of ZipFile Class

ZipFile class has some helpful methods that can come in handy.

isSplitArchive() returns true if a zip file is a split zip file. Otherwise, returns false;
...
ZipFile zip = new ZipFile("myzip.zip");
...
System.out.println(zip.isSplitArchive());
...
getSplitZipFiles() returns a list of split zip files.
...
ZipFile zip = new ZipFile("myzip.zip");
...
if(zip.isSplitArchive())
  List<File> splitZip = zip.getSplitZipFiles();
...
mergeSplitFiles(File outputZipFile) Merges split zip files into a single zip file without the need to extract the files in the archive. This method doesn't delete the split zip file.
...
ZipFile zip = new ZipFile("myzip.zip");
...
if(zip.isSplitArchive())
  zip.mergeSplitFiles(new File("merged.zip"));
...
isEncrypted() Checks to see if the zip file is encrypted.
...
ZipFile zip = new ZipFile("myzip.zip");
...
System.out.println(zip.isEncrypted());
...
isValidZipFile() Checks to see if the input zip file is a valid zip file. Note this method only checks for the validity of the headers and not the validity of each entry in the zip file.
 ...
ZipFile zip = new ZipFile("myzip.zip");
...
System.out.println(zip.isValidZipFile());
... 
setComment(String comment) Sets comment for the Zip file. Note that the zip file must exist in our file system first before we can set comments on it.
 ...
ZipFile zip = new ZipFile("myzip.zip");
...
zip.setComment("Comment1" + "\n" + "Comment2");
... 
To remove a comment, use empty string "" as argument for setComment method. getComment() returns the comment set for the Zip file.

getFileHeaders() Returns the list of file headers in the zip file. We can use file headers to list all files in every entry of a zip file.
 ...
ZipFile zip = new ZipFile("myzip.zip");
...
List<FileHeader> fileHeaders = 
zip.getFileHeaders();
fileHeaders.stream().
forEach(fileHeader -> 
        System.out.println
        (fileHeader.getFileName()));
... 
ZipParameters

ZipParameters contains parameters that define the structure of a zip file. If we instantiate ZipParameters using its default constructor. Default values of parameters are gonna be used.

These are the default values of zip parameters.
CompressionMethod.DEFLATE
CompressionLevel.NORMAL
EncryptionMethod.NONE
AesKeyStrength.KEY_STRENGTH_256
AesVerson.Two
SymbolicLinkAction.INCLUDE_LINKED_FILE_ONLY
readHiddenFiles is true
readHiddenFolders is true
includeRootInFolder is true
writeExtendedLocalFileHeader is true
CompressionMethod.DEFLATE is the default compression method. We can change this value by calling setCompressionMethod(CompressionMethod compressionMethod). Refer to CompressionMethod class for compression method types.

CompressionLevel.NORMAL is the compression level. This parameter is only applicable to DEFLATE compression method. We can change this value by calling setCompressionLevel(CompressionLevel compressionLevel) method. Refer to CompressionLevel class for compression level types.

EncryptionMethod.NONE is the default encryption method. We change this value if we want to create a password-protected zip file. To change this value, we call setEncryptionMethod(EncryptionMethod encryptionMethod) method. Refer to EncryptionMethod class for encryption method types.

AesKeyStrength.KEY_STRENGTH_256 is the default key length of AES encryption method. To change this value, we call setAesKeyStrength(AesKeyStrength aesKeyStrength) method. Refer to AesKeyStrength for available AES key length.

AesVerson.Two is the default version of AES encryption method. To change this value, call setAesVersion(AesVersion aesVersion) method. Refer to AesVersion for AES versions.

SymbolicLinkAction.INCLUDE_LINKED_FILE_ONLY is the default action for symbolic links. To change this value, we call setSymbolicLinkAction(ZipParameters.SymbolicLinkAction symbolicLinkAction) method. Refer to ZipParameters.SymbolicLinkAction for actions for symbolic links.

readHiddenFiles parameter default value is true. To change this value, we call setReadHiddenFiles(boolean readHiddenFiles) method.

readHiddenFolders parameter default value is true. To change this value, we call setReadHiddenFolders(boolean readHiddenFolders) method.

includeRootInFolder parameter default value is true. To change this value, we call setIncludeRootFolder(boolean includeRootFolder) method. You can see the effect of this parameter if you compress a file in a directory. For example, you add this "folder/file.txt" to your zip file. If includeRootInFolder parameter is true, only "file.txt" will be included to your zip file.

writeExtendedLocalFileHeader parameter default value is true. To change this value, we call setWriteExtendedLocalFileHeader(boolean writeExtendedLocalFileHeader) method. I assume this parameter refers to extra field added to local file header. More information about extra fields can be found here.