SMALL

Factory Method Pattern 정의

팩토리 메서드 패턴은 팩토리 패턴의 확장 패턴으로, 팩토리 패턴템플릿 메서드 패턴이 결합된 패턴이다

팩토리 패턴에 추상화를 결합하여 패턴을 확장한다

추상화를 통해서 골격(상위) 클래스를 제공하고, 상세한 구현은 하위 클래스로 위임

public abstract class Factory {
	public Product create() {
		return createProduct();
	}

	protected abstract Product createProduct();
}

public class ProductFactory extends Factory {

	@Override
	protected Product createProduct() {
		return new LGProduct();
	}
}

위의 예제에서 abstract class인 Factory가 골격(상위) 클래스이고, ProductFactory가 구현(하위) 클래스에 해당한다.

 

팩토리 메서드 패턴이 팩토리 + 탬플릿 메서드 패턴이라고 말하는 이유는 위의 abstract 클래스의 create 메서드를 보면 알 수 있다.

create 메서드는 abstract class의 abstract 메서드가 아니기 때문에 해당 메서드는 재정의가 불가능하다.

 

그렇기 때문에 해당 메서드에서 create를 호출할 때 실행하고 싶은 메서드를 나열하여 탬플릿화 할 수 있게 된다. 

public abstract class Factory {
	public void create() {
    
        // template method 
        createProduct();
        createSide();
        logDescription();
        
	}

	protected abstract Product createProduct();
}

 

실제 팩토리 메서드 패턴을 호출하는 코드를 보자

public class Main {
	public static void main(String[] args) {
		Factory factory = new ProductFactory();
		Product product = factory.create();
		product.name();
	}
}

이러한 형태라면, 사용자 코드에서는 create 메서드 내에 변화는 신경 쓸 필요가 없게 된다. ( 느슨한 결합 )

또한, 다른 종류의 Factory를 호출하는 코드로 바꾸어 끼고 싶다고 하여도, 다른 코드는 변경 없이 Factory를 생성하는 부분만 교체하면 된다.

 

LIST
SMALL
Factory Pattern 정의 

객체의 생성 동작을 별도 클래스로 분리하여 처리합니다.

또는 별도의 메서드를 호출하여 객체의 생성 동작을 처리합니다.

 

팩토리 패턴이 등장한 이유는 객체들 간의 의존관계를 분리하기 위해서 이다.

문제가 되는 Case를 보자 

public class Hello {
	public String greeting() {
//		return "안뇽!";
		Korean ko = new Korean();
		return ko.text();
	}
}

public class Korean {
	public String text() {
		return "안녕하세요";
	}
}

위의 Case에서는 Hello와 Korean 사이에 강한 결합 관계가 존재한다. 

 

이러한 관계를 끊어 주기 위해서, Factory Pattern을 사용한다. 

예시를 아래에서 보자.

public class Hello {
	public String greeting() {
//		return "안뇽!";
//		Korean ko = new Korean();
//		return ko.text();
		
		Language language = Factory.getInstance();
		return language.text();
	}
}

public interface Language {
	public String text();
}

public class Korean implements Language{
	
	@Override
	public String text() {
		return "안녕하세요";
	}

}

public class Factory {
	public static Language getInstance() {
		
		return new Korean();
	}
}

Hello 클래스에서 기존에 Korean 객체를 바로 생성하는 코드 대신에 Factory 클래스의 메서드를 호출하여 

강력한 결합력을 느슨한 관계로 변경하였다. 

 

또한, 추가적으로 Language Interface를 implement 하는 다양한 클래스를 두어 Factory를 통해서 다양한 객체를 반환해줄 수 있다. 

public class Hello {
	public String greeting() {
//		return "안뇽!";
//		Korean ko = new Korean();
//		return ko.text();
		
		Language language = Factory.getInstance("ko");
		return language.text();
	}
}

public class Factory {
	public static Language getInstance(String type) {
		
		if (type.equals("ko")) {
			return new Korean();	
		} else {
			return new English();
		}
		
	}
}
public interface Language {
	public String text();
}

public class Korean implements Language{
	
	@Override
	public String text() {
		return "안녕하세요";
	}

}

public class English implements Language{

	@Override
	public String text() {
		return "Hello !";
	}

}

 

Factory Pattern 장단점

팩토리 패턴을 통해 객체를 생성하면 유연한 코드 작성이 가능하고 동작도 쉽게 변경할 수 있다.

 

장점

  • 코드에서 생성과 관련된 모든 처리를 별도의 클래스 객체로 위임 가능
    ( 사용과 생성을 분리하는 과정에서 중복된 코드를 정리하는 효과 )
  • 유연성과 확장성 
    ( 개발 과정에서 클래스 이름이 변경돼도 코드를 일일이 수정하지 않고 팩토리 객체를 통해 변경 )

단점

  • 객체 생성을 위임하는 데 별도의 새로운 클래스가 필요
    ( 관리할 클래스 파일이 늘어난다 )

 

 

LIST
SMALL

JavaScript의 내장 함수들 중에 reduce(), map()에 대해서 알아보자. 

개발을 하다 보면 배열을 다루는 경우가 매우 많이 발생한다. 그럴 때마다 단순히 for문이나 foreach함수를 호출하기보다는 기본으로 제공하는 다양함 함수들을 사용하면서 익히면 JavaScript에 숙달되는데 많은 도움이 될 것이다. 

 

map()은 아래와 같은 형태로 많이 사용된다.

배열.map( (요소) => { return 요소;} )

반복문을 돌면서 배열 안에 요소들을 하나씩 ( 1:1 ) 짝지어주는 역할을 한다.

아래의 예제에서 chunks 배열에 ['banan=10', 'apple=20', orange=30']이 들어있다.

이 각각의 내용들을 { banana: '10', apple: '20', orange: '30'}과 같이 만들기 위한 작업을 보자.

  var result = {};
  chunks.forEach((chunk) => { // chunks = ['banan=10', 'apple=20', orange=30']
    const [key, value] = chunk.split('=');
    result[key] = value;
  });
  
  const result2 = chunks.map((chunk) => {
    const [key, value] = chunk.split('=');
    return { key: key, value: value };
  });
  // result2 = [{key: 'banana', value: '10'}, {key: 'apple', value: '20'} ..]

위의 코드와 같이 foreach를 통해 하는 작업을 map을 이용해서도 할 수 있다. 

 

그럼 다음으로 reduce()에 대해서 알아보자.

배열.reduce( (누적 값, 현재 값) => { return 결과; }, 초기 값 )

reduce()는 보통 배열을 값 하나로 줄이는데 쓰인다. ( 그래서 함수 이름도 reduce ( 줄어들다 ) 인가보다.. )

위에서 설명한 map()은 배열의 각 요소를 변경시킨다라 하면, reduce()는 배열 자체를 변경시킨다고 보면 될 것 같다.

chunks
    .map((chunk) => {
      const [key, value] = chunk.split('=');
      return { key, value };
    })
    .reduce((result, item) => { // item = {key: 'banana', value: '10'}
      result[item.key] = item.value;
      return result;
    }, {});
  // { banana: '10', apple: '20', orange: '30'}

위의 map()에서 본 예제에서 map()이 반환한 배열을 객체 형태로 변환하는 코드이다. 

이처럼 reduce()는 배열을 하나의 요소로 변형이 가능하게 만든다. ( 이런 경우에 많이 사용된다)

LIST
SMALL

You are given an integer array cost where cost[i] is the cost of ith step on a staircase. Once you pay the cost, you can either climb one or two steps.

You can either start from the step with index 0, or the step with index 1.

Return the minimum cost to reach the top of the floor.

 

int 배열을 입력값으로 받는다. cost[i]는 i번째 계단을 오르기 위한 금액이다.

해당 금액을 지불하면 해당 계단으로부터 1 또는 2개의 계단을 오를 수 있다. 

시작점은 0 또는 1번째 계단이 가능하다.

정상까지 도달하기 위한 최소한의 금액을 반환해라. 


해당 문제에서는 Top에 도달하기 위한 최소한의 금액이므로

반환되는 값은 Math.min( f(n-1), f(n-2)) 로 나타낼 수 있다. 

그리고, 방문한 계단에서의 최소로 지불해야 하는 값은 Math.min ( t[n-1], t[n-2] ) +cost [n]이다. 

해당 내역을 코드로 나타내면 아래와 같다.

    private int dp(int[] cost, int[] t, int len) {
        if (len == 0) {
            return t[0];
        }
        if (len == 1) {
            return t[1];
        }
        if (t[len] != 0) {
            return t[len];
        }

        int n1 = dp(cost, t, len - 1);
        int n2 = dp(cost, t, len - 2);
        t[len] = Math.min(n1, n2) + cost[len];

        return t[len];
    }
    public int minCostClimbingStairs(int[] cost) {
        int len = cost.length;
        int[] t = new int[len + 1];
        t[0] = cost[0];
        t[1] = cost[1];
        return Math.min(dp(cost, t, len - 1), dp(cost, t, len - 2));
    }

 

위의 방법은 DP문제를 Top-Down방식으로 해결하는 방법이고, 다음으로 Bottom-Up으로 해결하는 방법을 알아보자.

    public int minCostClimbingStairs(int[] cost) {
        int len = cost.length;
        int[] t = new int[len + 1];
        t[0] = cost[0];
        t[1] = cost[1];

        for (int i = 2; i < len; i++) {
        t[i] = Math.min(t[i - 1], t[i - 2]) + cost[i];
        }
        return Math.min(t[len-1], t[len-2]);
    }

 

문제 출처 :

leetcode.com/problems/min-cost-climbing-stairs/

 

Min Cost Climbing Stairs - LeetCode

Level up your coding skills and quickly land a job. This is the best place to expand your knowledge and get prepared for your next interview.

leetcode.com

 

 

LIST

+ Recent posts