5장 팩토리 패턴
품질이란 아무도 보고 있지 않을 때 올바르게 하는 것을 의미합니다.
헨리 포드
장에서 삼, 자신만의 게임을 만드는 것에 대해 생각하기 시작했습니다. 텍스트 전용 게임에 속지 않으려면 잠시 시간을 내어 무언가를 그릴 수 있는 방법을 살펴보겠습니다.
화면에. 이 장에서는 파이썬으로 그래픽의 기초를 다룰 것입니다. 우리는 선택한 무기로 PyGame 패키지를 사용할 것입니다. 우리는 팩토리 클래스를 만들 것입니다. 팩토리 클래스는 특정 매개변수 세트를 사용하는 객체를 정의하고 이를 사용하여 다른 클래스의 객체를 생성합니다. 또한 이러한 팩토리 클래스를 생성하기 위한 템플릿 역할을 할 추상 팩토리 클래스를 정의합니다.
가상 환경에서 다음 명령을 사용하여 pip를 사용하여 PyGame을 설치할 수 있습니다. pip install pygame
이것은 상당히 고통스럽지 않아야 합니다. 이제 작업할 실제 창을 가져오려면:
graphic_base.py 파이게임 가져오기
파이게임.초기화()
화면 = pygame.display.set_mode((800, 600))
graphic_base.py를 저장하고 다음 파일을 실행합니다: python graphic_base.py
너비 400픽셀, 높이 300픽셀의 빈 창이 화면에 나타났다가 즉시 사라집니다. 축하합니다. 첫 번째 항목을 만들었습니다.
61
베셀-바덴호르스트 2017
여 바덴호르스트, 실용적인 파이썬 디자인 패턴시간ttps://doi.org/10.1007/978-1-4842-2680-3_4
5장 공장 패턴
화면. 물론 우리는 화면이 조금 더 오래 활성 상태를 유지하기를 원하므로 graphic_base.py에 절전 기능을 추가해 보겠습니다.
graphic_base.py
파이게임 가져오기
시간 수입 수면에서
파이게임.초기화()
화면 = pygame.display.set_mode((800, 600))
수면(10)
time 패키지(표준 라이브러리의 일부)에서 지정된 시간(초) 동안 실행을 일시 중단하는 sleep 함수를 가져옵니다. 스크립트가 실행을 완료하고 창이 사라지기 전에 창을 10초 동안 열어 두는 스크립트 끝에 10초 절전 상태를 추가했습니다.
처음으로 화면에 창을 만들었을 때 매우 신이 났지만, 룸메이트에게 내 창을 보여주기 위해 전화를 걸었을 때 그는 깜짝 놀랐습니다. 새 창작물을 보여주기 전에 창에 무언가를 추가하는 것이 좋습니다. 창에 사각형을 추가하려면 graphic_base.py를 확장합니다.
graphic_base.py
파이게임 가져오기 시간 가져오기
파이게임.초기화()
화면 = pygame.display.set_mode((800.600))
pygame.draw.rect(화면, (255, 0, 34), pygame.Rect(42, 15, 40, 32)) pygame.display.flip()
시간 수면(10)
pygame.draw.rect 함수는 가리키는 화면 변수에 사각형을 그립니다.
당신의 창에. 두 번째 매개변수는 모양을 채우는 데 사용되는 색상을 포함하는 튜플이며 마지막으로 파이게임 사각형은 왼쪽 상단과 오른쪽 하단 모서리의 좌표와 함께 전달됩니다. 색상 튜플에서 볼 수 있는 세 가지 값은 RGB 값(빨간색, 녹색, 파란색)으로 알려진 값을 구성하며 각 구성 요소는 최종 색상 혼합에서 구성 요소 색상의 강도를 나타내는 255 값입니다.
pygame.display.flip()을 생략하면 모양이 표시되지 않습니다. 이는 PyGame이 메모리 버퍼에 화면을 그린 다음 전체 이미지를 활성 화면으로 회전하기 때문입니다. 디스플레이를 업데이트할 때마다 화면의 변경 사항을 보려면 pygame.display.flip()을 호출해야 합니다.
화면에 다양한 색상으로 다양한 사각형을 그려보십시오.
게임 프로그래밍에서 가장 기본적인 개념은 게임 루프. 아이디어는 다음과 같이 작동합니다. 게임은 게임 상태를 업데이트하기 위해 몇 가지 계산을 수행하는 사용자의 입력을 찾은 다음 플레이어에게 피드백을 제공합니다. 우리의 경우 플레이어가 화면에서 보는 것만 업데이트하지만 오디오 또는 햅틱 피드백을 추가할 수 있습니다. 이것은 플레이어가 멈출 때까지 계속 발생합니다. 화면이 새로고침될 때마다 pygame.display.flip() 함수를 실행하여 새로고침된 디스플레이를 표시합니다.
플레이어.
게임의 기본 구조는 다음과 같습니다.
다음과 같은 일부 초기화가 발생합니다. 나. 윈도우 설정 및 초기화
화면에서 항목의 위치 및 색상.
사용자가 게임을 종료하지 않는 동안 게임 루프를 실행합니다. 사용자가 종료하면 창을 종료합니다.
코드에서 다음과 같이 보일 수 있습니다.
graphic_base.py 파이게임 가져오기
window_dimensions = 800, 600
화면 = pygame.display.set_mode(window_dimensions)
player_quits = 거짓
player_quits가 아닌 동안:
pygame.event.get()의 이벤트에 대해: if event.type == pygame.QUIT:
player_quits = 참
pygame.display.flip()
63
챕터 팩토리 패턴
현재 이 코드는 아무것도 하지 않고 플레이어가 창에서 닫기 버튼을 클릭한 다음 실행을 마칠 때까지 기다립니다. 보다 상호 작용이 가능하도록 작은 사각형을 추가하고 사용자가 화살표 키 중 하나를 누를 때 이동합니다.
이렇게 하려면 화살표 키 이벤트에 응답할 준비가 된 화면의 초기 위치에 사각형을 그려야 합니다.
shape_game.py(ch04_03.py 참조) 파이게임 가져오기
window_dimensions = 800, 600
화면 = pygame.display.set_mode(window_dimensions)
x = 100 y = 100
player_quits = 거짓
player_quits가 아닌 동안:
pygame.event.get()의 이벤트에 대해: if event.type == pygame.QUIT:
player_quits = 참
눌림 = pygame.key.get_pressed() 눌리면 (pygame.K_UP): y -= 4
눌리면 (pygame.K_DOWN): y += 4 눌리면 (pygame.K_LEFT): x -= 4
누를 때(pygame.K_RIGHT): x += 4
screen.fill((0, 0, 0))
pygame.draw.rect(화면, (255, 255, 0), pygame.Rect(x, y, 20, 20))
pygame.display.flip()
코드를 약간 가지고 놀면서 블록이 창 경계를 넘어 이동하지 않도록 할 수 있는지 확인하십시오.
이제 블록이 화면 주위를 이동할 수 있으므로 원에 대해 이 모든 작업을 수행하는 것은 어떻습니까? 그런 다음 삼각형을 위해. 이제 게임 캐릭터 아이콘입니다. . . 당신은 그림을 얻습니다. 갑자기 많은 디스플레이 코드가 게임 루프를 막고 있습니다. 상황에 대한 객체 지향 접근 방식을 사용하여 이를 약간 정리하면 어떨까요?
64
5장 공장 패턴
shape_game.py 파이게임 가져오기
클래스 모양(객체):
def __init__(self, x, y):
x = x
y = y
데프 풀(셀프):
NotImplementedError() 증가
def move(자신, 방향):
if direction == ‘up’: self.y -= 4
elif 방향 == ‘아래로’:
자기.y += 4
elif 방향 == ‘왼쪽’:
자기.x -= 4
elif 방향 == ‘오른쪽’:
x += 4
클래스 스퀘어(모양):
데프 풀(셀프):
pygame.draw.rect(
화면,
(255, 255, 0), pygame.Rect(self.x, self.y, 20, 20)
)
학급 원(모양):
def draw(self): pygame.draw.circle(
화면,
(0, 255, 255), (selfx, self.y), 10
)
65
5장 공장 패턴
__name__ == ‘__main__’인 경우:
window_dimensions = 800, 600
화면 = pygame.display.set_mode(window_dimensions)
정사각형 = 정사각형(100, 100) obj = 정사각형
player_quits = 거짓
player_quits가 아닌 동안:
pygame.event.get()의 이벤트에 대해: if event.type == pygame.QUIT:
player_quits = 참
눌림 = pygame.key.get_pressed()
누를 때 (pygame.K_UP): obj.move(‘up’)
아래인 경우(pygame.K_DOWN): obj.move(‘아래’) 아래인 경우(pygame.K_LEFT): obj.move(‘left’) 아래인 경우(pygame.K_RIGHT): obj.move(‘right’)
screen.fill((0, 0, 0)) obj.draw()
pygame.display.flip()
이제 원형 및 정사각형 개체를 사용할 수 있으므로 C 키를 누를 때(현재 키가 아닌 경우) 화면의 개체가 원으로 바뀌도록 프로그램을 수정하는 방법을 고려하십시오. 마찬가지로 S 키를 누르면 모양이 사각형으로 변경됩니다. 게임 루프 내에서 이 모든 것을 처리하는 것보다 개체를 사용하는 것이 얼마나 쉬운지 확인하십시오.
팁 이 장의 코드와 함께 PyGame의 키보드 단축키를 확인하십시오.
다음과 같이 구현할 수 있는 개선 사항이 한두 가지 있습니다. B. 어떤 모양을 다루고 있는지 추적할 필요가 없도록 각 클래스에서 수행해야 하는 이동 및 그리기와 같은 작업을 추상화합니다. 우리는 일반적으로 모양을 참조할 수 있기를 원하고 그것이 어떤 모양인지(또는 이미지나 애니메이션 프레임이 아닌 시작하는 모양인지)에 대해 걱정하지 않고 스스로 그리라고 지시할 수 있기를 원합니다.
물론 다형성은 우리가 새 객체를 생성할 때마다 코드를 업데이트해야 하고 큰 게임에서는 여러 곳에서 발생하기 때문에 완전한 솔루션은 아닙니다. 문제는 새 유형을 만들고 해당 유형을 사용하지 않는 것입니다.
더 나은 코드를 작성하고 싶기 때문에 Shapeshifter 게임에 추가하려는 확장을 처리하는 더 나은 방법을 찾는 동안 좋은 코드의 다음 특성을 고려하십시오.
좋은 코드는
유지하기 쉬운
업데이트하기 쉽고,
쉽게 확장 가능
달성하려는 것이 무엇인지 명확합니다.
좋은 코드는 몇 주 전에 작성한 작업을 가능한 한 힘들이지 않게 만들어야 합니다. 마지막으로 원하는 것은 두려워하는 코드 영역을 만드는 것입니다.
우리는 생성 코드를 시스템 전체에 배포하는 대신 공통 인터페이스를 사용하여 객체를 생성할 수 있기를 원합니다. 생성할 수 있는 도형 유형을 업데이트할 때 변경해야 하는 코드를 지역화합니다. 새로운 유형을 추가하는 것은 시스템의 확장 가능성이 가장 높기 때문에 코드 개선 측면에서 가장 먼저 살펴봐야 할 영역 중 하나라는 것은 당연합니다.
중앙 집중식 개체 생성 시스템을 만드는 한 가지 방법은 팩터리 패턴을 사용하는 것입니다. 이 패턴에는 두 가지 고유한 접근 방식이 있으며 간단한 팩터리 방식부터 시작하여 추상 팩터리 구현으로 이동하여 두 가지 접근 방식을 모두 다룰 것입니다. 또한 게임 스켈레톤과 관련하여 이를 구현하는 방법도 살펴보겠습니다.
팩토리 패턴에 들어가기 전에 프로토타입 패턴과 팩토리 패턴 사이에 한 가지 중요한 차이점이 있다는 사실에 주목하고 싶습니다. 프로토타입 패턴은 하위 분류가 필요하지 않지만 초기화 작업이 필요한 반면, 팩토리 패턴은 하위 분류가 필요하지만 초기화는 필요하지 않습니다. 이들 각각에는 고유한 장점과 선택할 수 있는 위치가 있으며 이 장이 진행됨에 따라 이 구분이 더 명확해집니다.
67
챕터 팩토리 패턴
문자열을 전달하고 반환 값으로 받는 방식으로 메서드를 호출하려는 경우
ㅏ 새 개체우리는 본질적으로 공장 방법. 객체의 유형은 메서드에 전달된 문자열에 의해 결정됩니다.
이렇게 하면 소프트웨어에 기능을 추가하여 작성하는 코드를 쉽게 확장할 수 있습니다. 새 클래스를 추가하고 새 문자열을 수락하고 생성한 클래스를 반환하도록 팩토리 메서드를 확장하면 됩니다.
팩토리 메소드의 간단한 구현을 살펴보겠습니다.
shape_factory.py 파이게임 가져오기
클래스 모양(객체):
def __init__(self, x, y): self.x = x
y = y
데프 풀(셀프):
NotImplementedError() 증가
def move(자신, 방향):
if direction == ‘up’: self.y -= 4
elif 방향 == ‘아래로’:
자기.y += 4
elif 방향 == ‘왼쪽’:
자기.x -= 4
elif 방향 == ‘오른쪽’:
x += 4
@정적 방법
데프 팩토리(유형):
유형 == “원”인 경우:
type == “Square”인 경우 Circle(100, 100)을 반환합니다.
리턴 스퀘어 (100, 100)
68
챕터 팩토리 패턴
ater 0, “잘못된 양식 요청: ” + 유형
클래스 스퀘어(모양):
데프 풀(셀프):
pygame.draw.rect(
화면,
(255, 255, 0), pygame.Rect(self.x, self.y, 20, 20)
)
학급 원(모양):
def draw(self): pygame.draw.circle(
화면,
(0, 255, 255), (selfx, self.y), 10
)
__name__ == ‘__main__’인 경우:
window_dimensions = 800, 600
화면 = pygame.display.set_mode(window_dimensions)
obj = Shape.factory(“정사각형”) player_quits = 잘못됨
player_quits가 아닌 동안:
pygame.event.get()의 이벤트에 대해: if event.type == pygame.QUIT:
player_quits = 참
눌림 = pygame.key.get_pressed()
누를 때 (pygame.K_UP): obj.move(‘up’)
아래인 경우(pygame.K_DOWN): obj.move(‘아래’) 아래인 경우(pygame.K_LEFT): obj.move(‘left’) 아래인 경우(pygame.K_RIGHT): obj.move(‘right’)
69
챕터 팩토리 패턴
screen.fill((0, 0, 0)) obj.draw()
pygame.display.flip()
사각형에서 원 또는 다른 원하는 모양이나 이미지로 이동하기 위해 위의 코드 스니펫을 조정하는 것이 얼마나 더 쉬울까요?
팩토리 메서드의 일부 지지자들은 모든 생성자가 비공개이거나 보호되어야 한다고 제안합니다. 새 객체가 생성되는지 이전 객체가 재사용되는지 여부는 클래스 사용자에게 중요하지 않기 때문입니다. 아이디어는 개체의 요구 사항을 생성에서 분리한다는 것입니다.
이 아이디어를 신조로 따라해서는 안 되지만 자신의 프로젝트에서 시도해보고 어떤 이점을 얻을 수 있는지 확인하십시오. 팩토리 메소드와 팩토리 패턴 사용에 익숙해지면 판단력을 사용하여 당면한 프로젝트에서 패턴의 유용성을 결정할 수 있습니다.
게임에 새 클래스를 추가하여 화면에 그릴 때마다 factory() 메서드를 변경하기만 하면 됩니다.
다른 유형의 공장이 필요한 경우 어떻게 해야 합니까? 플레이어 캐릭터 팩토리에 비해 음향 효과 팩토리나 환경 요소를 추가하고 싶으신가요? 동일한 기본 팩토리에서 다양한 종류의 팩토리 하위 클래스를 만들 수 있기를 원합니다.
전체 팩토리 컬렉션에 액세스하기 위한 단일 인터페이스를 생성하려는 경우 추상 팩토리를 안전하게 사용할 수 있습니다. 컬렉션의 각 추상 팩터리는 미리 정의된 인터페이스를 구현해야 하며 해당 인터페이스의 각 함수는 팩터리 메서드 패턴에 따라 다른 추상 형식을 반환합니다.
가져오기 ABC
클래스 AbstractFactory(객체): __metaclass__ = abc.ABCMeta
@abc.abstractmethod def make_object(self):
돌려 주다
70
챕터 팩토리 패턴
class CircleFactory(AbstractFactory): def make_object(self):
- 뭔가를
리턴 서클()
클래스 SquareFactory(AbstractFactory): def make_object(self):
- 뭔가를
리턴 스퀘어()
여기에서는 기본 제공 추상 클래스를 정의할 수 있는 내장 abc 모듈을 사용했습니다. 추상 팩토리는 구체적인 팩토리를 정의하기 위한 청사진을 정의한 다음 이 예에서 원과 사각형을 만듭니다.
Python은 동적으로 유형이 지정되므로 공통 기본 클래스를 정의할 필요가 없습니다. 코드를 더 Pythonic하게 만들면 다음과 같이 보일 것입니다.
class CircleFactory(AbstractFactory): def make_object(self):
- 뭔가를
리턴 서클()
클래스 SquareFactory(AbstractFactory): def make_object(self):
- 뭔가를
리턴 스퀘어()
def draw_function(팩토리):
드로어블 = factory.make_object() 드로어블.드로()
def prepare_client():
squareFactory = SquareFactory() draw_function(squareFactory)
CircleFactory = CircleFactory() draw_function(CircleFactory)
71
챕터 팩토리 패턴
지금까지 설정한 베어본 게임에서 팩토리가 게임 루프에서 실행할 수 있는 play() 메서드를 포함하는 개체를 생성하여 소리를 재생하고 동작을 계산하거나 그림에 그릴 모양과 이미지를 생성한다고 상상할 수 있습니다. 화면. 추상 팩토리의 또 다른 일반적인 용도는 다양한 운영 체제의 GUI 요소에 대한 팩토리를 생성하는 것입니다. 모두 동일한 기본 기능을 사용하지만 구축되는 공장은 프로그램이 실행되는 운영 체제에 따라 선택됩니다.
축하해요! 훌륭한 코드를 작성하는 능력이 향상되었습니다. 추상 팩토리를 사용하면 변경하기 쉬운 코드와 테스트하기 쉬운 코드를 작성할 수 있습니다.
당신이 소프트웨어를 개발할 때, 당신은 잔소리로부터 자신을 보호하고 싶습니다.
미래의 모든 상황을 수용할 수 있는 무언가를 구축해야 합니다. 소프트웨어의 미래에 대해 생각하는 것은 좋은 일이지만 미래의 가능한 모든 요구 사항을 충족할 수 있을 만큼 충분히 일반적인 소프트웨어를 개발하는 것은 종종 무의미합니다. 가장 단순한 이유는 아무 것도 상상할 방법이 없기 때문입니다.
당신의 소프트웨어가 갈 곳. 나는 당신이 순진해야 하고 코드의 즉각적인 미래를 고려하지 말아야 한다는 말은 아니지만 새로운 기능을 통합하기 위해 코드를 발전시킬 수 있다는 것은 소프트웨어 개발자로서 배울 수 있는 가장 가치 있는 기술 중 하나입니다. 이전에 작성한 모든 이전 코드를 버리고 올바른 코드를 작성하기 위해 다시 시작하려는 유혹이 항상 있습니다. 이것은 꿈이며 일단 제대로 이해하면 코드가 보기에 좋지 않게 만드는 새로운 것을 배우게 되며 아무 것도 완료하지 않고 다시 반복해야 합니다.
일반적인 원칙은 YAGNI입니다. 소프트웨어 개발 경력에서 이 약어를 접하게 될 것입니다. 그게 무슨 뜻이야? 당신은 그것을 필요하지 않습니다! 현재의 문제를 잘 해결하는 코드를 작성하고 다음 문제를 해결하기 위해 필요한 경우에만 변경하는 것이 원칙입니다.
이 때문에 많은 소프트웨어 디자인은 초기에 더 간단한 팩토리 방법을 사용하고 개발자가 더 많은 유연성이 필요한 경우에만 추상 팩토리, 프로토타입 또는 빌더 패턴을 사용하도록 프로그램을 발전시킵니다.
72
챕터 팩토리 패턴
원, 삼각형, 사각형 간 전환 기능 추가
버튼을 누르면.
단순한 모양 대신 이미지 개체를 구현해 보세요.
위, 아래, 왼쪽 또는 오른쪽으로 이동할 때 그려지는 별도의 이미지 간에 전환하려면 이미지 개체 클래스를 확장하십시오.
챌린지의 경우 이미지를 이동할 때 이미지에 애니메이션을 추가해 보세요.