전에 카페에 기재한 내용인데, 확실히 유용하여 다시 적어봅니다.
원문: https://cafe.naver.com/crazygm/227601
이 코드 리뷰 방식은 저 ENRU가 고안했습니다.
만약 여러분이 아주 맛있기로 유명한 고속터미널의 스파게티 집에서 스파게티를 하나 사들고 와서
집에서 까는 순간 식탁에서 떨어져 바닥에 와장창 엎어졌다고 합시다.

배고프고 속도 상하겠지만 일단 바닥 청소부터 시작해야겠죠.
이 때, 여러분 같으면 어떻게 치우시겠습니까?
아마..

네. 면부터 일단 건져서 담고, 스파게티 소스는 행주로 닦겠죠.
비유가 잘 들어맞는 지는 모르겠지만 오래된 스파게티 코드를 정리하는 법도 그렇습니다.
여기서 면은 코드의 구조 및 큰 개요, 소스는 잔 가지라고 보시면 됩니다.
어쩔 수 없이 이 코드를 재 사용해야하는데, 지금 문제들이 이리저리 얽혀있어서 판단하기 어려운 경우:
그게 제 상황이었기 때문에, 마냥 그대로 통으로 받아 적는 것보다 훨씬 빠르고 좋은 방법은 없을까?하고
일단 고안을 한 번 해보았습니다.
아시다시피 그대로 적으면서 이해가 잘 되시는 부분도 있습니다. 하지만 코드 하이라이터별 차이라던가,
또 불편한 점도 확실히 존재하기 때문에 아무리 노트패드같은 좋은 프로그램을 사용한다 하더라도
시간이 오래 걸릴 수밖에 없는 것은 사실입니다. 우리가 단순히 마인드맵처럼 오브젝트의 관계를 파악하려고 할 때도,
오브젝트 분석 하나하나에 시간이 너무 오래 소요가 됩니다. 특히 겜스는 오브젝트별로 이벤트가 있고, 그 이벤트 안에
각종 코드들이 담겨있기 때문에 오브젝트별 연결관계를 파악하려다가 전체 코드를 모두 리뷰해야하는 경우도
생깁니다. .
이렇게 모든 코드를 받아서 쓰는 것보다 필요한 것에 집중하는 경우가 때로 필요할 것입니다.
물론 그것이 코드적으로 정확하다거나 아름답다거나 과학적이라거나 그런 것은 아니지만,
개인 차원의 메모 방식이라는 게 분명히 필요합니다. 그것이 필요하다면, 그 효율성에 대한 제고도 필요합니다.
일단 제가 현재 떠올려본 방식은 이러합니다.
◆ 빠른 rearrange / 구조화 피드백 분석 을 위한 코드 정리의 원칙
1. 구절로 나눈다.
ex)
qsi_find() 펑션 -> qsi의 취지 파악
- qsi 초기화
- for문
- flip이 hor인 경우 ->
- flip이 vert인 경우 ->
2. 모두 일일히, 세세히 적는 것이 아니고
단계별로 요약한다.
ex)
- qsi = noone; (x)
- if !instance_exists(magic_mouse) exit; (x)
이렇게 모든 정보를 담으려고 노력하며 세세히 적지 않는다. 이것은 요약/구조화가 아닌 복사 붙여넣기.
- qsi 초기화. (o)
- 인스턴스 존재 확인
-demiderus x enryu
이렇게 하는 목적은 전체적인 구조를 보는데 집중하는 것입니다.
이를 통해 '구조적' 문제를 빨리 파악하고 캐치하는 데에 주안점이 있습니다.
이렇게 요약하면 오브젝트별 마인드맵을 구성하기도 보다 용이할 것입니다.
애초에, 제가 예전에 만들어둔 코드를 다시 꺼내서 보다보니, 그러한 스파게티 코드나 에러를 깔끔히 정리하고 재구성하기 위해 고안되었습니다.
정리 원칙을 적용한 예시는 이와 같습니다.
가령 원래의 스파게티 코드가 다음과 같다고 합시다.
hold = false;
hold_target=noone;
hold_type=noone;
depth=-100
inbox_qs = false;
inbox_inv = false;
inbox_arm = false;
qsi = noone;
function qsi_find()
{
qsi = noone;
if !instance_exists(QuickSlot) exit;
for (var i=0; i<12; i++)
{
if QuickSlot.flip="hor"
{
if QuickSlot.text_dir="u" { if device_mouse_decentralized(QuickSlot.x+QuickSlot.slot_x[i], QuickSlot.y+QuickSlot.slot_y[0], 48, 48) { qsi = i; } }
if QuickSlot.text_dir="d" { if device_mouse_decentralized(QuickSlot.x+QuickSlot.slot_x[i], QuickSlot.y+QuickSlot.slot_y[1], 48, 48) { qsi = i; } }
}
if QuickSlot.flip="vert"
{
if QuickSlot.text_dir="l"{ if device_mouse_decentralized(QuickSlot.x+QuickSlot.slot_y[0], QuickSlot.y+QuickSlot.slot_x[i], 48, 48){ qsi = i; }}
if QuickSlot.text_dir="r"{ if device_mouse_decentralized(QuickSlot.x+QuickSlot.slot_y[1], QuickSlot.y+QuickSlot.slot_x[i], 48, 48){ qsi = i; }}
}
}
return qsi;
}
function destroy_duplicant()
{
for (var i=0; i<12; i++)
{
var checking_slot = QuickSlot.my_slot[i];
if checking_slot!=noone && is_struct(checking_slot)
{
if checking_slot.title == hold_target.title
{
QuickSlot.my_slot[i]=noone;
}
}
}
}
function rearrange()
{
var _original = QuickSlot.my_slot[qsi];
var _original_type = QuickSlot.slot_type[qsi];
QuickSlot.my_slot[qsi] = hold_target;
QuickSlot.slot_type[qsi] = hold_type;
if hold_type != _original_type { hold_target=noone; }
else { hold_target = _original; hold_type = _original_type;}
}
이걸 정리를 해보자면,
{ object: magic_mouse }
<Create>
· Hold 관련 변수 초기화
· 뎁스 초기화
· inbox 관련변수 초기화
· qsi 초기화
· qsi_find() 펑션 -> qsi 위치 파악
ㅣ · qsi 초기화
ㅣ · for 문 (0~12)
ㅣ ㅣ· Quick Slot의 flip이 hor인 경우
ㅣ ㅣ· " 의 text_dir이 u인 경우 -> 마우스 위치 체크-> qsi 재설정
ㅣ ㅣ · " 의 text_dir이 d인 경우 -> " -> "
ㅣ ㅣ· Quick Slot의 flip이 vert인 경우
ㅣ ㅣ· " 의 text_dir이 l인 경우 -> " -> "
ㅣ ㅣ· " 의 text_dir이 r인 경우 -> " -> "
ㅣ · qsi 반환
· destroy_duplicant() 펑션 -> 복제품 삭제
ㅣ · for 문 (0~12)
ㅣ ㅣ· checking_slot 초기화
ㅣ ㅣ· checking_slot이 name이고 구조체 일 경우
ㅣ ㅣ· checking_slot의 title이 hold 타겟의 title인 경우 -> Quick_slot의 my_slot[i] 초기화
· rearrange() 펑션 -> 재구성
ㅣ · original 관련 지역변수 초기화 (Quick_Slot에서 받아옴)
ㅣ · Quick_Slot의 my_slot[qsi]와 slot_type[qsi]를 hold 변수 값으로 바꾼다.
ㅣ · 만약 hold_type이 _original_type이 아닌 경우 -> hold_target 초기화
ㅣ · 인 경우 -> hold_target, hold_type을 _original 변수 값으로 바꾼다.
분석 총평:
· 문제점 : 모두 Quick_Slot 위주로 설게되어 UI 전체를 묶는 본래의 역할을 못함
· 해결책 : function에 object를 인수로 받고 그 설정별 기능을 달리 하자, 혹은 inv에서 skill_UI에서 처럼 구조체를 그냥 넘겨주되
rearrange() 펑션과 destroy_duplicant() 펑션에 대한 수정을 가하자.
여기에 덧붙여진 원칙들이 추가가 있는데, 말하자면:
1.
변수 재설정시
가령 a라는 변수라고 하면
a 재설정 이라고만 적는 게 원칙이지만,
break; 문이나 return의 바로 윗 줄 같은 경우 최종적인 변환을 의미하기도 하므로 표시해도 좋습니다.
2. 스크립트나 펑션이 들어가는 주홍 글씨면 표시해주어도 좋습니다.
이해가 더 쉬워지는 경우도 있기 때문입니다.
3. 오브젝트 생성시 겜스에서 자체로 설정이 되는 자체 내장변수의 경우 초기값이여도 재설정이라고 표현해줍니다.
가령, direction, image_index등.
*선언 및 초기화 이후에도 계속 초기화라 쓰이는 것은 엄밀히 말해 '초기값으로 환원'입니다. 압축 및 알아보기 편해 동일한 용어를 쓰곤 있지만 뭐랄까 혼동이 생기시는 분들도 있을 것 같아서.
'My review > 코드 리뷰 [GML]' 카테고리의 다른 글
바람냥님의 Perfect Platformer 소스 리뷰 (0) | 2022.03.19 |
---|