Update(2020.07.18): Modify example and code.

Post about Singleton Pattern.


Environment and Prerequisite

  • Java


What is Singleton Pattern?

Singleton Pattern

  • Singleton Pattern: Software design pattern that restricts the instantiation of a class to one single instance. It access to its instance by using like getInstance() method or function.
  • Image Source: https://en.wikipedia.org/wiki/Singleton_pattern


Implementation

Usual Case Implementation

  • Let’s imagine we make bed.
  • Consider bed should be only one instance, so then we can use singleton pattern like below.

Bed

public class Bed {
    private static Bed bed = null;

    private Bed() {
    }

    public static Bed getBed() {
        if (bed == null) {
            bed = new Bed();
        }
        return bed;
    }

    public void print(String str) {
        System.out.println(str);
    }
}

User

public class User {
    private String name;

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

    public void print() {
        Bed.getBed().print(this.name + " " + "is in" + " " + Bed.getBed().toString() + " " + "bed");
    }
}

Main

public class Main {
    public static void main(String[] args) {
        final int NUM_OF_USERS = 6;
        User[] users = new User[NUM_OF_USERS];
        for (int i = 0; i < NUM_OF_USERS; ++i) {
            users[i] = new User(i + "-user");
            users[i].print();
        }
    }
}

Result

0-user is in com.company.Bed@60e53b93 bed
1-user is in com.company.Bed@60e53b93 bed
2-user is in com.company.Bed@60e53b93 bed
3-user is in com.company.Bed@60e53b93 bed
4-user is in com.company.Bed@60e53b93 bed
5-user is in com.company.Bed@60e53b93 bed
  • Above class makes instance if there is no instance. If instance exists, then return the instance.
  • Use static to make bed variable and getBed() method. bed variable and getBed() method are belong to class so it can be used without instance.
  • By using like this, we can make only one instance.
  • Result shows that there is only one bed instance.


Problem

  • There can be a race condition if multi threads access to this instance simultaneously before instantiation.
  • There can be multiple instances if threads access simultaneously before making instance.
  • Below example uses Thread.sleep(10) to make multiple instances.

Bed

public class Bed {
    private static Bed bed = null;

    private Bed() {
    }

    public static Bed getBed() {
        if (bed == null) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                System.out.println(e.toString());
            }
            bed = new Bed();
        }
        return bed;
    }

    public void print(String str) {
        System.out.println(str);
    }
}

User

public class User extends Thread {
    private String name;

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

    public void run() {
        Bed.getBed().print(this.name + " " + "is in" + " " + Bed.getBed().toString() + " " + "bed");
    }
}

Main

public class Main {
    public static void main(String[] args) {
        final int NUM_OF_USERS = 6;
        User[] users = new User[NUM_OF_USERS];
        for (int i = 0; i < NUM_OF_USERS; ++i) {
            users[i] = new User(i + "-user");
            users[i].start();
        }
    }
}

Result

0-user is in com.company.Bed@4d0c434e bed
2-user is in com.company.Bed@4d0c434e bed
5-user is in com.company.Bed@135b88ad bed
1-user is in com.company.Bed@17733f02 bed
3-user is in com.company.Bed@4d0c434e bed
4-user is in com.company.Bed@2c79921e bed
  • Like this, if threads access simultaneously then it fails to make one instance in singleton pattern.


Solution - Instantiate when initialize class

  • Make instance immediately when make variable in class.
  • Like private static final Bed bed = new Bed();, make instance immediately.

Bed

public class Bed {
    private static final Bed bed = new Bed();

    private Bed() {
    }

    public static Bed getBed() {
        if (bed == null) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                System.out.println(e.toString());
            }
        }
        return bed;
    }

    public void print(String str) {
        System.out.println(str);
    }
}

User

public class User extends Thread {
    private String name;

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

    public void run() {
        Bed.getBed().print(this.name + " " + "is in" + " " + Bed.getBed().toString() + " " + "bed");
    }
}

Main

public class Main {
    public static void main(String[] args) {
        final int NUM_OF_USERS = 6;
        User[] users = new User[NUM_OF_USERS];
        for (int i = 0; i < NUM_OF_USERS; ++i) {
            users[i] = new User(i + "-user");
            users[i].start();
        }
    }
}

Result

1-user is in com.company.Bed@6fa76e7 bed
3-user is in com.company.Bed@6fa76e7 bed
2-user is in com.company.Bed@6fa76e7 bed
0-user is in com.company.Bed@6fa76e7 bed
4-user is in com.company.Bed@6fa76e7 bed
5-user is in com.company.Bed@6fa76e7 bed
  • All threads access to only one instance.


Solution - Synchronize instance construction method

  • Use synchronized to prevent from accessing multiple threads to method simultaneously.

Bed

public class Bed {
    private static Bed bed = null;

    private Bed() {
    }

    public synchronized static Bed getBed() {
        if (bed == null) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                System.out.println(e.toString());
            }
            bed = new Bed();
        }
        return bed;
    }

    public void print(String str) {
        System.out.println(str);
    }
}

User

public class User extends Thread {
    private String name;

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

    public void run() {
        Bed.getBed().print(this.name + " " + "is in" + " " + Bed.getBed().toString() + " " + "bed");
    }
}

Main

public class Main {
    public static void main(String[] args) {
        final int NUM_OF_USERS = 6;
        User[] users = new User[NUM_OF_USERS];
        for (int i = 0; i < NUM_OF_USERS; ++i) {
            users[i] = new User(i + "-user");
            users[i].start();
        }
    }
}

Result

5-user is in com.company.Bed@6fa76e7 bed
2-user is in com.company.Bed@6fa76e7 bed
0-user is in com.company.Bed@6fa76e7 bed
4-user is in com.company.Bed@6fa76e7 bed
3-user is in com.company.Bed@6fa76e7 bed
1-user is in com.company.Bed@6fa76e7 bed
  • All threads access to only one instance.


Problem

  • Even though above multiple thread problems are solved, if there is variable in class like below then there comes another problem.
  • cnt’s value is not consistent.

Bed

public class Bed {
    private static Bed bed = null;
    private static int cnt = 0;

    private Bed() {
    }

    public synchronized static Bed getBed() {
        if (bed == null) {
            bed = new Bed();
        }
        return bed;
    }

    public void print(String str) {
        cnt++;
        System.out.println(str + " " + "cnt: " + cnt);
    }
}

User

public class User extends Thread {
    private String name;

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

    public void run() {
        Bed.getBed().print(this.name + " " + "is in" + " " + Bed.getBed().toString() + " " + "bed");
    }
}

Main

public class Main {
    public static void main(String[] args) {
        final int NUM_OF_USERS = 6;
        User[] users = new User[NUM_OF_USERS];
        for (int i = 0; i < NUM_OF_USERS; ++i) {
            users[i] = new User(i + "-user");
            users[i].start();
        }
    }
}

Result

1-user is in com.company.Bed@1f904935 bed cnt: 2
4-user is in com.company.Bed@1f904935 bed cnt: 2
0-user is in com.company.Bed@1f904935 bed cnt: 2
3-user is in com.company.Bed@1f904935 bed cnt: 3
2-user is in com.company.Bed@1f904935 bed cnt: 3
5-user is in com.company.Bed@1f904935 bed cnt: 4


Solution - Add synchronized in method

  • Cover method with synchronized() to prevent from simultaneous threads access.
  • synchronized(this) means sync to its instance.

Bed

public class Bed {
    private static Bed bed = null;
    private static int cnt = 0;

    private Bed() {
    }

    public synchronized static Bed getBed() {
        if (bed == null) {
            bed = new Bed();
        }
        return bed;
    }

    public void print(String str) {
        synchronized (this) {
            cnt++;
            System.out.println(str + " " + "cnt: " + cnt);
        }
    }

    /*
    public synchronized void print(String str) {
        cnt++;
        System.out.println(str + " " + "cnt: " + cnt);
    }
    */
}

User

public class User extends Thread {
    private String name;

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

    public void run() {
        Bed.getBed().print(this.name + " " + "is in" + " " + Bed.getBed().toString() + " " + "bed");
    }
}

Main

public class Main {
    public static void main(String[] args) {
        final int NUM_OF_USERS = 6;
        User[] users = new User[NUM_OF_USERS];
        for (int i = 0; i < NUM_OF_USERS; ++i) {
            users[i] = new User(i + "-user");
            users[i].start();
        }
    }
}

Result

0-user is in com.company.Bed@4f90c612 bed cnt: 1
3-user is in com.company.Bed@4f90c612 bed cnt: 2
2-user is in com.company.Bed@4f90c612 bed cnt: 3
1-user is in com.company.Bed@4f90c612 bed cnt: 4
4-user is in com.company.Bed@4f90c612 bed cnt: 5
5-user is in com.company.Bed@4f90c612 bed cnt: 6
  • Now cnt is printed well and there is only one instance.


Comparison between singleton pattern and static class


Reference

스트레티지 패턴(Strategy Pattern)에 대해서 알아보자


환경

  • Java


스트레티지 패턴(Strategy Pattern)이란?

스트레티지 패턴(Strategy Pattern)

  • 스트레티지 패턴(Strategy Pattern): 특정한 알고리즘을 정의하고 이를 캡슐화하여 해당 알고리즘이 필요한 대상들이 이를 상호 교체 가능하게 하는 구조
  • Java의 경우 인터페이스를 생성하고 해당 부분을 구현해서 캡슐화 하고 이를 원하는 객체의 메소드에 붙여주면 된다. 자세한 설명과 예제는 아래 참고
  • 이미지 출처: https://en.wikipedia.org/wiki/Strategy_pattern


필요성

로봇 사례

  • 로봇을 만든다고 생각해보자. 로봇은 태권V도 있고 아톰도 있고 건담도 있다. 이때 어떤 방법으로 만들어야할까? 가장 기본틀인 로봇을 추상 클래스로 만들고 상속을 통해 각각을 만들어보자.
  • 우선 로봇 추상 클래스를 만들고 태권V와 아톰만 만들어보자.


예제

Robot

public abstract class Robot {
    private String name;
    public Robot(String name){ this.name = name; }

    public String getName(){ return name;}

    public abstract void attack();
    public abstract void move();
}

TaekwonV

public class TaekwonV extends Robot {

    public TaekwonV(String name){
        super(name);
    }

    @Override
    public void attack() {
        System.out.println("kick");
    }

    @Override
    public void move() {
        System.out.println("walk");
    }
}

Atom

public class Atom extends Robot {

    public Atom (String name){
        super(name);
    }

    @Override
    public void attack() {
        System.out.println("punch");
    }

    @Override
    public void move() {
        System.out.println("fly");
    }
}

Main

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

        Robot taekwonV = new TaekwonV("TaekwonV");
        Robot atom = new Atom("Atom");

        System.out.println("I am " + taekwonV.getName());
        taekwonV.attack();
        taekwonV.move();

        System.out.println();

        System.out.println("I am " + atom.getName());
        atom.attack();
        atom.move();
    }
}


해당 코드의 문제점

  • 기존 로봇의 공격 방법을 수정하려면 새로운 기능을 위해 기존 코드를 수정하기 때문에 OCP(Open-Closed Principle)에 위반된다. 예를들어 위의 예제에서 아톰이 태권V와 똑같은 move()를 사용하려 하면 코드를 직접 수정해야하고 나아가 중복이 발생하며 로봇의 종류가 많아질수록 코드의 관리는 어려워진다.
  • 새로운 로봇을 만들어서 아톰과 같은 공격과 태권V와 같은 이동을 하게 하고 싶다면? 동일한 코드를 복사하게 작성하게 되면 중복이 발생하게 된다. 예를 들어서 건담을 만들때 아톰의 attack()과 태권V의 move() 코드를 또 동일하게 작성하기 때문에 중복이 발생한다.


해결 방법

  • 로봇을 만드는 과정에 있어서 변화하는 부분이 무엇인지 먼저 찾아야한다. 변화될 수 있는 부분을 찾아서 이를 캡슐화하고 상호 교체 가능하도록 만드는것이 중요하다.
  • 예제 코드들에서는 attack()과 move()의 코드들이 계속 변경될 수 있으며 이를 캡슐화해서 필요할때 붙여주면 코드의 중복도 줄이고 코드의 변경도 줄일 수 있다.
  • 직접적인 구현은 각각을 interface로 구현해서 객체를 만들때 붙여주면 된다.
  • 그림에서 보면 move()의 경우에 MovingStrategy라는 인터페이스를 만들고 각각의 방법에 대한 구현을 implements를 통해서 클래스화 시키면된다.


예제

Robot

public abstract class Robot {
    private String name;
    private AttackStrategy attackStrategy;
    private MovingStrategy movingStrategy;

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

    public String getName() {
        return name;
    }

    public void attack() {
        attackStrategy.attack();
    }

    public void move() {
        movingStrategy.move();
    }

    public void setAttackStrategy(AttackStrategy attackStrategy) {
        this.attackStrategy = attackStrategy;
    }

    public void setMovingStrategy(MovingStrategy movingStrategy) {
        this.movingStrategy = movingStrategy;
    }

}

AttackStrategy

public interface AttackStrategy {
    public void attack();
}

MovingStrategy

public interface MovingStrategy {
    public void move();
}

KickingStrategy

public class KickingStrategy implements AttackStrategy {
    @Override
    public void attack() {
        System.out.println("kick");
    }
}

PunchingStrategy

public class PunchingStrategy implements AttackStrategy {
    @Override
    public void attack() {
        System.out.println("punch");
    }
}

WalkingStrategy

public class WalkingStrategy implements MovingStrategy {
    @Override
    public void move() {
        System.out.println("walk");
    }
}

FlyingStrategy

public class FlyingStrategy implements MovingStrategy {
    @Override
    public void move() {
        System.out.println("fly");
    }
}

TaekwonV

public class TaekwonV extends Robot {
    public TaekwonV(String name) {
        super(name);
    }
}

Atom

public class Atom extends Robot {
    public Atom(String name) {
        super(name);
    }
}

Gundam

public class Gundam extends Robot {
    public Gundam(String name) {
        super(name);
    }
}

Main

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

        Robot taekwonV = new TaekwonV("TaekwonV");
        Robot atom = new Atom("Atom");
        Robot gundam = new Gundam("Gundam");

        taekwonV.setAttackStrategy(new KickingStrategy());
        taekwonV.setMovingStrategy(new WalkingStrategy());

        atom.setAttackStrategy(new PunchingStrategy());
        atom.setMovingStrategy(new FlyingStrategy());

        gundam.setAttackStrategy(new PunchingStrategy());
        gundam.setMovingStrategy(new WalkingStrategy());

        System.out.println("I am " + taekwonV.getName());
        taekwonV.attack();
        taekwonV.move();

        System.out.println();

        System.out.println("I am " + atom.getName());
        atom.attack();
        atom.move();

        System.out.println();

        System.out.println("I am " + gundam.getName());
        gundam.attack();
        gundam.move();

    }
}


참고자료

Post about Strategy Pattern.


Environment and Prerequisite

  • Java


What is Strategy Pattern?

Strategy Pattern

  • Strategy Pattern: It is a design pattern which define set of algorithms and interchange those algorithms when it is needed. Those algorithms are encapsulated by class. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.
  • In case of java, define strategy to interface and implement it in class to encapsulate it. Add it to object’s method when you use it. There will be an examples below.
  • Image Source: https://en.wikipedia.org/wiki/Strategy_pattern


Necessity

Robot Example

  • Let’s imagine we make robots. Robots will be TaekwonV, Atom and Gundam. How to implement them then? Make basic form in abstract Robot class and use inheritance(‘extends’ keyword in java).
  • First, let’s make abstract Robot class, TaekwonV class and Atom class.


Example

Robot

public abstract class Robot {
    private String name;
    public Robot(String name){ this.name = name; }

    public String getName(){ return name;}

    public abstract void attack();
    public abstract void move();
}

TaekwonV

public class TaekwonV extends Robot {

    public TaekwonV(String name){
        super(name);
    }

    @Override
    public void attack() {
        System.out.println("kick");
    }

    @Override
    public void move() {
        System.out.println("walk");
    }
}

Atom

public class Atom extends Robot {

    public Atom (String name){
        super(name);
    }

    @Override
    public void attack() {
        System.out.println("punch");
    }

    @Override
    public void move() {
        System.out.println("fly");
    }
}

Main

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

        Robot taekwonV = new TaekwonV("TaekwonV");
        Robot atom = new Atom("Atom");

        System.out.println("I am " + taekwonV.getName());
        taekwonV.attack();
        taekwonV.move();

        System.out.println();

        System.out.println("I am " + atom.getName());
        atom.attack();
        atom.move();
    }
}


Problems in above code.

  • To modify existed code to new code, we need to modify existed code and it violates OCP(Open-Closed Principle). For example in above code if we like to use same move() method in both Atom and TaekwonV we need to modify existed code and it also cause duplicate code. As the number of robots grows, it is hard to manage code.
  • What if we like to make new robot and use same method of TaekwonV’s move() and Atom’s attack()? There will be a code duplication if we copy and paste it.


Solution

  • In implementing robot, we need to find which part of code are changeable. Find changing part, encapsulate it and make it interchangeable.
  • In above code, attack() and move() are changeable. So encapsulate those methods and use it when it is needed. It prevents from code duplication and code modifying.
  • Make each to java’s interface and make object when it is needed.
  • In case of move() method, make MovingStrategy interface and implements it in class.


Example

Robot

public abstract class Robot {
    private String name;
    private AttackStrategy attackStrategy;
    private MovingStrategy movingStrategy;

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

    public String getName() {
        return name;
    }

    public void attack() {
        attackStrategy.attack();
    }

    public void move() {
        movingStrategy.move();
    }

    public void setAttackStrategy(AttackStrategy attackStrategy) {
        this.attackStrategy = attackStrategy;
    }

    public void setMovingStrategy(MovingStrategy movingStrategy) {
        this.movingStrategy = movingStrategy;
    }

}

AttackStrategy

public interface AttackStrategy {
    public void attack();
}

MovingStrategy

public interface MovingStrategy {
    public void move();
}

KickingStrategy

public class KickingStrategy implements AttackStrategy {
    @Override
    public void attack() {
        System.out.println("kick");
    }
}

PunchingStrategy

public class PunchingStrategy implements AttackStrategy {
    @Override
    public void attack() {
        System.out.println("punch");
    }
}

WalkingStrategy

public class WalkingStrategy implements MovingStrategy {
    @Override
    public void move() {
        System.out.println("walk");
    }
}

FlyingStrategy

public class FlyingStrategy implements MovingStrategy {
    @Override
    public void move() {
        System.out.println("fly");
    }
}

TaekwonV

public class TaekwonV extends Robot {
    public TaekwonV(String name) {
        super(name);
    }
}

Atom

public class Atom extends Robot {
    public Atom(String name) {
        super(name);
    }
}

Gundam

public class Gundam extends Robot {
    public Gundam(String name) {
        super(name);
    }
}

Main

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

        Robot taekwonV = new TaekwonV("TaekwonV");
        Robot atom = new Atom("Atom");
        Robot gundam = new Gundam("Gundam");

        taekwonV.setAttackStrategy(new KickingStrategy());
        taekwonV.setMovingStrategy(new WalkingStrategy());

        atom.setAttackStrategy(new PunchingStrategy());
        atom.setMovingStrategy(new FlyingStrategy());

        gundam.setAttackStrategy(new PunchingStrategy());
        gundam.setMovingStrategy(new WalkingStrategy());

        System.out.println("I am " + taekwonV.getName());
        taekwonV.attack();
        taekwonV.move();

        System.out.println();

        System.out.println("I am " + atom.getName());
        atom.attack();
        atom.move();

        System.out.println();

        System.out.println("I am " + gundam.getName());
        gundam.attack();
        gundam.move();

    }
}


Reference

cURL 사용 시 데이터(text/plain, x-www-form-urlencoded 또는 json)를 같이 보내는 법을 알아보자.


환경

  • Linux
  • cURL


cURL

옵션을 사용해 데이터를 포함한 요청 보내기

  • -d 옵션을 사용
curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'


사용 예제

text/plain

  • -d 옵션을 사용하고 POST 방식으로 text 형태의 자료를 보내는 예제
curl -XPOST -H "Content-Type: text/plain" -d "raw text data" http://127.0.0.1:5000/test

application/x-www-form-urlencoded

  • -d 옵션을 사용하고 POST 방식으로 x-www-form-urlencoded 형태의 자료를 보내는 예제
curl -XPOST -H "Content-Type: application/x-www-form-urlencoded" -d "param1=value1&param2=value2" http://127.0.0.1:5000/test

application/json

  • -d 옵션을 사용하고 POST 방식으로 JSON 형태의 자료를 보내는 예제
curl -XPOST -H "Content-Type: application/json" http://127.0.0.1:5000/test -d '
{
   "test": "1234test",
   "child": {
      "child_1": "Hi",
      "cihld_2": "Hi"
   },
   "elements": [ "a", "b", "c" ]
}
'


참고자료

Summarize how to attach data(text/plain, x-www-form-urlencoded or json) when using cURL


Environment and Prerequisite

  • Linux
  • cURL


cURL

Send request with data using option

  • Use -d option.
curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'


Usage Example

text/plain

  • Example of sending text format data with using -d option and POST method.
curl -XPOST -H "Content-Type: text/plain" -d "raw text data" http://127.0.0.1:5000/test

application/x-www-form-urlencoded

  • Example of sending x-www-form-urlencoded format data with using -d option and POST method.
curl -XPOST -H "Content-Type: application/x-www-form-urlencoded" -d "param1=value1&param2=value2" http://127.0.0.1:5000/test

application/json

  • Example of sending JSON format data with using -d option and POST method.
curl -XPOST -H "Content-Type: application/json" http://127.0.0.1:5000/test -d '
{
   "test": "1234test",
   "child": {
      "child_1": "Hi",
      "cihld_2": "Hi"
   },
   "elements": [ "a", "b", "c" ]
}
'


Reference