Jekyll Liquid 템플릿에서 배열을 만들고 원소를 추가하는 방법을 알아보자


환경

  • Linux
  • Jekyll


Jekyll 배열 생성 및 추가

생성

  • split 필터 사용
  • 문자열을 ,로 구분해 배열로 만드는 필터로 문자열이 없기 때문에 빈 배열을 만듭니다.
{% assign ARRAY_NAME = "" | split: ',' %}


추가

  • push 사용해 배열에 추가하고 추가한 배열을 변수에 다시 할당합니다.
  • push를 하면 현재 배열에 추가된채로 저장되는게 아니기 때문에 새로 할당해야 합니다.
{% assign ARRAY_NAME = ARRAY_NAME | push: ELEMENT %}


예제

  • 아래는 블로그에서 특정 collections를 가져와서 새로 배열을 만드는 예시
  • opensource-contributions: 배열
  • post: 원소
{% assign opensource-contributions = "" | split: ',' %}
{% for post in site.opensource-contributions %}
  {% if post.post_id != page.post_id %}
     {% assign opensource-contributions = opensource-contributions | push: post %}
  {% endif %}
{% endfor %}


참고자료

Summarize making array and adding element in Jekyll Liquid


Environment and Prerequisite

  • Linux
  • Jekyll


Jekyll Array Create and Add

Create

  • Use split filter
  • It is used to convert comma-separated items from a string to an array but there is no string so it makes empty array.
{% assign ARRAY_NAME = "" | split: ',' %}


Add

  • Use push to add element to array and reassign to original variable.
  • push does not actually add to current array so it needs to be reassigned.
{% assign ARRAY_NAME = ARRAY_NAME | push: ELEMENT %}


Example

  • Example of making new array from specific collections in my blog.
  • opensource-contributions: Array
  • post: Element
{% assign opensource-contributions = "" | split: ',' %}
{% for post in site.opensource-contributions %}
  {% if post.post_id != page.post_id %}
     {% assign opensource-contributions = opensource-contributions | push: post %}
  {% endif %}
{% endfor %}


Reference

업데이트(2020.07.18): 예제 및 코드 변경

싱글턴 패턴(Singleton Pattern)에 대해서 알아보자


환경

  • Java


싱글턴 패턴(Singleton Pattern)이란?

싱글턴 패턴(Singleton Pattern)

  • 싱글턴 패턴(Singleton Pattern): 클래스가 오직 하나의 인스턴스만을 유지하도록 하는 패턴이며 한번 만들어지면 getInstance()와 같은 함수 또는 메소드 호출을 통해서 객체에 접근한다.
  • 이미지 출처: https://en.wikipedia.org/wiki/Singleton_pattern


구현

일반적인 구현

  • 침대를 만든다고 가정해보자.
  • 침대는 하나의 객체만 만들어져야한다고 생각하면 싱글턴 패턴으로 아래처럼 구현이 가능하다.

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
  • 위 클래스에서는 객체가 있는지 확인하고 있으면 반환하고 없다면 만들어서 반환해주는 구조
  • static를 사용해서 bed 변수와 getBed() 메소드를 만들었으며 이렇게 했기에 이 둘은 클래스 자체에 속하며 인스턴스 없이도 호출이 및 사용이 가능하다.
  • 이러한 방식을 통해 단 하나의 객체만 만들어서 사용할 수 있다.
  • 결과를 보면 모두 동일한 침대를 사용했다.


문제점

  • 만약 여러 스레드가 이 객체가 생성되기 전에 동시에 접근한다면 경합 조건(Race Condition)이 발생할 수 있다.
  • 객체를 만들기전에 스레드가 동시에 접근하면 객체를 여러개 만들게되는 상황에 직면할 수 있다.
  • 하단의 예제는 Thread.sleep(10)을 통해 객체를 여러개 생성하는 경우를 만들어본 결과이다.

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
  • 이처럼 스레드가 동시에 접근하게 되면 하나의 객체만을 만들어서 사용하려는 싱글턴 패턴의 구현에 실패하게 된다.


문제 해결 구현법 - 정적 변수에 바로 초기화

  • 클래스에서 변수를 생성함과 동시에 객체를 만든다.
  • private static final Bed bed = new Bed();처럼 생성과 동시에 객체를 만들어준다.

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
  • 모두 똑같이 하나의 객체에 접근했음을 알 수 있다.


문제 해결 구현법 - 인스턴스 생성 메소드 동기화

  • synchronized를 이용해 여러 스레드가 동시에 메소드에 접근하는걸 방지한다.

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
  • 모두 똑같이 하나의 객체에 접근했음을 알 수 있다.


문제점

  • 스레드를 2가지 방식으로 해결했어도 아래처럼 클래스에서 상태를 유지해야하는 변수가 있다면 이야기가 달라진다.
  • cnt의 값이 일관적이지 않다.

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


문제 해결 구현법 - 메소드 안에 동기화 부분 추가

  • synchronized()를 이용해 스레드의 동시 접근을 막을 부분을 감싸주면 된다.
  • synchronized(this)는 해당 객체에 대한 동기화를 의미합니다.

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
  • 이제 cnt 값도 정상적으로 나오며 객체도 하나만 생성된다.


싱글턴 패턴과 정적 클래스의 비교


참고자료

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

    }
}


참고자료