나는 스튜어트 시에라 (Stuart Sierra)의 " 씽킹 인 데이터 (Thinking In Data) " 연설을 보고이 게임의 아이디어 중 하나를 내가 만드는이 게임의 디자인 원칙으로 채택했다. 차이점은 그가 Clojure에서 일하고 있고 JavaScript에서 일하고 있다는 것입니다. 나는 우리 언어들 사이에 몇 가지 큰 차이점이 있음을 알았습니다
- Clojure는 관용적으로 기능적인 프로그래밍입니다
- 대부분의 상태는 불변입니다
나는 "모든 것이지도"라는 슬라이드에서 아이디어를 얻었습니다 (11 분, 6 초에서> 29 분). 그가 말한 것들은 :
- 2-3 개의 인수를 사용하는 함수가 표시 될 때마다이를 맵으로 바꾸고 맵을 전달하는 경우를 만들 수 있습니다. 여기에는 많은 장점이 있습니다.
- 인수 순서에 대해 걱정할 필요가 없습니다.
- 추가 정보에 대해 걱정할 필요가 없습니다. 여분의 키가 있다면 그것은 실제로 우리의 관심사가 아닙니다. 그들은 그냥 흐르고 방해하지 않습니다.
- 스키마를 정의 할 필요가 없습니다
- 객체를 전달하는 것과는 달리 데이터 숨기기가 없습니다. 그러나 그는 데이터 숨기기가 문제를 일으킬 수 있으며 과대 평가되었다고 주장합니다.
- 공연
- 간편한 구현
- 네트워크 또는 프로세스를 통해 통신하자마자 양측이 데이터 표현에 동의해야합니다. 데이터 작업 만하면 건너 뛸 수있는 추가 작업입니다.
내 질문과 가장 관련이 있습니다. "함수를 구성 할 수있게하라" 는 29 분입니다 . 다음은 개념을 설명하기 위해 사용하는 코드 샘플입니다.
;; Bad (defn complex-process [] (let [a (get-component @global-state) b (subprocess-one a) c (subprocess-two a b) d (subprocess-three a b c)] (reset! global-state d))) ;; Good (defn complex-process [state] (-> state subprocess-one subprocess-two subprocess-three))
필자는 대다수의 프로그래머가 Clojure에 익숙하지 않다는 것을 알고 있으므로이를 필수 스타일로 다시 작성하겠습니다.
;; Good def complex-process(State state) state = subprocess-one(state) state = subprocess-two(state) state = subprocess-three(state) return state
장점은 다음과 같습니다.
- 손쉬운 테스트
- 이러한 기능을 분리하여 쉽게 볼 수 있습니다.
- 한 줄을 주석 처리하고 단일 단계를 제거하여 결과가 무엇인지 쉽게 확인할 수 있습니다.
- 각 하위 프로세스는 상태에 대한 추가 정보를 추가 할 수 있습니다. 하위 프로세스 1이 하위 프로세스 3과 통신해야하는 경우 키 / 값을 추가하는 것만 큼 간단합니다.
- 데이터를 다시 저장할 수 있도록 상태에서 필요한 데이터를 추출 할 상용구가 없습니다. 전체 상태를 전달하고 하위 프로세스가 필요한 것을 할당하도록하십시오.
이제 제 상황으로 돌아가서 : 저는이 교훈을 게임에 적용했습니다. 즉, 거의 모든 고급 기능이 gameState
객체를 가져오고 반환 합니다. 이 개체는 게임의 모든 데이터를 포함합니다. EG : badGuys 목록, 메뉴 목록, 지상 전리품 등입니다. 다음은 업데이트 기능의 예입니다.
update(gameState)
...
gameState = handleUnitCollision(gameState)
...
gameState = handleLoot(gameState)
...
내가 여기서 물어볼 것은 기능적 프로그래밍 언어에서만 실용적 아이디어를 왜곡 한 가증을 일으켰 는가? JavaScript는 관용적으로 기능 하지 않으며 (그렇지만 작성할 수는 있지만) 불변의 데이터 구조를 작성하는 것은 정말 어려운 일입니다. 저와 관련된 한 가지 사항은 각 하위 프로세스가 순수 하다고 가정 한다는 것입니다. 왜 그런 가정을해야합니까? 내 함수 중 일부가 순수한 것은 드문 일입니다 (따라서, 나는 종종을 수정한다는 것을 의미 gameState
합니다. 불변의 데이터가 없다면 이러한 아이디어가 분리됩니까?
언젠가 깨어나서이 전체 디자인이 가짜임을 깨달았으며 실제로 Big Ball Of Mud 안티 패턴을 구현하고 있습니다.
솔직히, 나는이 코드를 몇 달 동안 노력해 왔으며 훌륭했습니다. 나는 그가 주장한 모든 이점을 얻고있는 것 같습니다. 내 코드는 매우 쉽게 나를 위해 추론 할 수 있습니다. 그러나 나는 한 사람 팀이므로 지식의 저주를 가지고 있습니다.
최신 정보
이 패턴으로 6 개월 이상 코딩했습니다. 보통이 시점까지 나는 내가 한 일을 잊어 버렸습니다. 그리고 그곳에서 "내가 이것을 깨끗하게 작성 했습니까?" 놀이에 온다. 내가하지 않으면 정말 힘들 것입니다. 지금까지 나는 전혀 고투하지 않습니다.
유지 보수성을 검증하기 위해 또 다른 눈 세트가 필요한 방법을 이해합니다. 내가 말할 수있는 것은 무엇보다도 유지 보수성에 관심이 있다는 것입니다. 나는 어디에서 일하든 항상 깨끗한 코드를위한 가장 큰 전도자입니다.
이 코딩 방법으로 이미 개인적인 경험이 나쁜 사람들에게 직접 답장하고 싶습니다. 그때는 몰랐지만 코드를 작성하는 두 가지 방법에 대해 이야기하고 있다고 생각합니다. 내가 한 방식은 다른 사람들이 경험 한 것보다 더 구조화 된 것 같습니다. 누군가가 "모든 것이지도"라는 개인적인 경험이 없으면 다음과 같은 이유로 유지 관리가 얼마나 어려운지 이야기합니다.
- 함수가 요구하는 맵의 구조를 절대 알 수 없습니다
- 모든 함수는 예상치 못한 방식으로 입력을 변경할 수 있습니다. 특정 키가 맵에 들어간 방법 또는 사라진 이유를 찾으려면 코드베이스 전체를 살펴 봐야합니다.
그러한 경험을 가진 사람들에게 아마도 코드베이스는 "모든 것이 N 유형의 맵 중 1 개를 필요로합니다." 내 것은 "모두 1 개 유형의지도 중 1 개가 필요합니다"입니다. 그 1 가지 유형의 구조를 안다면 모든 것의 구조를 알고 있습니다. 물론, 그 구조는 보통 시간이 지남에 따라 커집니다. 그래서 ...
참조 구현 (예 : 스키마)을 찾을 수있는 곳이 하나 있습니다. 이 참조 구현은 게임이 사용하는 코드이므로 오래되지 않습니다.
두 번째 요점은 참조 구현 외부의 맵에 키를 추가 / 제거하지 않고 이미 존재하는 것을 변경하는 것입니다. 또한 대규모 자동 테스트 제품군이 있습니다.
이 아키텍처가 결국 자체 무게로 무너지면 두 번째 업데이트를 추가하겠습니다. 그렇지 않으면 모든 것이 잘되고 있다고 가정하십시오. :)