파이썬 디자인 패턴 - 생성 디자인 패턴 - 팩터리 메서드 패턴
13 Aug 2018 | python designpattern 생성디자인패턴
1-3. 팩터리 메서드 패턴
- 팩터리 메서트 패턴은 객체 생성을 요청할 때 하위 클래스가 인스턴스화할 클래스를 지정할 수 있게끔 고안된 패턴
- 생성하려는 클래스가 어떤 것인지 모를 때도 활용할 수 있다.
- 특정 게임판을 생성하는 하위 클래스의 바탕이 되는 추상 보드 클래스 생성
- 게임에 따라 각 말이 자체 클래스를 갖게 하고싶음
게임판을 인스턴스화하고 출력하는 최상위 코드
- 이 함수는 프로그램 모든 버전에서 동일. 각 게임판을 생성하고 콘솔에 출력
gameboard1.py
___str___()
메서드를 통해 게임판 내부 표현을 문자열로 변환
- 게임판은 한 글자로 된 문자열 리스트로 표현
- 말이없는 칸은 None
- console() 함수는 배경색 위에 배치할 말이 담긴 문자열을 반환
- AbstractFormBuilder와 마찬가지로
AbstractBoard
클래스에 abc.ABCMeta
메타클래스를 전달해 추상 클래스를 만들 수 있지만, 본 코드에서는 raise NotImplementedError()
를 사용 (하위 클래스에서 implement하지 않으면 에러 발생)
AbstractBoard
를 구현한 concrete class인 CheckersBoard()
class 는 10x10 국제 규격 체커판을 생성
poppulate_board()
메서드는 하드코딩된 클래스(BlackDraought
, WhiteDraught
)를 활용하기 때문에 팩터리 메서드가 아니다!
- 이 메서드를 어떻게 팩터리 메서트로 만들지 보여주기 위해서 첨부
ChessBoard
클래스도 마찬가지로 poppulate_board()
메서드가 어떻게 팩터리 메서드로 바뀌는지 보여주기 위해 첨부
Piece()
클래스는 말에 대한 기반 클래스
str
변수만 사용할 수 도 있지만, 그렇게 하면 어떤 객체가 말인지 판단할 수 없음
__slots__=()
를 사용해 인스턴스가 어떤 데이터도 가실 수 없도록 보장 (2.6에서 살펴볼 예정)
- 모든 말 클래스가 공통적으로 활용하는 패턴을 볼 수 있음
- 각 말은 변경 불가능한
Piece
의 하위 클래스(자체가 str의 하위 클래스)
- 각 말을 표현하는 유니코드 글자가 담긴 1글자짜리 문자열로 초기화
- 소규모 하위 클래스가 14 개가 있음
- 중복이 굉장히 많다!
gameboard2.py
- 위의 gameboard1.py 의
populate_board()
와 다르게 create_piece()
팩터리 메서드를 활용한다.
creae_piece()
함수는 인자에 따라 적절한 타입의 객체(BlackDraought
나 WhiteDraught
같은)를 반
ChessBoard
class에도 똑같이 존재 (색깔과 말 이름을 인자로 전달)
- 이 팩터리 메서트는 내장 함수인
eval()
을 활용해 클래스 인스턴스를 생성
- ex) 인자가 “knight”, “black”이면
eval("{}Chess{}")~
을 통해 BlackChessKnight()
가 됨
- 하지만
eval()
은 위험 (어떤것이든 생성 가능)
- gameboard1.py 의 소규모 하위 클래스 14개를 단 한 블록의 코드로 클래스를 모두 생성했다.
itertools.chain()
: 하나 이상의 iterable
을 받아 전체를 순회할 수 있는 단일 iterable
을 반환
- 여기서는 두 개의
iterable
을 전달
- 검은색과 하얀색 체커 말의 유니코드 포인트에 대한 2-튜플
- 검은색과 하얀색 체스 말에 대한 range 객체
- 각 유니코드 포인트에 대해 한 글자로 된 문자열을 생성해 이 문자의 유니코드 명칭에 따라 클래스 이름을 생성 (“black chess knight”는
BlackChessKnight
가 된다)
exec()
을 사용
- 너무 위험함
gameboard3.py
- gameboard2.py 의 populate_board() 메서드와는 다르게, 잘못 입력하기 쉬운 문자열 리터럴 대신 상수 값을 사용해 말과 색깔을 지정한다.
- 각 말을 생성하기 위해 새로운
creae_piece()
팩터리를 활용
- 위는
ChessBoard
와 CheckersBoard
가 상속 할 추상 클래스이다
- 두 클래스는
create_piece()
를 상속한다.
create_piece()
는 ~Board 클래스의 __classForPiece
dict를 참조하여 kind, color를 받아 말 이름을 반환한다.\
- gameboard2.py 와 다르게
exec()
이나 eval()
을 사용하지 않고 안전하게 동적으로 생성할 수 있다.
- gameboard2.py 와 다르게
eval()
이나 exec()
을 활용하는 대신 안전한 방법 사용
- 일단
code
에서 글자와 그에 대한 명칭을 가지고 있다면 maek_new_method()
메서드를 호출해 해로운 함수 (new()
)를 생성
- 내장함수인
type()
을 활용해 새로운 클래스를 생성
type()
을 이용해 클래스를 생성하려면
- 클래스 타입
- 기반 클래스들에 대한 튜플 (Piece 하나만 넘김)
- 클래스 애트리뷰트에 대한 딕셔너리
- 방금 생성한
new()
함수를 __new__
메서드로 설정
- 현재 모듈(
sys.modules[__name__]
)에 새로 생성한 클래스를 name이라는 애트리뷰트로 추가하기 위해 setattr()
를 호출
make_new_method()
는 new()
함수를 생성
super()
는 활용 할 수 없음, 왜냐면 new()
가 생성될 시기에는 super()
가 접근할 클래스가 아직 없기 때문
***
gameboard4.py
- gameboard3.py 에서
setattr()
를 사용한 것과 달리, 전역변수가 저장된 딕셔너리의 참조를 얻어, name에 있는 값이 key이고 새로 생성한 Class가 값인 새 아이템을 추가 -> setattr()
와 똑같이 동작
new()
메서드 생성의 경우에도, lambda
를 활용하여 해결
create_piece()
가 gameboard4.py 의 팩터리 메서드이다.
- 사용되는 상수값은 gameboard3.py 와 같지만, 클래스 객체의 dict 대신 내장 함수인
globals()
를 통해 얻은 dict에서 해당 클래스를 동적으로 찾는다.
- 이렇게 얻은 클래스 객체를 바로 호출해서 원하는 말 인스턴스를 얻는다.
1-3. 팩터리 메서드 패턴
- 팩터리 메서트 패턴은 객체 생성을 요청할 때 하위 클래스가 인스턴스화할 클래스를 지정할 수 있게끔 고안된 패턴
- 생성하려는 클래스가 어떤 것인지 모를 때도 활용할 수 있다.
- 특정 게임판을 생성하는 하위 클래스의 바탕이 되는 추상 보드 클래스 생성
- 게임에 따라 각 말이 자체 클래스를 갖게 하고싶음
게임판을 인스턴스화하고 출력하는 최상위 코드
- 이 함수는 프로그램 모든 버전에서 동일. 각 게임판을 생성하고 콘솔에 출력
gameboard1.py
___str___()
메서드를 통해 게임판 내부 표현을 문자열로 변환- 게임판은 한 글자로 된 문자열 리스트로 표현
- 말이없는 칸은 None
- console() 함수는 배경색 위에 배치할 말이 담긴 문자열을 반환
- AbstractFormBuilder와 마찬가지로
AbstractBoard
클래스에abc.ABCMeta
메타클래스를 전달해 추상 클래스를 만들 수 있지만, 본 코드에서는raise NotImplementedError()
를 사용 (하위 클래스에서 implement하지 않으면 에러 발생)
AbstractBoard
를 구현한 concrete class인CheckersBoard()
class 는 10x10 국제 규격 체커판을 생성poppulate_board()
메서드는 하드코딩된 클래스(BlackDraought
,WhiteDraught
)를 활용하기 때문에 팩터리 메서드가 아니다!- 이 메서드를 어떻게 팩터리 메서트로 만들지 보여주기 위해서 첨부
ChessBoard
클래스도 마찬가지로poppulate_board()
메서드가 어떻게 팩터리 메서드로 바뀌는지 보여주기 위해 첨부
Piece()
클래스는 말에 대한 기반 클래스str
변수만 사용할 수 도 있지만, 그렇게 하면 어떤 객체가 말인지 판단할 수 없음__slots__=()
를 사용해 인스턴스가 어떤 데이터도 가실 수 없도록 보장 (2.6에서 살펴볼 예정)
- 모든 말 클래스가 공통적으로 활용하는 패턴을 볼 수 있음
- 각 말은 변경 불가능한
Piece
의 하위 클래스(자체가 str의 하위 클래스) - 각 말을 표현하는 유니코드 글자가 담긴 1글자짜리 문자열로 초기화
- 각 말은 변경 불가능한
- 소규모 하위 클래스가 14 개가 있음
- 중복이 굉장히 많다!
gameboard2.py
- 위의 gameboard1.py 의
populate_board()
와 다르게create_piece()
팩터리 메서드를 활용한다. creae_piece()
함수는 인자에 따라 적절한 타입의 객체(BlackDraought
나WhiteDraught
같은)를 반ChessBoard
class에도 똑같이 존재 (색깔과 말 이름을 인자로 전달)
- 이 팩터리 메서트는 내장 함수인
eval()
을 활용해 클래스 인스턴스를 생성- ex) 인자가 “knight”, “black”이면
eval("{}Chess{}")~
을 통해BlackChessKnight()
가 됨 - 하지만
eval()
은 위험 (어떤것이든 생성 가능)
- ex) 인자가 “knight”, “black”이면
- gameboard1.py 의 소규모 하위 클래스 14개를 단 한 블록의 코드로 클래스를 모두 생성했다.
itertools.chain()
: 하나 이상의iterable
을 받아 전체를 순회할 수 있는 단일iterable
을 반환- 여기서는 두 개의
iterable
을 전달- 검은색과 하얀색 체커 말의 유니코드 포인트에 대한 2-튜플
- 검은색과 하얀색 체스 말에 대한 range 객체
- 각 유니코드 포인트에 대해 한 글자로 된 문자열을 생성해 이 문자의 유니코드 명칭에 따라 클래스 이름을 생성 (“black chess knight”는
BlackChessKnight
가 된다) exec()
을 사용- 너무 위험함
gameboard3.py
- gameboard2.py 의 populate_board() 메서드와는 다르게, 잘못 입력하기 쉬운 문자열 리터럴 대신 상수 값을 사용해 말과 색깔을 지정한다.
- 각 말을 생성하기 위해 새로운
creae_piece()
팩터리를 활용
- 위는
ChessBoard
와CheckersBoard
가 상속 할 추상 클래스이다- 두 클래스는
create_piece()
를 상속한다.create_piece()
는 ~Board 클래스의__classForPiece
dict를 참조하여 kind, color를 받아 말 이름을 반환한다.\- gameboard2.py 와 다르게
exec()
이나eval()
을 사용하지 않고 안전하게 동적으로 생성할 수 있다.
- 두 클래스는
- gameboard2.py 와 다르게
eval()
이나exec()
을 활용하는 대신 안전한 방법 사용- 일단
code
에서 글자와 그에 대한 명칭을 가지고 있다면maek_new_method()
메서드를 호출해 해로운 함수 (new()
)를 생성 - 내장함수인
type()
을 활용해 새로운 클래스를 생성type()
을 이용해 클래스를 생성하려면- 클래스 타입
- 기반 클래스들에 대한 튜플 (Piece 하나만 넘김)
- 클래스 애트리뷰트에 대한 딕셔너리
- 방금 생성한
new()
함수를__new__
메서드로 설정
- 현재 모듈(
sys.modules[__name__]
)에 새로 생성한 클래스를 name이라는 애트리뷰트로 추가하기 위해setattr()
를 호출
- 일단
make_new_method()
는new()
함수를 생성super()
는 활용 할 수 없음, 왜냐면new()
가 생성될 시기에는super()
가 접근할 클래스가 아직 없기 때문 ***gameboard4.py
- gameboard3.py 에서
setattr()
를 사용한 것과 달리, 전역변수가 저장된 딕셔너리의 참조를 얻어, name에 있는 값이 key이고 새로 생성한 Class가 값인 새 아이템을 추가 ->setattr()
와 똑같이 동작 new()
메서드 생성의 경우에도,lambda
를 활용하여 해결create_piece()
가 gameboard4.py 의 팩터리 메서드이다.- 사용되는 상수값은 gameboard3.py 와 같지만, 클래스 객체의 dict 대신 내장 함수인
globals()
를 통해 얻은 dict에서 해당 클래스를 동적으로 찾는다. - 이렇게 얻은 클래스 객체를 바로 호출해서 원하는 말 인스턴스를 얻는다.
- 사용되는 상수값은 gameboard3.py 와 같지만, 클래스 객체의 dict 대신 내장 함수인